在springboot加入springsecurity后,关于用户认证和授权这块就全权交给springsecurity,用户认证和授权难免会涉及到token相关的东西,比如说用户登录成功,为其创建一个token,当用户退出的时候,将token移除等等操作。基于springsecurity的接口,我们决定书写一些工具类来方便操作:
在springsecurity中,有一个PasswordEncoder接口专门用来对密码加密的,如果我们不喜欢,可以实现这个接口,书写自己的逻辑,比如下面是我们自己使用md5进行加密的实现类:
@Component public class DefaultPasswordEncoder implements PasswordEncoder { public DefaultPasswordEncoder() { this(-1); } /** * @param strength * the log rounds to use, between 4 and 31 */ public DefaultPasswordEncoder(int strength) { } public String encode(CharSequence rawPassword) { return MD5.encrypt(rawPassword.toString()); } public boolean matches(CharSequence rawPassword, String encodedPassword) { //参数一是表单提交的,参数二是数据库已加密过的密码;该方法比较两个密码是否相同 return encodedPassword.equals(MD5.encrypt(rawPassword.toString())); } }
还有根据用户信息生成token,根据token解析出用户信息的代码也可以封装起来,如下:
@Component public class TokenManager { private long tokenExpiration = 24*60*60*1000; private String tokenSignKey = "123456"; public String createToken(String username) { String token = Jwts.builder().setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)) .signWith(SignatureAlgorithm.HS512, tokenSignKey).compressWith(CompressionCodecs.GZIP).compact(); return token; } public String getUserFromToken(String token) { String user = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token).getBody().getSubject(); return user; } public void removeToken(String token) { //jwttoken无需删除,客户端扔掉即可。 } }
上面和JwtUtils是差不多的,到时候具体看公司的业务需求改代码即可。
还有退出时,需要将redis中的token移除掉,对退出的操作也进行一定的封装:
public class TokenLogoutHandler implements LogoutHandler { //该接口是springsecurity提供的,用于退出 private TokenManager tokenManager; private RedisTemplate redisTemplate; public TokenLogoutHandler(TokenManager tokenManager, RedisTemplate redisTemplate) { this.tokenManager = tokenManager; this.redisTemplate = redisTemplate; } @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { String token = request.getHeader("token"); if (token != null) { tokenManager.removeToken(token); //这个只是形式,其实没用 //清空当前用户缓存中的权限数据 String userName = tokenManager.getUserFromToken(token); redisTemplate.delete(userName); } ResponseUtil.out(response, R.ok()); } }
我们来梳理下token,一般用户登录成功后,用户浏览器中会有token,这个token是访问其他服务时不需要再次登录或者关闭浏览器后再次访问该网站时不需要登录,即自动登录;而在CSRF功能中,在表单的隐藏域token是用于防CSRF的,两者不是一回事。
梳理完毕,当用户退出时,用户浏览器中的token应由前端代码来删除,不关后端的事,而后端需要把redis中该用户的权限删除掉,保证即使传来token了,也没有相应权限进行操作了。需要再次登录,那这样的话,redis中就会重新写入用户的权限列表。
下面再写一个用于处理未授权的情况,统一处理类:
public class UnauthorizedEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { ResponseUtil.out(response, R.error()); } }
需要实现AuthenticationEntryPoint ,当用户访问某资源未授权时,会调用commence方法
最后总结下,上面的4个工具类放在spring-security的子模块下,只是出于跟springsecurity的工作有挂钩,也可以单独弄出来作为commen工具类的。