概念
SpringSecurity提供了声明式的安全访问控制解决方案, 对访问权限进行认证和授权, 基于Spring AOP和Servlet过滤器, 提供了安全性方面的全面解决方案.
核心概念:
- Principle, 代表用户的对象, 还包括一切可以用于验证的设备
- Authority, 代表用户的角色
- Permission, 代表授权
- 验证(Authentication), 指的是建立使用者信息(Principal)的过程
验证过程如下:
1.用户使用用户名和密码登录
2.过滤器(UsernamePasswordAuthenticationFilter)获取到用户名以及密码, 然后封装成Authentication
3.AuthenticationManager认证token
4.AuthenticationManager认证成功, 返回一个封装了用户权限信息的Authentication对象, 用户的上下文信息
5.Authentication对象赋值给当前的SecutiryContext, 建立这个用户的安全上下文
6.用户进行一些受到访问控制机制保护的操作, 访问控制机制会根据当前安全上下文信息检查这个操作所需的权限
- 授权(Authorization)
核心类
- SecurityContext, 包含当前正在访问系统的用户的详细信息
- SecurityContextHolder, 用来保存SecurityContext
- ProviderManager, 维护一个认证的列表, 以便处理不同的认证方式的认证
- DaoAuthenticationProvider, AuthenticationProvider最常用的实现, 用来获取用户提交的用户名和密码, 并进行正确性比对, 如果正确, 则返回一个数据库中的用户信息
- UserDetails, 用户实体类, 包含用户名, 密码, 权限等信息
- UserDetailService, 用户相关的信息是通过UserDetailService来加载的
- GrantedAuthority, 每一个GrantedAuthority代表赋予当前用户的一种权限
- Filter
配置
1.继承WebSecurityConfigurerAdapter, 开启@EnableWebSecurity注解
/**
* @author gavin
* @date 2020-06-20
*/
@Configuration
@EnableWebSecurity // 启用web安全配置
@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法安全设置
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
/**
* 进行权限控制规则相关配置
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
// 定义/admin/下的所有的url, 只有拥有admin的角色才能访问
.antMatchers("/admin/**").hasRole("ADMIN")
// 除上面外所有请求都需要鉴权认证
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
// 开启CSRF保护
.csrf();
}
/**
* 认证相关, 用来配置全局的认证相关的信息, 包含AuthenticationProvider和UserDetailService
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
// 自定义加密规则
// auth.userDetailsService(userDetailsService).passwordEncoder(new PasswordEncoder() {
// @Override
// public String encode(CharSequence charSequence) {
// return null;
// }
//
// @Override
// public boolean matches(CharSequence charSequence, String s) {
// return false;
// }
// });
}
/**
* 进行全局请求忽略规则配置, HttpFireWall配置, debug配置, 全局SecurityFilterChain配置
*/
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
2.实现UserDetailsService接口, 实现loadUserByUsername方法
/**
* @author gavin
* @date 2020-06-20
*/
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = new User();
user.setUserName("111");
user.setPassword(new BCryptPasswordEncoder().encode("123"));
List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList("1");
user.setGrantedAuthorities(grantedAuthorities);
user.setEnable(true);
return user;
}
}
3.实现UserDetails接口
@Data
public class User implements UserDetails {
private String userName;
private String password;
private Boolean enable;
private Collection<? extends GrantedAuthority> grantedAuthorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.grantedAuthorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.userName;
}
/**
* 账户未过期
*/
@Override
public boolean isAccountNonExpired() {
return true;
}
/**
* 账户未被锁
*/
@Override
public boolean isAccountNonLocked() {
return true;
}
/**
* 账户密码未过期
*/
@Override
public boolean isCredentialsNonExpired() {
return true;
}
/**
* 账户启用
*/
@Override
public boolean isEnabled() {
return null != this.enable && this.enable;
}
}
多种方式获取用户信息
@GetMapping("/context")
public SecurityContext getSecurityContext(Principal principal, Authentication authentication, HttpServletRequest request) {
String userName = principal.getName();
String userName2 = authentication.getName();
String userName3 = request.getUserPrincipal().getName();
SecurityContext context = SecurityContextHolder.getContext();
// {"authentication":{"authorities":[{"authority":"user"}],"details":{"remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"B8D706FE1ED79D5923F010166C975C4F"},"authenticated":true,"principal":{"password":"$2a$10$FTCyXR.LdQHphI3tXJZL2uObbVlRuo5CJLQCk0A8UNBuqpC0O1f/2","enable":true,"grantedAuthorities":[{"authority":"user"}],"enabled":true,"username":"111","authorities":[{"authority":"user"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true},"credentials":null,"name":"111"}}
String userName4 = context.getAuthentication().getName();
return context;
}
权限控制
在方法上实现权限控制, 需要启用 @EnableGlobalMethodSecurity, 还需配置启用的类型:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableGlobalMethodSecurity(jsr250Enabled = true)
@EnableGlobalMethodSecurity(securedEnabled= true)
- JSR-250
JSR, JavaSpecification Requests, java规范提案. JSR-250是用于提供安全方法设置的, 主要提供了注解@RolesAllowed
- prePostEnabled
基于表达式的注解, 主要提供注解@PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter
- securedEnabled
提供注解@Secured
JWT
JWT, JSON WEB Token, 是一个开放的标准, 用于在各方之间以JSON对象安全的传输信息, 这些信息通过数字签名进行验证和授权.
JWT请求流程:
- 用户使用浏览器发送账号和密码
- 服务器使用私钥创建一个JWT返回给浏览器
- 浏览器将JWT串在请求头中向服务器发送请求
- 服务器验证该JWT
- 根据授权规则返回资源给浏览器
JWT组成:
- 头部(header): 通过Base 64编码生成的字符串, header中存放的内容说明编码对象是一个jwt
- 载荷(payload): 主要包含claim, claim是一些实体的状态和额外的元数据
- 签名(signature): 使用编码后的header和payload及一个秘钥, 使用header中指定签名算法进行签名.