当前位置: 首页 > 知识库问答 >
问题:

为什么AccessTokenRequest的PreservedState永久为空,并产生与CSRF相关的InvalidRequestException?

闾丘德宇
2023-03-14

作为上下文,我一直在尝试获得一个相当简单的@SprintBoot应用程序,其中包含一个与WSO2 Identity Server集成的@EnableOAuth2Sso注释。

在我看来,让这个工作应该是一个配置问题(正如Spring云安全上所宣传的)——但是到目前为止我还没有运气好。

为了理解正在发生的事情,我使用了我的调试器来逐步执行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应用程序收到验证代码后,我已经批准了声明:

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.set保护状态(http://localhost:9998/login)
  • DefaultOAuth2ClientContext.state.put(avjDRM,http://localhost:9998/login)
  • DefaultAccessTokenRequest.set保护状态(http://localhost:9998/login)
  • DefaultOAuthClientContext.state.put(MREOgG,http://localhost:9998/login)
  • 登录WSO2表单
  • DefaultOAuth2ClientContext.state.remove(avjDRM)
  • 对象保存状态=oauth2Context.remove保存状态(avjDRM)

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

共有2个答案

洪飞鸿
2023-03-14

只是在试的时候看到了一个类似的问题https://spring.io/guides/tutorials/spring-boot-oauth2/并观察到这个问题与chrome浏览器有关,它在facebook重定向后没有传递JSESSIONID。

您可以看到,当第二次登录/facebook调用没有传递之前设置的sessionid时,set cookie被调用了两次

邵子平
2023-03-14

事实证明,在提供的代码部分中引用的preservedState为null的原因是,正在创建bean Oauth2ClientContext的一个新实例,而这恰恰是不应该发生的事情——Oauth2ClientContext的全部目的是存储状态。根据OAuth2协议(RFC 6749),保存状态对于防止跨站点请求伪造非常重要(参见第10.12节)。

选择这一点很简单,只需启用调试日志记录,并将根据WSO2生成的输出与工作示例中看到的结果进行比较。在我的例子中,我总是回到Spring团队自己提供的工作示例。

这是客户端配置(application.yml),然后使用Spring team 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没有传回预期的JSESSIONID,因此找不到会话范围的OAuth2ClientContext。

如果绝望,修复这种情况的潜在黑客是克隆Spring OAuth 2并执行以下操作:

在类内AuthorizationCodeAccessTokenProvider中,通过修改请求中的保留状态,可以执行以下操作。

    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();
 类似资料:
  • 问题内容: 在过去的几天里,我一直在尝试通过重构我的一个命令行实用程序来在Golang中进行并发,但是我被困住了。 这是原始代码(master分支)。 这是并发分支(x_concurrent分支)。 当我使用执行并发代码时,如果将JIRA问题添加到此处的通道,则永不执行,这将导致我永远挂起。 我的想法是,我有大量的JIRA问题,我想为每个goroutine编写一个goroutine,以查看是否需要

  • 启动应用程序的代码如下: 在A点,我只是关闭了上下文来检查哪些beans已经关闭,然后Hibernate60秒,以便有时间检查JMX控制台。我注意到,即使上下文是关闭的,但是生产者仍然在JMX中注册。之后,我跟踪了代码,注意到在上下文关闭时,KafkaTemplate调用以下代码: 这意味着它创建了一个生产者,但是因为它是事务性的,所以它不会被关闭。 这使得在关闭上下文时,的方法将清除缓存并物理地

  • 问题内容: 当我加载该模块时: (来自http://www.freesoftwaremagazine.com/articles/drivers_linux?page=0,2) 该模块在2.6.39-02063904-generic(来自Ubuntu PPA)上被标记为in 且无法卸载。但是它在默认的2.6.38内核上可以正常工作。(均在Ubuntu 11.04 x86上)。 2.6.39中发生了什

  • 问题内容: 我正在运行,并且在.htaccess文件中执行了以下操作: 如果我在PHP文件中打印出变量,则会得到一个空数组。为什么我的环境变量没有出现在这里?为什么首先是空的? 我确实找到了变量,但是它出现在变量中。由于某种原因,它出现了两次。为什么是这样? 看来我可以使用来获得它,所以也许我应该只使用它。但是我仍然对造成这种情况的原因有些好奇。这是Windows问题吗?还是发生了什么事? 问题答

  • 我有两个实体帐户和头寸。POSITION表和ACCOUNT表有内容,但我认为至少应该有一行的POSITION_ACCOUNT表是空的。我可能做错了什么?以下是相关代码(我使用Eclipse Link 2.4.2: 就位: @许多私人名单账户; 在帐户: @ManyTo许多(mappdBy="帐户",抓取=FetchType. EAGER,级联=CascateType. PERSIST)@JoinT

  • 我有一个docker compose文件,用django和postgres数据库构建一个web服务器。基本上看起来是这样的: 我正在使用一个卷来持久化我的数据我设法运行我的容器并将数据添加到数据库中。已成功创建卷:docker volume ls 但该卷为空,docker system df-v的输出显示: 此外,如果我想或需要使用docker compose down和docker compos