项目背景:项目由springboot构建,通过jwt进行认证和授权,这是无连接、无状态的。要实现用户唯一性,无法通过session等其他入手。
前期分析:
认证这里是通过fliter获取请求中的token
public class JWTFilter extends GenericFilterBean {
public static final String AUTHORIZATION_HEADER = "Authorization";
private TokenProvider tokenProvider;
public JWTFilter(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String jwt = resolveToken(httpServletRequest);
if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {
Authentication authentication = this.tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
private String resolveToken(HttpServletRequest request){
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
随后tokenProvider.validateToken(jwt)这里对token解析,也只是通过简单的验证,看通过密钥能不能正常解析出,解析不出或者异常即验证不通过
public boolean validateToken(String authToken) { try { Jwts.parser().setSigningKey(key).parseClaimsJws(authToken); return true; } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) { log.info("Invalid JWT signature."); log.trace("Invalid JWT signature trace: {}", e); } catch (ExpiredJwtException e) { log.info("Expired JWT token."); log.trace("Expired JWT token trace: {}", e); } catch (UnsupportedJwtException e) { log.info("Unsupported JWT token."); log.trace("Unsupported JWT token trace: {}", e); } catch (IllegalArgumentException e) { log.info("JWT token compact of handler are invalid."); log.trace("JWT token compact of handler are invalid trace: {}", e); } return false; }
这里要实现用户唯一,要做到两点:
1、对用户登录后的token需要持久化(redis或者直接入库)。
2、在验证时,我你们需要解析token,获取用户信息,并于存储的token比对,一致则通过,否则限制访问。
实现:
这里预期实现,A用户在地点1登录,并不退出。随后A用户在地点2登录。如果地点1再有任何操作,回限制访问,并返回登陆页面。
首先改写dofliter,在这里,我们需要获取到token,并去除掉登录的接口,不然发现这个用户已经登陆过,在地点2的登陆请求会被拦截。
随后根据解析出的用户信息,从存储中获取登录时记录的token进行校验。这里获取用户信息,我也是和上面解析token的校验一样,通过密钥解析token。
private boolean isOtherLogin(String jwt){ boolean result = false; //判断当前用户是否 已在异地登陆 String username = this.tokenProvider.getUserName(jwt); if(username!=null&&!"".equals(username)){ log.info("username of isOtherLogin:{}",username); List<PersistentToken> persistentTokenList = persistentTokenService.findByUserAgentOrderByTokenDateDesc(username); if(persistentTokenList!=null&&persistentTokenList.size()>0){ String user_token = persistentTokenList.get(0).getTokenValue(); if(!jwt.equals(user_token)){ result = true; } } } log.info("result of isOtherLogin:{}",result); return result; }