背景
- 项目spring-security+oauth2
版本是5.7.6
- 项目以client的形式请求
TokenEndPoint
中的/oauth/token
,使用Basic认证调用接口,返回token
问题描述
先前是可以登录访问,更新代码之后,现在登录访问报401 Unauthorized
问题的难点是,这个错误信息是项目中请求以http方式请求TokenEndPoint
中的/oauth/token
返回的,请求并没有到达路径对应接口就返回了,我并没有了解过spring security中过滤器具体有哪些,所以不太好打断点,这导致我浪费很多的时间。
提示:在不了解的情况下,我使用的是笨方法,根据报错信息(Unauthorized), 全局搜索,然后找到线索。
在lib包中的代码,要搜索需要有源码
原因
不废话,原因是走了下边过滤器的这段代码(注意,这是我的项目,走的该Filter,自己的项目还需要结合实际情况来)。
BasicAuthenticationFilter.java
提示:以下代码已经处理过,只保留与本文有关逻辑内容
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
// 获取用户名
UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
String username = authRequest.getName();
// 是否需要校验
if (authenticationIsRequired(username)) {
// 校验,失败抛出异常
Authentication authResult = this.authenticationManager.authenticate(authRequest);
// 通过,返回(空函数)
onSuccessfulAuthentication(request, response, authResult);
}
}
catch (AuthenticationException ex) {
// 失败,返回(空函数)
onUnsuccessfulAuthentication(request, response, ex);
if (this.ignoreFailure) { // 是否忽略认证失败
chain.doFilter(request, response);
}
else {
// 填充401状态和错误信息
this.authenticationEntryPoint.commence(request, response, ex);
}
return;
}
chain.doFilter(request, response);
}
Authentication authResult = this.authenticationManager.authenticate(authRequest);
中会校验报错,然后被catch中的逻辑填充401 和报错信息。
到这里,如果你也遇到类似问题,就通过这个类打个断点试试把。
具体原因
通过跟Authentication authResult = this.authenticationManager.authenticate(authRequest);
中的校验代码,发现其通过循环找到对应的Provider去执行校验,如下图
其中provider是DaoAuthenticationProvider
,它继承自抽象类AbstractUserDetailsAuthenticationProvider
,如下图
AbstractUserDetailsAuthenticationProvider实现了authenticate
方法,其中逻辑主要取了用户凭证信息用以比对(这里就不展示了)。
校验部分预留了模板方法,具体校验逻辑则下沉到子类DaoAuthenticationProvider
中,类似模板方法模式。
其子类校验逻辑实现如下,通过passwordEncoder
密码编码器比对参数中的凭证,与抽象类中获取的凭证信息是否一样。
我出现报错的原因就是这个passwordEncoder的实例类发生了变动,因为pom中依赖版本升级,导致这个密码编码器在高版本中发生了改变,由原来的明文编码器变为了hash编码器。
未升级前使用明文
升级后使用了hash加密