一、登录接口处理逻辑。
当用户登录时,根据参数信息,判断用户是否强制登录。如果不是强制登录且该账号无人登录,则将生成的token存入Redis缓存,如果有人登录,则返回错误信息给前端,前端弹出提示框,如果继续登录,前端修改参数信息,即为强制登录,则将生成的新token存入Redis缓存。
//生成token并设置有效时长
String jwt = JwtUtil.getJwt(bdOrganizeEmployee.getUserName().trim(),bdOrganizeEmployee.getUserId().trim(), 1000 * 60 * 60 * 24);
response.setHeader("token", jwt);
log.info("+token:{}",jwt);
//非强制登录
if("0".equals(bdOrganizeEmployeeDTO.getIsLogin())){
//当前账号未登录
if(redisTemplate.opsForHash().get(bdOrganizeEmployee.getUserId(),bdOrganizeEmployee.getUserName())==null){
//将token存入redis设置有效时间
redisTemplate.opsForHash().put(bdOrganizeEmployee.getUserId(),bdOrganizeEmployee.getUserName(),jwt);
redisTemplate.expire(bdOrganizeEmployee.getUserId(),60*2,TimeUnit.MINUTES);
}else {
//当前账号已登录,返回状态码,前端弹出确认框,确认登录时需修改传入参数isLogin为1
return new ResponseBean(CodeEnum.AUTH_FORCE_LOGIN.getCode(), CodeEnum.AUTH_FORCE_LOGIN.getMsg(), null);
}
}
//强制登录
if ("1".equals(bdOrganizeEmployeeDTO.getIsLogin())){
//将token存入redis设置有效时间
redisTemplate.opsForHash().put(bdOrganizeEmployee.getUserId(),bdOrganizeEmployee.getUserName(),jwt);
redisTemplate.expire(bdOrganizeEmployee.getUserId(),60*2,TimeUnit.MINUTES);
}
二、生成token
public class JwtUtil {
private static final String SALT = "msunsoft";
public static String getJwt(String obj, String obj2,long time) {
return Jwts.builder().claim("userName", obj)
.claim("userId", obj2)
//JWTID
.setId(UUID.randomUUID().toString().replace("-",""))
.setExpiration(new Date(System.currentTimeMillis() + time))
.signWith(SignatureAlgorithm.HS256, SALT)
.compact();
}
public static Claims parsetJwt(String token){
return Jwts.parser().setSigningKey(SALT).parseClaimsJws(token).getBody();
}
}
三、创建拦截器
根据redis缓存中的tokenId和前端传入的tokenId是否相同,判断是否为同一用户
@Slf4j
@Component
public class ValidationInterceptor implements HandlerInterceptor {
@Resource
private RedisTemplate redisTemplate;
@Override
public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if (token==null){
return true;
}
Claims claims = null;
claims = JwtUtil.parsetJwt(token);
//验证账号是否为多用户登录
String userName = String.valueOf(claims.get("userName"));
String userId = String.valueOf(claims.get("userId"));
//从缓存中获取token
Object tokenObj = redisTemplate.opsForHash().get(userId, userName);
String tokenRedis = "";
if (tokenObj != null) {
tokenRedis = String.valueOf(tokenObj);
String redisId = JwtUtil.parsetJwt(tokenRedis).getId();
String id = claims.getId();
//id不同,判定为其他用户登录,挤下线
if (!redisId.equals(id)) {
log.info("账号被他人登录,用户={}", userName);
response.setContentType("text/json;charset=utf-8");
ObjectMapper mapper = new ObjectMapper();
ResponseBean responseBean = new ResponseBean(CodeEnum.AUTH_CONCURRENT_LOGIN.getCode(), CodeEnum.AUTH_CONCURRENT_LOGIN.getMsg(), null);
response.getWriter().write(mapper.writeValueAsString(responseBean));
return false;
}
//刷新token过期时间
redisTemplate.expire(userId, 60*2, TimeUnit.MINUTES);
} else {
//token失效
log.info("token已失效,用户={}", userName);
response.setContentType("text/json;charset=utf-8");
ObjectMapper mapper = new ObjectMapper();
ResponseBean responseBean = new ResponseBean(CodeEnum.AUTH_OUTDATED.getCode(), CodeEnum.AUTH_OUTDATED.getMsg(), null);
response.getWriter().write(mapper.writeValueAsString(responseBean));
return false;
}
return true;
}
}
四、启动拦截器
@Configuration
public class InterceptorConfigurer implements WebMvcConfigurer {
private ValidationInterceptor validationInterceptor;
@Autowired
public void setValidationInterceptor(ValidationInterceptor validationInterceptor) {
this.validationInterceptor = validationInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 可以添加多个拦截器,一般只添加一个
// addPathPatterns("/**") 表示对所有请求都拦截
// .excludePathPatterns("/base/index") 表示排除对/base/index请求的拦截
// 多个拦截器可以设置order顺序,值越小,preHandle越先执行,postHandle和afterCompletion越后执行
// order默认的值是0,如果只添加一个拦截器,可以不显示设置order的值
registry.addInterceptor(validationInterceptor).addPathPatterns("/**");
//.excludePathPatterns("/base/index").order(0);
// registry.addInterceptor(securityBreachConfigInterceptor).addPathPatterns("/**")
// .excludePathPatterns("/base/index").order(1);
}
}