问题描述
用springSecurity+jwt做自定义登录描述的时候,发送任何请求携带了token还是会走默认的认证逻辑。并且也在自定义的jwt过滤器中做了认证逻辑。
解决方案
先看一下代码
// 生成authentication对象
SysUser sysUser = new SysUser();
sysUser.setAccount(claims.get("account").asString());
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser,sysUser.getAuthorities());
// 告诉security已经认证了,否则会再去认证
authenticationToken.setAuthenticated(true);
// 放入上下文
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
// 放行
doFilter(request, response, filterChain);
从jwt中读出载荷数据,生成UsernamePasswordAuthenticationToken 对象并且放入了上下文,但是还是会走认证逻辑。
跟着断点查看了很久发现在SpringSecurity的认证拦截器中有个方法调用的是Authentication接口的isAuthentication方法,如果返回的是true表示认证了,就不会走认证逻辑,否则重新认证。
于是尝试手动设置改成true
// 告诉security已经认证了,否则会再去认证
authenticationToken.setAuthenticated(true);
根本不行,不知道为啥,走不到这里。然后查看UsernamePasswordAuthenticationToken代码。终于发现构造方法用错了。
看一下这两种构造方法
public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
三个参数,会设置认证标志
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
两个参数,设置认证标识为未认证。
总结
也就是说我用错了构造方法,导致认证标识未改变,才会一直走认证逻辑。做后代码改成三参数的构造器就解决了
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(sysUser, null,sysUser.getAuthorities());