spring-security - 为什么 AccessTokenRequest 的 PreservedState 永远为空,并导致与 CSRF 相关的 InvalidRequestException?

标签 spring-security spring-boot wso2is spring-security-oauth2 spring-cloud

作为上下文,很长一段时间以来,我一直在尝试获得一个相当简单的 @SprintBootApplication 以及与 WSO2 Identity Server 集成的附加 @EnableOAuth2Sso 注释。

在我看来,这项工作应该是一个配置问题(如 Spring Cloud Security 上所宣传的那样) - 但到目前为止我还没有运气。

为了了解发生了什么,我使用我的调试器单步执行 spring-security-oauth2 代码以找出发生了什么。在这样做时,我注意到我的 AccessTokenRequest 的 PreservedState 永远为空,并导致与 CSRF 相关的 InvalidRequestException。这是相关代码:

public class AuthorizationCodeAccessTokenProvider extends OAuth2AccessTokenSupport implements AccessTokenProvider {

....

private MultiValueMap<String, String> getParametersForTokenRequest(AuthorizationCodeResourceDetails resource,
        AccessTokenRequest request) {

    MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
    form.set("grant_type", "authorization_code");
    form.set("code", request.getAuthorizationCode());

    Object preservedState = request.getPreservedState();
    if (request.getStateKey() != null || stateMandatory) {
        // The token endpoint has no use for the state so we don't send it back, but we are using it
        // for CSRF detection client side...
        if (preservedState == null) {
            throw new InvalidRequestException(
                    "Possible CSRF detected - state parameter was required but no state could be found");
        }
    }

当涉及到上述代码时,我在使用 admin/admin 登录并且我的 Web 应用程序已收到身份验证代码后已批准 claim :
http://localhost:9998/loginstate=Uu8ril&code=20ffbb6e4107ce3c5cf9ee22065f4f2 

鉴于我首先需要做的就是让登录部分正常工作,我尝试禁用 CSRF,但无济于事。

相关配置如下:
spring:
  profiles: default
security:
  oauth2:
    client:
      accessTokenUri: https://localhost:9443/oauth2/token
      userAuthorizationUri: https://localhost:9443/oauth2/authorize
      clientId: yKSD9XwET9XJ3srGEFXP6AfHhAka
      clientSecret: zuPTcdJH435h3wgl055XNZ5ffNMa
      scope: openid
      clientAuthenticationScheme: header
     resource:
      userInfoUri: https://localhost:9443/oauth2/userinfo?schema=openid

就我自己的调查工作而言,令人担忧的是,在 DefaultOAuthClientContext 中,保留状态在需要使用之前就被清除了,这似乎是一个排序问题。
  • DefaultAccessTokenRequest.setPreservedState(http://localhost:9998/login)
  • DefaultOAuth2ClientContext.state.put(avjDRM, http://localhost:9998/login )
  • DefaultAccessTokenRequest.setPreservedState(http://localhost:9998/login)
  • DefaultOAuthClientContext.state.put(MREOgG, http://localhost:9998/login )
  • 登录 WSO2 表格
  • DefaultOAuth2ClientContext.state.remove(avjDRM)
  • OAUth2RestTemplate...acquireAccessToken......Object reservedState = oauth2Context.removePreservedState(avjDRM)

  • 我正在使用最新版本的 Spring Boot (1.3.0) 和 WSO2 Identity Server (5.0)。还使用 spring-security-oauth 2.0.8。

    最佳答案

    事实证明,在提供的代码部分中引用的 reservedState 为 null 的原因是因为正在创建一个新的 bean Oauth2ClientContext 实例,而这正是不应该发生的 - OAuth2ClientContext 的全部目的是存储状态。就 OAuth2 协议(protocol)(RFC 6749)而言,保存状态对于防止跨站点请求伪造非常重要(参见第 10.12 节)。
    解决这个问题是一件简单的事情,即启用调试日志记录并将针对 WSO2 IS 生成的输出与一个工作示例所看到的内容进行比较。在我的例子中,我总是回归到的工作示例是 Spring 团队自己提供的示例。
    这是客户端配置(application.yml),然后使用 Spring 团队 SSO 服务器记录输出测试:

    spring:
      profiles: default
    security:
      oauth2:
        client:
          accessTokenUri: http://192.168.0.113:32768/uaa/oauth/token
          userAuthorizationUri: http://192.168.0.113:32768/uaa/oauth/authorize
      clientId: acme
      clientSecret: acmesecret
    resource:
      jwt:
        keyValue: |
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnGp/Q5lh0P8nPL21oMMrt2RrkT9AW5jgYwLfSUnJVc9G6uR3cXRRDCjHqWU5WYwivcF180A6CWp/ireQFFBNowgc5XaA0kPpzEtgsA5YsNX7iSnUibB004iBTfU9hZ2Rbsc8cWqynT0RyN4TP1RYVSeVKvMQk4GT1r7JCEC+TNu1ELmbNwMQyzKjsfBXyIOCFU/E94ktvsTZUHF4Oq44DBylCDsS1k7/sfZC2G5EU7Oz0mhG8+Uz6MSEQHtoIi6mc8u64Rwi3Z3tscuWG2ShtsUFuNSAFNkY7LkLn+/hxLCu2bNISMaESa8dG22CIMuIeRLVcAmEWEWH5EEforTg+QIDAQAB
          -----END PUBLIC KEY-----
      id: openid
      serviceId: ${PREFIX:}resource
    
    请注意,没有提到创建 的行OAuth2ClientContext .
    DEBUG o.s.security.web.FilterChainProxy - /login?code=9HLSpP&state=G9kpy3 at position 6 of 12 in additional filter chain; firing Filter: 'OAuth2ClientAuthenticationProcessingFilter'
    DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/login'; against '/login'
    DEBUG o.s.s.o.c.f.OAuth2ClientAuthenticationProcessingFilter - Request is to process authentication
    INFO  o.s.s.o.c.DefaultOAuth2ClientContext - Getting access token request
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'scopedTarget.accessTokenRequest'
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration'
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'scopedTarget.accessTokenRequest'
    INFO  o.s.s.o.client.OAuth2RestTemplate - Longer lived state key: G9kpy3
    INFO  o.s.s.o.client.OAuth2RestTemplate - Removing preserved state in oauth2context
    INFO  o.s.s.o.c.DefaultOAuth2ClientContext - Found preserved state: http://localhost:9999/login
    
    这是客户端配置 (application.yml),然后使用 WSO2IS 5.0.0 记录输出测试:
    spring:
      profiles: wso2
    server:
      port: 9998
    security:
      oauth2:
        client:
          accessTokenUri: https://localhost:9443/oauth2/token
          userAuthorizationUri: https://localhost:9443/oauth2/authorize
          clientId: yKSD9XwET9XJ3srGEFXP6AfHhAka
          clientSecret: zuPTcdJH435h3wgl055XNZ5ffNMa
          scope: openid
          clientAuthenticationScheme: header
        resource:
          userInfoUri: https://localhost:9443/oauth2/userinfo?schema=openid
    
    注意 的行创建 bean 'scopedTarget.oauth2ClientContext' 的实例 .
    DEBUG o.s.security.web.FilterChainProxy - /login?state=PWhQwv&code=372ff0c197a4c85a0caf070cc9a6678 at position 6 of 12 in additional filter chain; firing Filter: 'OAuth2ClientAuthenticationProcessingFilter'
    DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/login'; against '/login'
    DEBUG o.s.s.o.c.f.OAuth2ClientAuthenticationProcessingFilter - Request is to process authentication
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'scopedTarget.oauth2ClientContext'
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration$SessionScopedConfiguration$ClientContextConfiguration'
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'scopedTarget.oauth2ClientContext'
    INFO  o.s.s.o.c.DefaultOAuth2ClientContext - Getting access token request
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'scopedTarget.accessTokenRequest'
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.security.oauth2.config.annotation.web.configuration.OAuth2ClientConfiguration'
    DEBUG o.s.b.f.s.DefaultListableBeanFactory - Finished creating instance of bean 'scopedTarget.accessTokenRequest'
    INFO  o.s.s.o.client.OAuth2RestTemplate - Longer lived state key: PWhQwv
    INFO  o.s.s.o.client.OAuth2RestTemplate - Removing preserved state in oauth2context
    INFO  o.s.s.o.c.DefaultOAuth2ClientContext - Found preserved state: null
    
    最后,下一个调用端口自然是确定为什么 OAuth2ClientContext 不是使用 WSO2 IS 配置创建的。调查表明这是因为 WSO2 IS 没有传回预期的 JSESSIONID,因此将找不到 session 范围的 OAuth2ClientContext。
    如果绝望,解决这种情况的潜在黑客是克隆 Spring OAuth 2 并执行以下操作:
    在 AuthorizationCodeAccessTokenProvider 类中,使用 hack 来更改请求中的保留状态。
        private MultiValueMap<String, String>     getParametersForTokenRequest(AuthorizationCodeResourceDetails resource,
            AccessTokenRequest request) {
    
                MultiValueMap<String, String> form = new   LinkedMultiValueMap<String, String>();
                form.set("grant_type", "authorization_code");
                form.set("code", request.getAuthorizationCode());
    
                request.setPreservedState("http://localhost:9998/login");
                Object preservedState = request.getPreservedState();
    

    关于spring-security - 为什么 AccessTokenRequest 的 PreservedState 永远为空,并导致与 CSRF 相关的 InvalidRequestException?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34219911/

    相关文章:

    java - 使用 Spring Security 的访问被拒绝页面不起作用

    spring-boot - 从后端容器连接到 keycloak 容器时连接被拒绝

    java - Spring Boot 管理 - 基本身份验证

    spring-boot - Spring Boot 显示无效日期(年月)格式的错误消息 : eg 2020-15

    spring - 不能将 PowerMockRunner 与 SpringBootTest 一起使用

    wso2 - 如何在 WSO2 IS 中的 SAML 响应中返回用户属性

    java - 连接错误的用户时出错

    java - 生产环境: Spring security login success redirect to localhost

    error-handling - WSO2 Identity Server-无法配置自定义全部捕获异常(exception)页面

    wso2 - wso2身份服务器和CAS有什么区别?