一、初识SpringSecurity
在springboot项目中加入spring security。
1、在pom.xml中加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2、启动
在启动信息里面有一个自动生成的密码,默认用户名是user。
2018-10-18 15:31:10.241 INFO 4996 --- [ main] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: dd6f7b68-7409-4195-b908-b78cc9ec92af
此时我们就可以用user:dd6f7b68-7409-4195-b908-b78cc9ec92af登录了。
3、自己配置用户名密码
在application.yml中加入自定义配置
spring:
security:
user:
name: admin
password: admin
此时用户名密码就被定义成admin:admin了。
4、自定义配置类
更进一步,上面的安全模式只能有一个用户名密码,我们写一个自定义的配置类,配置多个用户名密码。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user")
.password("user")
.roles("USER")
.and()
.withUser("admin")
.password("admin")
.roles("ADMIN")
.and()
.withUser("zhangsan")
.password("123456")
.roles("ADMIN");
}
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
}
@EnableWebSecurity注解开启Spring Security的功能
@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解
在configure(HttpSecurity http)方法里面,默认的认证代码是:
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
NoOpPasswordEncoder:springboot默认对密码会解析,加上这个是保持用户输入的密码
5、权限的简单使用
我们可以在方法上加上权限注解,如下:
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HomeController {
@RequestMapping("/home")
@PreAuthorize("hasRole('ADMIN')")
public String home() {
return "hello world";
}
}
常用权限
表达式 | 描述 |
---|---|
hasRole([role]) | 当前用户是否拥有指定角色。 |
hasAnyRole([role1,role2]) | 多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true。 |
hasAuthority([auth]) | 等同于hasRole |
hasAnyAuthority([auth1,auth2]) | 等同于hasAnyRole |
Principle | 代表当前用户的principle对象 |
authentication | 直接从SecurityContext获取的当前Authentication对象 |
permitAll | 总是返回true,表示允许所有的 |
denyAll | 总是返回false,表示拒绝所有的 |
isAnonymous() | 当前用户是否是一个匿名用户 |
isRememberMe() | 表示当前用户是否是通过Remember-Me自动登录的 |
isAuthenticated() | 表示当前用户是否已经登录认证成功了。 |
isFullyAuthenticated() | 如果当前用户既不是一个匿名用户,同时又不是通过Remember-Me自动登录的,则返回true。 |
二、进阶 Security
用数据库存储用户和角色,实现安全认证。
1、先定义一个user:
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class User implements UserDetails {
private String username;
private String password;
private Collection<? extends GrantedAuthority> roles;
public Collection<? extends GrantedAuthority> getRoles() {
return roles;
}
public void setRoles(Collection<? extends GrantedAuthority> roles) {
this.roles = roles;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
2、定义一个service:
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.List;
public class MyUserDetailsServiceimp implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
// ROLE默认是带ROLE_开头
User user = new User();
user.setUsername(s);
user.setPassword(s);
user.setRoles(list);
return user;
}
}
我们只定义了用户名,密码,角色列表,
其他的账号过期,账号上锁,凭证过期,是否可用都默认设置为true,暂时不考虑。
实际使用的时候是根据username去数据库查询记录,
在根据的userid查询role,
并根据记录设置相关的账号过期,上锁等情况。
3、修改我们的配置类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(getUserDetailService());
}
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
@Bean
public UserDetailsService getUserDetailService() {
return new MyUserDetailsServiceimp();
}
}
用户密码角色的获取从UserDetailService获取。
4、不拦截的页面
有些页面我们不想拦截,所有人都能访问,这时候只需要修改我们的配置类就行了:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/plugins/**",
"/css/**",
"/fonts/**",
"/js/**",
"/home1",
"/home2"
).permitAll() //默认不拦截静态资源的url pattern (2)
.anyRequest().authenticated().and()
.formLogin()
//.loginPage("/login")// 登录url请求路径 (3)
//.defaultSuccessUrl("/httpapi").permitAll()
.and() // 登录成功跳转路径url(4)
.logout().permitAll();
http.logout().logoutSuccessUrl("/"); // 退出默认跳转页面 (5)
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(getUserDetailService());
}
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
}
@Bean
public UserDetailsService getUserDetailService() {
return new MyUserDetailsServiceimp();
}
}
如上所示,css,js,font不拦截,/根不拦截,/home1, /home2不拦截
还可以自定义登录页面,以及登录成功之后的跳转页面
我们看一下此时的homecontroller
@RestController
public class HomeController {
@RequestMapping("/")
public String index() {
return "hello index!";
}
@RequestMapping("/home")
public String home() {
return "hello home";
}
@RequestMapping("/home1")
public String home1() {
return "hello home1";
}
@RequestMapping("/home2")
public String home2() {
return "hello home2";
}
}
5、session
spring security默认帮我们管理session,我们改下controller,如下:
@RestController
public class HomeController {
@RequestMapping("/")
public String index() {
return "hello index!";
}
@RequestMapping("/home")
@PreAuthorize("hasRole('ADMIN')")
public String home() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = "";
if (principal instanceof UserDetails) {
username = ((UserDetails)principal).getUsername();
} else {
username = principal.toString();
}
System.out.println(username);
return "hello home";
}
@RequestMapping("/home1")
public String home1() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String username = "";
if (principal instanceof UserDetails) {
username = ((UserDetails)principal).getUsername();
} else {
username = principal.toString();
}
System.out.println(username);
return "hello home1";
}
}
启动服务,我们一次访问:
http://localhost:8080/home1
http://localhost:8080/home
http://localhost:8080/home1
控制台打印如下:
anonymousUser
zhangsan
zhangsan
网友评论