【OAuth2】Spring Security OAuth2 授权失败(401) 问题整理

Spring Cloud架构中采用Spring Security OAuth2作为权限控制,关于OAuth2详细介绍可以参考阮一峰的网络日志理解OAuth 2.0

项目中采用OAuth2四种模式中的两种,Password模式和Client模式, Password模式用于控制用户的登录,Client模式用于控制后端服务相互调用。

权限架构调整后在近期发现一些问题,由于网上资料不多,只能单步调试方式看源码 (其实带着问题看源码是最好的方式)。现在将问题和解决方案整理如下:

一、免登录接口校验token问题

问题:APP端反馈,一些免登录接口会校验token

详细:经过测试发现,免登录接口 如果传了access_token会对token合法性就行校验,如果不传接口不会校验,这导致了免登录接口的过期token会报错

排查:经过查看源码发现spring-security-oauth2的过滤器 ==OAuth2AuthenticationProcessingFilter==会对请求进行拦截,(具体源码就不截图了)

  • 如果存在access_token则会根据userInfoEndpointUrl去认证服务器上校验token信息,
  • 如果不存在access_token则会继续执行spring-security的拦截器FilterSecurityInterceptorFilterSecurityInterceptor对路径是否需要授权,已经授权是否通过做校验~

解决: 可以采用过滤器在执行到核心过滤器OAuth2AuthenticationProcessingFilter ,将不需要授权的请求头中的access_token过滤掉。或者APP免登录接口不传token

最终采用的是后者

二、Token失效返回的是状态401的错误

1、问题: APP端反馈,传递失效access_token,返回401状态,期望是200同时以错误码方式提示token失效。

排查:经过单步调试分析源码发现,token失效后,认证服务器会抛出异常,同时响应给资源服务器,资源服务发现认证服务器的错误后会抛出InvalideException

抛出的异常会经过默认的DefaultWebResponseExceptionTranslator处理然后 Reseponse给Client端。

解决:通过上面的分析指导。最后的异常是在DefaultWebResponseExceptionTranslator 处理的,所以只需要

  • 自定义实现类Implements WebResponseExceptionTranslator接口处理异常装换逻辑,
  • 使得自定义的类生效

(1)自定义异常转换类

@Slf4j
public class Auth2ResponseExceptionTranslator implements WebResponseExceptionTranslator {

    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception e) {
        log.error("Auth2异常", e);
        Throwable throwable = e.getCause();
        if (throwable instanceof InvalidTokenException) {
            log.info("token失效:{}", throwable);
            return new ResponseEntity(new Message<>(ServerConstant.INVALID_TOKEN.getMsg(), ServerConstant.INVALID_TOKEN.getCode()), HttpStatus.OK);
        }
        return new ResponseEntity(new Message(e.getMessage(), String.valueOf(HttpStatus.METHOD_NOT_ALLOWED.value())), HttpStatus.METHOD_NOT_ALLOWED);
    }
}

(2)资源服务器中使得自定义类生效

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {


  @Override
  public void configure(ResourceServerSecurityConfigurer resources) throws Exception {       
    // 定义异常转换类生效
    AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
    ((OAuth2AuthenticationEntryPoint) authenticationEntryPoint).setExceptionTranslator(new Auth2ResponseExceptionTranslator());
    resources.authenticationEntryPoint(authenticationEntryPoint);
  }


  @Override
  public void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .exceptionHandling()            
        // 定义的不存在access_token时候响应
        .authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
        .and()
        .authorizeRequests().antMatchers("/**/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .httpBasic().disable();
  }
}

2、问题:测试发现授权接口,当请求参数中不存在access_token时发现接口返回错误信息:

{"timestamp":1539337154336,"status":401,"error":"Unauthorized","message":"No message available","path":"/app/businessCode/list"}

排查:经过前面的分析发现,上面提到Security的FilterSecurityInterceptor对OAuth2中返回的信息和本身配置校验后,抛出AccessDenyException

解决:经过上面的几个问题的处理,发现思路还是一样的,需要定义响应结果,即

  1. **自定义响应处理逻辑SecurityAuthenticationEntryPoint **
  2. 自定义处理逻辑SecurityAuthenticationEntryPoint生效(见上面的配置)

SecurityAuthenticationEntryPoint具体实现:

@Slf4j
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
       log.error("Spring Securtiy异常", authException);
       response.setCharacterEncoding("UTF-8");
       response.setContentType("application/json; charset=utf-8");
       PrintWriter out = response.getWriter();
       out.print(JSON.toJSONString(new Message<>(ServerConstant.INVALID_TOKEN.getMsg(), ServerConstant.INVALID_TOKEN.getCode())));
    }
}

本文转载自博客园作者**浮生半日**的Spring Security OAuth2 授权失败(401) 问题整理一文。

针对前端请求OAuth框架自带的/oauth/token接口跨域问题,可以使用Spring Boot的CORS(跨域资源共享)来解决。CORS是一种机制,允许浏览器在发送AJAX请求时,跨域访问其他域下的资源。通过配置CORS,可以允许前端请求/oauth/token接口跨域访问。 在后台使用Spring Cloud框架,包括Eureka、Gateway,可以配置Hystrix简单实现和跨域功能。此外,还可以使用OAuth2来实现认证和授权,使用JWT token进行身份验证。 因此,可以通过在Spring Boot中配置CORS解决前端请求OAuth框架自带的/oauth/token接口跨域问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Springboot通过cors解决跨域问题(解决spring security oath2的/oauth/token跨域问题)](https://blog.csdn.net/jazz2013/article/details/116591240)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [前端(ElementUI)后端(Spring Cloud Eureka、Gateway、OAuth2、JWTtoken、RSA)](https://download.csdn.net/download/qq_24296051/87646211)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值