在使用 springSecurity 登录用户 + 自定义注解+aop实现日志记录问题时,再给业务方法 登录功能添加日志注解时,报错:
java.lang.ClassCastException: org.springframework.security.authentication.AnonymousAuthenticationToken cannot be cast to org.springframework.security.authentication.UsernamePasswordAuthenticationToken
很明显的错误,AnonymousAuthenticationToken 类型不可用扮演 UsernamePasswordAuthenticationToken 类型,但是,我们给 SecurityContextHolder 里添加用户信息都是用的类型转换,怎么会出这个问题呐?
通过打断点,发现其中的问题。由于我是给登录功能添加的日志,所以定义的实现OncePerRequestFilter 接口的过滤器 JwtAuthenticationTokenFilter 中先去判断是否又token,因为还没有登录,所以没有存入SecurityContextHolder。然后,通过aop切面去实现日志整理的,在获取用户信息时是没有用户信息的,所以报错。
所以,JwtAuthenticationTokenFilter 还没有存入用户信息的时候,就执行了切面整理日志,所以报错了。写代码还是得耐心啊!!
补充:JwtAuthenticationTokenFilter 是在切面之前执行的,只是登录功能是没有token的,所以也没有用户信息。
解决方案:登录时不走aop切面,登录成功日志入库。
public ResponseResult login(User user) {
long startTime = System.currentTimeMillis();
//AuthenticationManager authenticate进行用户认证
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());
Authentication authenticate = authenticationManager.authenticate(authentication);
//如果认证没通过,给用户对应的提示
if(Objects.isNull(authenticate)){
throw new RuntimeException("登录失败");
}
//如果认证通过了,使用userId生成一个jwt,jwt存入ResponseResult进行返回
LoginUser loginUser = (LoginUser)authenticate.getPrincipal();
String userId = loginUser.getUser().getId().toString();
String jwt = JwtUtil.createJWT(userId);
Map<String, String> map = new HashMap<>();
map.put("jwt",jwt);
//把完整的用户信息存入Redis,userId作为key
redisCache.setCacheObject("login:"+userId,loginUser);
// 添加日志
// 填充日志内容
long endTime = System.currentTimeMillis();
SysLogOperation sysLogOperation = new SysLogOperation();
sysLogOperation.setUserName(loginUser.getUser().getUserName());
sysLogOperation.setCreateId(loginUser.getUser().getId());
sysLogOperation.setStatus("0");
Date date = new Date();
sysLogOperation.setRequestTime(endTime -startTime);
sysLogOperation.setCreateTime(date);
sysLogOperation.setUpdateTime(date);
sysLogOperation.setClassName("org.example.service.impl");
sysLogOperation.setRequestMethod("login");
sysLogOperationService.insertSysLog(sysLogOperation);
return new ResponseResult(200,"登陆成功",map);
}