Security 权限系统
- UserDetails
Security 中的用户接口,我们自定义用户类要实现该接口。
- GrantedAuthority
Security 中的用户权限接口,自定义权限需要实现该接口:
public class MyGrantedAuthority implements GrantedAuthority { private String authority; }
authority 表示权限字段,需要注意的是在 config 中配置的权限会被加上 ROLE_
前缀,比如我们的配置 authorizeRequests().antMatchers("/test").hasRole("test")
,配置了一个 test
权限但我们存储的权限字段(authority)应该是 ROLE_test
。
- UserDetailsService
Security 中的用户 Service,自定义用户服务类需要实现该接口:
@Service public class MyUserDetailService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return..... } }
loadUserByUsername的作用在上文中已经说明,就是根据用户名查询用户对象。
- SecurityContextHolder
用户在完成登录后 Security 会将用户信息存储到这个类中,之后其他流程需要得到用户信息时都是从这个类中获得,用户信息被封装成 SecurityContext ,而实际存储的类是 SecurityContextHolderStrategy ,默认的SecurityContextHolderStrategy 实现类是 ThreadLocalSecurityContextHolderStrategy 它使用了ThreadLocal来存储了用户信息。
手动填充 SecurityContextHolder 示例:
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("test","test",list); SecurityContextHolder.getContext().setAuthentication(token);
对于使用 token 鉴权的系统,我们就可以验证token后手动填充SecurityContextHolder,填充时机只要在执行投票器之前即可,或者干脆可以在投票器中填充,然后在登出操作中清空SecurityContextHolder。
Security 扩展
Security 可扩展的有
- 鉴权失败处理器
- 验证器
- 登录成功处理器
- 投票器
- 自定义token处理过滤器
- 登出成功处理器
- 登录失败处理器
- 自定义 UsernamePasswordAuthenticationFilter
- 鉴权失败处理器
Security 鉴权失败默认跳转登录页面,我们可以实现 AccessDeniedHandler 接口,重写 handle() 方法来自定义处理逻辑;然后参考配置类说明将处理器加入到配置当中。
- 验证器
实现 AuthenticationProvider 接口来实现自己验证逻辑。需要注意的是在这个类里面就算你抛出异常,也不会中断验证流程,而是算你验证失败,我们由流程图知道,只要有一个验证器验证成功,就算验证成功,所以你需要留意这一点。
- 登录成功处理器
在 Security 中验证成功默认跳转到上一次请求页面或者路径为 "/" 的页面,我们同样可以自定义:继承 SimpleUrlAuthenticationSuccessHandler 这个类或者实现 AuthenticationSuccessHandler 接口。我这里建议采用继承的方式,SimpleUrlAuthenticationSuccessHandler 是默认的处理器,采用继承可以契合里氏替换原则,提高代码的复用性和避免不必要的错误。
- 投票器
投票器可继承 WebExpressionVoter 或者实现 AccessDecisionVoter接口;WebExpressionVoter 是 Security 默认的投票器;我这里同样建议采用继承的方式;添加到配置的方式参考 上文;
注意:投票器 vote 方法返回一个int值;-1代表反对,0代表弃权,1代表赞成;投票管理器收集投票结果,如果最终结果大于等于0则放行该请求。
- 自定义token处理过滤器
自定义 token 处理器继承自 OncePerRequestFilter 或者 GenericFilterBean 或者 Filter 都可以,在这个处理器里面需要完成的逻辑是:获取请求里的 token,验证 token 是否合法然后填充 SecurityContextHolder ,虽然说过滤器只要添加在投票器之前就可以,但我这里还是建议添加在 http.addFilterAfter(new MyFittler(), LogoutFilter.class);
- 登出成功处理器
实现LogoutSuccessHandler接口,添加到配置的方式参考上文。
- 登录失败处理器
登录失败默认跳转到登录页,我们同样可以自定义。继承 SimpleUrlAuthenticationFailureHandler 或者实现 AuthenticationFailureHandler,建议采用继承。
- 自定义UsernamePasswordAuthenticationFilter
我们自定义UsernamePasswordAuthenticationFilter可以极大提高我们 Security的灵活性(比如添加验证验证码是否正确的功能)。
我们直接继承 UsernamePasswordAuthenticationFilter ,然后在配置类中初始化这个过滤器,给这个过滤器添加登录失败处理器,登录成功处理器,登录管理器,登录请求 url 。
这里配置略微复杂,贴一下代码清单
初始化过滤器:
MyUsernamePasswordAuthenticationFilte getAuthenticationFilter(){ MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter(redisService); myUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(new MyUrlAuthenticationFailureHandler()); myUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler()); myUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/sign_in"); myUsernamePasswordAuthenticationFilter.setAuthenticationManager(getAuthenticationManager()); return myUsernamePasswordAuthenticationFilter; }
添加到配置:
http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
总结
对于 Security 的扩展配置关键在于 configure(HttpSecurityhttp)
方法;扩展认证方式可以自定义 authenticationManager
并加入自己验证器,在验证器中抛出异常不会终止验证流程;扩展鉴权方式可以自定义 accessDecisionManager
然后添加自己的投票器并绑定到对应的 url(url 匹配方式为 ant)上,投票器 vote(Authenticationauthentication,FilterInvocationfi,Collection<ConfigAttribute>attributes)
方法返回值为三种:-1 0 1,分别表示反对弃权赞成。
对于 token 认证的校验方式,可以暴露一个获取的接口,或者重写 UsernamePasswordAuthenticationFilter
过滤器和扩展登录成功处理器来获取 token,然后在 LogoutFilter
之后添加一个自定义过滤器,用于校验和填充 SecurityContextHolder。
另外,Security 的处理器大部分都是重定向的,我们的项目如果是前后端分离的话,我们希望无论什么情况都返回 json ,那么就需要重写各个处理器了。