Spring Security:OAuth 在获取访问令牌之前陷入重定向循环,但初始页面旁边的任何页面都不受保护

Posted

技术标签:

【中文标题】Spring Security:OAuth 在获取访问令牌之前陷入重定向循环,但初始页面旁边的任何页面都不受保护【英文标题】:Spring Security: OAuth Stuck in a redirect loop before getting access token, but no pages beside initial page is protected 【发布时间】:2020-12-01 13:45:50 【问题描述】:

我有一个 Spring Boot 应用程序,它与前端的 React 捆绑在一起。

React 是一个单页应用程序,服务于 http://localhost:8080/。它将进行任何页面更改并更新本地浏览器 URL,使其看起来像是更改为新页面,但实际上它仍然位于根位置。例如,单击“Admin Dashboard”会更新浏览器 URL 为“http://localhost:8080/admin”,但它仍会在主页上(对任何过度解释表示歉意)。

如果用户点击刷新,浏览器会向服务器询问实际上并不存在的 /admin。为了解决这个问题,我将任何 404 错误重定向回根 URL (http://localhost:8080/) 并且 React 会智能地显示 Admin 页面,因此它看起来不仅仅是一个单页应用程序。

这很好,但我当前的问题是我正在尝试更改安全性,以便我的应用程序成为 OAuth 1.0a(生产者是只支持 1.0a 的旧应用程序)消费者并针对 3rd Party OAuth Producer 应用程序对用户进行身份验证,如果凭据成功验证,它们将被重定向回我的应用程序并可以继续访问我的应用程序受保护的资源(如果我对它的工作方式的期望是错误的,请告诉我)。

问题:应用程序将请求重定向到第 3 方应用程序,我可以毫无问题地登录。但是,我陷入了 OAuth 循环,并且 ERR_TOO_MANY_REDIRECTS 超时。我认为它卡在第 3 步,它被重定向到应用程序,然后应用程序应该交换访问令牌的详细信息。我认为处理第 3 步的任何东西都被视为受保护的资源,因此它会不断重放 OAuth 流程,但不确定。但这没有任何意义,因为除了授权资源(/static/testing/** - 这是该目录中唯一的东西 - 更改了它,因此我可以更确定事情的布局)之外,所有请求都是允许的。我需要用我自己的代码来处理这最后一步吗?

这是页面超时并出现 ERR_TOO_MANY_REDIRECTS 时的最终到达网址。它有很多 ?oauth_token=&oauth_verifier= 的新实例。每个都有不同的键,所以它似乎遇到了一些阻碍它完成 OAuth 流程并从一开始就不断重复这些步骤的东西,但是却卡在了同一个地方。

http://localhost:8080/static/testing/index.html?oauth_token=b20eecd7a7994a62b751b43458049302&oauth_verifier=c03877ca49be497fb7f2e803e97e8137&oauth_token=25097eab2e3b4b97bf6cbb5a112bb4a8&oauth_verifier=74877789499242f18a9b4d01ee9fea44&oauth_token=6d3136579d234b1c8d21c5f1aabe43a1&oauth_verifier=fe51e99d83954f928b598923ff10fc44&oauth_token=dcb33bce0d8b40d89ae09429adc0fd73&oauth_verifier=4df25da7f1b1403880dce4f7d36b25a5&oauth_token=e9cb8a40323448bbad391df137b7e81d&oauth_verifier=885aa185049f426895529885f34f85c6&oauth_token=bcbe5012553e405c8b6699907fd78256&oauth_verifier=ee9c69c3d1df433289562008b5483810&oauth_token=d7fb00125b664bff820488dc4bc1852d&oauth_verifier=b0c7dee12f4647f2821860170b1c6c42&oauth_token=fbb0f9a6075345c6b1e2c1f11fc874a6&oauth_verifier=f30761f460134103a132c60803145358&oauth_token=ff5da5a77b2e4599b3b3465b48668504&oauth_verifier=a9fe7da1abb1460a8836896aaa442cc9&oauth_token=726ea4b12fd8445bb2cb5e1de10fd94b&oauth_verifier=8d8653ea2dbe4547a0401c6b8524638b&oauth_token=6b8e507ce3254f79b8ce6d98c9ec6784&oauth_verifier=491e101a3cc04c01b16e8834711a0ac8&oauth_token=792be2983ca1496a9ec0f7384eae6316&oauth_verifier=db916051bff443ceacd1b59d2d87cdb8&oauth_token=98f1d137d7144e568f96b7b21df0679c&oauth_verifier=06c3c963161d4fcd966573db262d021c&oauth_token=74ef3c374186405a93a507ebfd7779f0&oauth_verifier=9e492fcf456d4c4e80cb21ed5ec9c6e5&oauth_token=6d609503d8c64cca993ab9298a85054e&oauth_verifier=41e4aca57f184c7b88f4d82820c1f5ab&oauth_token=0f32bfceaa474bff91a88dafe4da9d66&oauth_verifier=59a67e101fe64ee7ba7531d8cb7f65af&oauth_token=af4065dac8184b748d8719cc2f2d429d&oauth_verifier=20d0ffee40214635a12f987b7d338053&oauth_token=718f65634195446c831b8acc9d9ddc81&oauth_verifier=3e6b0ac42028403abf0ed77d0b205fe7&oauth_token=6ce3d8e1460748ccb6a5a151cc467d40&oauth_verifier=dd5173ad4fab491aab50a66047329dfa

注意:这是我的应用程序中的重定向(不直接相关,但可能希望提供上下文)

重定向:

*RedirectController*: /private to /  <-- was used in the original login implementation
*NotFoundHandler*: 404 to /static/index.html
*WebSocketConfig*: Registered at /ws
 
*NotFoundHandler Source Code*: (and we removed application.getSources().remove(ErrorPageFilter.class) from ApplicationContext)
@ControllerAdvice
public class NotFoundHandler 

    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<String> renderDefaultPage() 
        try 
            InputStream inputStream = new ClassPathResource("/static/index.html").getInputStream();
            String body = StreamUtils.copyToString(inputStream, Charset.defaultCharset());
            return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(body);
         catch (IOException e) 
            e.printStackTrace();
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("There was an error completing the action.");
        
    

OAuth 配置: 注意:除了一个目录 (/static/testing/) 之外,我已允许所有未经身份验证的请求 (/) - 或者至少这是我的意图。

@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        // permit all requests (except the /static/testing/** which is called out further down)
        http.authorizeRequests().antMatchers("/**").permitAll();

        http.addFilterAfter(this.oauthConsumerContextFilter(), SwitchUserFilter.class);
        http.addFilterAfter(this.oauthConsumerProcessingFilter(), OAuthConsumerContextFilter.class);
    

    // IMPORTANT: this must not be a Bean
    OAuthConsumerContextFilter oauthConsumerContextFilter() 
        OAuthConsumerContextFilter filter = new OAuthConsumerContextFilter();
        filter.setConsumerSupport(this.consumerSupport());
        return filter;
    

    // IMPORTANT: this must not be a Bean
    OAuthConsumerProcessingFilter oauthConsumerProcessingFilter() 
        OAuthConsumerProcessingFilter filter = new OAuthConsumerProcessingFilter();
        filter.setProtectedResourceDetailsService(this.prds());

        LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> map = new LinkedHashMap<>();

        // one entry per oauth:url element in xml
        map.put(
                // 1st arg is equivalent of url:pattern in xml
                // 2nd arg is equivalent of url:httpMethod in xml
                new AntPathRequestMatcher("/static/testing/**", null),
                // arg is equivalent of url:resources in xml
                // IMPORTANT: this must match the ids in prds() and prd() below
                Collections.singletonList(new SecurityConfig("myResource")));

        filter.setObjectDefinitionSource(new DefaultFilterInvocationSecurityMetadataSource(map));

        return filter;
    

    @Bean // optional, I re-use it elsewhere, hence the Bean
    OAuthConsumerSupport consumerSupport() 
        CoreOAuthConsumerSupport consumerSupport = new CoreOAuthConsumerSupport();
        consumerSupport.setProtectedResourceDetailsService(prds());
        return consumerSupport;
    

    @Bean // optional, I re-use it elsewhere, hence the Bean
    ProtectedResourceDetailsService prds() 
        return (String id) -> 
            switch (id) 
                // this must match the id in prd() below
                case "myResource":
                    return prd();
            
            throw new RuntimeException("Invalid id: " + id);
        ;
    

    ProtectedResourceDetails prd() 
        BaseProtectedResourceDetails details = new BaseProtectedResourceDetails();

        // this must be present and match the id in prds() and prd() above
        details.setId("myResource");

        details.setConsumerKey("<consumer key was here>");
        details.setSharedSecret(new SharedConsumerSecretImpl("<consumer secret was here>"));

        details.setRequestTokenURL("https://localhost:9443/oauth-request-token");
        details.setUserAuthorizationURL("https://localhost:9443/oauth-authorize");
        details.setAccessTokenURL("https://localhost:9443/oauth-access-token");

        // any other service-specific settings

        return details;
    

最后一个问题 在它从生产者接收到 oauth_token 和 oauth_verifier 之后,它似乎卡在了 OAuth 流程的最后一个主要步骤,并且从未使用访问令牌 URL 发出最终请求以获取访问令牌。有谁知道为什么它会卡在这一点上?

是否在最后一步重定向回受保护的资源 (/static/testing/index.html),并且 oauthConsumerProcessingFilter 没有先拦截请求并请求访问令牌?

或者是我应该将 /static/testing/index.html 转发给 Provider 而不是提供本地内容,而且我对 OAuth 流程的理解不适用于我的用例尝试做(当我自己的应用程序受保护的资源被访问时,真的只是使用 OAuth 对 Provider 进行用户验证)?

######################编辑#################### #

添加 Spring 日志:

这里是 Spring 日志,显示了来自 Provider 的初始令牌请求,重定向到 Provider,但是一旦 Provider 登录并重定向回 Callback,Spring 就无法识别当前正在进行的 oauth 进程,并使用附加到 URL 的 oauth_token 和 oauth_verifier 重新开始,并无限期循环此过程,直到浏览器因 ERR_TOO_MANY_REDIRECTS 失败。

2020-08-12T14:53:56,383 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-08-12T14:53:56,383 DEBUG http-nio-8080-exec-1 o.s.s.w.c.HttpSessionSecurityContextRepository: No HttpSession currently exists
2020-08-12T14:53:56,383 DEBUG http-nio-8080-exec-1 o.s.s.w.c.HttpSessionSecurityContextRepository: No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-08-12T14:53:56,385 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-08-12T14:53:56,385 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.u.m.AntPathRequestMatcher: Request 'GET /static/testing/index.html' doesn't match 'POST /logout'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 6 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.s.HttpSessionRequestCache: saved request doesn't match
2020-08-12T14:53:56,386 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 7 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-08-12T14:53:56,387 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 8 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-08-12T14:53:56,388 DEBUG http-nio-8080-exec-1 o.s.s.w.a.AnonymousAuthenticationFilter: Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-08-12T14:53:56,388 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 9 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-08-12T14:53:56,388 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 10 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 11 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.w.u.m.AntPathRequestMatcher: Request '/static/testing/index.html' matched by universal pattern '/**'  (it matches against the /** permit All)
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: Secure object: FilterInvocation: URL: /static/testing/index.html; Attributes: [permitAll]
2020-08-12T14:53:56,389 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.a.v.AffirmativeBased: Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2987f971, returned: 1
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: Authorization successful
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.a.i.AbstractSecurityInterceptor: RunAsManager did not change Authentication object
2020-08-12T14:53:56,393 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 12 of 13 in additional filter chain; firing Filter: 'OAuthConsumerContextFilter'
2020-08-12T14:53:56,394 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Storing access tokens in request attribute 'OAUTH_ACCESS_TOKENS'.
2020-08-12T14:53:56,394 DEBUG http-nio-8080-exec-1 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html at position 13 of 13 in additional filter chain; firing Filter: 'OAuthConsumerProcessingFilter'
2020-08-12T14:53:56,394 DEBUG http-nio-8080-exec-1 o.s.s.w.u.m.AntPathRequestMatcher: Checking match of request : '/static/testing/index.html'; against '/static/testing/**' (it further matches against the OAuth resource since it is /static/testing/**)
2020-08-12T14:53:56,404 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Obtaining request token for resource: myResource (Step 1a: Getting request token from JTS. The callback URL is the protected page that we requested, is that a problem? Or should a filter intercept it on the next step to take over part 3 in the process before actually requesting the resource?)
 
2020-08-12T14:53:56,548 DEBUG http-nio-8080-exec-1 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature base: POST&https%3A%2F%2Flocalhost%3A9443%2Fjts%2Foauth-request-token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fstatic%252Ftesting%252Findex.html%26oauth_consumer_key%3D14b5f4e56856464c9ba64e11a6d558cb%26oauth_nonce%3Dce7d30a7-0b11-4525-a42f-9a353868a5f2%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1597258436%26oauth_version%3D1.0
2020-08-12T14:53:56,548 DEBUG http-nio-8080-exec-1 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature: zAri6vOmcybjeKi3W6CvTkMv5uk=
2020-08-12T14:53:56,628 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Request token obtained for resource myResource: cba433cd39a34a34a80e3dbe96d593bd (Step 1b:Received the request token from JTS)
 
2020-08-12T14:53:56,629 DEBUG http-nio-8080-exec-1 o.s.s.o.c.f.OAuthConsumerContextFilter: Redirecting request to https://localhost:9443/jts/oauth-authorize?oauth_token=cba433cd39a34a34a80e3dbe96d593bd for user authorization of the request token for resource myResource. (Step 2a: redirecting user to the JTS login page for authorization)
 
2020-08-12T14:53:56,629 DEBUG http-nio-8080-exec-1 o.s.s.w.DefaultRedirectStrategy: Redirecting to 'https://localhost:9443/jts/oauth-authorize?oauth_token=cba433cd39a34a34a80e3dbe96d593bd'
2020-08-12T14:53:56,630 DEBUG http-nio-8080-exec-1 o.s.s.w.h.w.HstsHeaderWriter: Not injecting HSTS header since it did not match the requestMatcher (is this line and the next 3 a problem? Should it be storing the security context and can’t because of the http/https mismatch?)org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3df79515
2020-08-12T14:53:56,630 DEBUG http-nio-8080-exec-1 o.s.s.w.c.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper: SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-08-12T14:53:56,631 DEBUG http-nio-8080-exec-1 o.s.s.w.a.ExceptionTranslationFilter: Chain processed normally
2020-08-12T14:53:56,632 DEBUG http-nio-8080-exec-1 o.s.s.w.c.SecurityContextPersistenceFilter: SecurityContextHolder now cleared, as request processing completed
2020-08-12T14:53:59,846 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?  (We’ve now received the call back redirection from JTS to go back to /static/testing/index.html but with the oauth_token and oauth_verifier which would be used in the final step, sending those back to JTS to get a oauth_access token (using /jts/oauth-access-token). However, it seems it doesn’t realize it has the current OAuth flow in process, and repeats all of the above steps in an infinite loop til the browser stops the process with ERR_TOO_MANY_REDIRECTS. Is this because the SecurityContext wasn’t stored? Or another configuration issue I’m missing?). oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2020-08-12T14:53:59,846 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2020-08-12T14:53:59,846 DEBUG http-nio-8080-exec-2 o.s.s.w.c.HttpSessionSecurityContextRepository: No HttpSession currently exists
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.c.HttpSessionSecurityContextRepository: No SecurityContext was available from the HttpSession: null. A new one will be created.
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 4 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 5 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.u.m.AntPathRequestMatcher: Request 'GET /static/testing/index.html' doesn't match 'POST /logout'
2020-08-12T14:53:59,847 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 6 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.s.HttpSessionRequestCache: saved request doesn't match
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 7 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 8 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.a.AnonymousAuthenticationFilter: Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
2020-08-12T14:53:59,848 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 9 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 10 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 11 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.w.u.m.AntPathRequestMatcher: Request '/static/testing/index.html' matched by universal pattern '/**'
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: Secure object: FilterInvocation: URL: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca; Attributes: [permitAll]
2020-08-12T14:53:59,849 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@7bf9dbd5: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.a.v.AffirmativeBased: Voter: org.springframework.security.web.access.expression.WebExpressionVoter@2987f971, returned: 1
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: Authorization successful
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.a.i.AbstractSecurityInterceptor: RunAsManager did not change Authentication object
2020-08-12T14:53:59,850 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 12 of 13 in additional filter chain; firing Filter: 'OAuthConsumerContextFilter'
2020-08-12T14:53:59,851 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Storing access tokens in request attribute 'OAUTH_ACCESS_TOKENS'.
2020-08-12T14:53:59,851 DEBUG http-nio-8080-exec-2 o.s.s.w.FilterChainProxy$VirtualFilterChain: /static/testing/index.html?oauth_token=cba433cd39a34a34a80e3dbe96d593bd&oauth_verifier=82d017779d1547129e84b3173e7c3dca at position 13 of 13 in additional filter chain; firing Filter: 'OAuthConsumerProcessingFilter'
2020-08-12T14:53:59,851 DEBUG http-nio-8080-exec-2 o.s.s.w.u.m.AntPathRequestMatcher: Checking match of request : '/static/testing/index.html'; against '/static/testing/**'
2020-08-12T14:53:59,852 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Obtaining request token for resource: myResource
2020-08-12T14:53:59,853 DEBUG http-nio-8080-exec-2 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature base: POST&https%3A%2F%2Flocalhost%3A9443%2Fjts%2Foauth-request-token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%253A8080%252Fstatic%252Ftesting%252Findex.html%253Foauth_token%253Dcba433cd39a34a34a80e3dbe96d593bd%2526oauth_verifier%253D82d017779d1547129e84b3173e7c3dca%26oauth_consumer_key%3D14b5f4e56856464c9ba64e11a6d558cb%26oauth_nonce%3D08415a86-c1b8-4bcf-9379-961afddd6f3e%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1597258439%26oauth_version%3D1.0
2020-08-12T14:53:59,853 DEBUG http-nio-8080-exec-2 o.s.s.o.c.s.HMAC_SHA1SignatureMethod: signature: x/yPyZhL1RS8RqDOpqnpBwjG57I=
2020-08-12T14:53:59,856 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Request token obtained for resource myResource: c97af51ba3554c24a8a00642ca84d950
2020-08-12T14:53:59,856 DEBUG http-nio-8080-exec-2 o.s.s.o.c.f.OAuthConsumerContextFilter: Redirecting request to https://localhost:9443/jts/oauth-authorize?oauth_token=c97af51ba3554c24a8a00642ca84d950 for user authorization of the request token for resource myResource.
2020-08-12T14:53:59,856 DEBUG http-nio-8080-exec-2 o.s.s.w.DefaultRedirectStrategy: Redirecting to 'https://localhost:9443/jts/oauth-authorize?oauth_token=c97af51ba3554c24a8a00642ca84d950'
2020-08-12T14:53:59,857 DEBUG http-nio-8080-exec-2 o.s.s.w.h.w.HstsHeaderWriter: Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@3df79515
2020-08-12T14:53:59,857 DEBUG http-nio-8080-exec-2 o.s.s.w.c.HttpSessionSecurityContextRepository$SaveToSessionResponseWrapper: SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
2020-08-12T14:53:59,857 DEBUG http-nio-8080-exec-2 o.s.s.w.a.ExceptionTranslationFilter: Chain processed normally
2020-08-12T14:53:59,858 DEBUG http-nio-8080-exec-2 o.s.s.w.c.SecurityContextPersistenceFilter: SecurityContextHolder now cleared, as request processing completed```

【问题讨论】:

当您查看提供程序的日志时,是否有任何迹象表明第 3 步正在发生,如果是,响应是什么?如有必要,您可以拉出 Wireshark 以查看 localhost 和提供者之间的请求和响应。 谢谢,我在客户端没有看到任何内容,在 Provider 中尝试过,但没有显示日志记录,不过我会继续处理这部分内容。我刚刚更新以包含客户端日志,似乎当客户端接收到从提供者到消费者回调的重定向时,消费者客户端没有意识到当前的 OAuth 握手/进程已经到位,并重新开始.然后这个过程将无限循环.. 找到了提供者日志,它们看起来不错,但没有明确指出是否尝试了第三步(虽然我已经超过了限制,所以无法粘贴) 要考虑的一件事是您的客户和提供商都在localhost 上。这有时会对会话 cookie 造成严重破坏。考虑通过编辑您的 /etc/hosts 文件来通过虚假域寻址您的提供商,例如https://idp:9443 而不是 https://localhost:9443。即使这不能解决问题,请说明这是否会改变您观察到的行为。 非常感谢,我不确定它是什么(玩了很多场景,我想我尝试编辑本地主机文件但不确定因为花了一些时间尝试不同的选择,再次感谢你,把我推向了正确的方向)。最终为 Provider 设置了单独的应用服务器、域和 ssl 证书,现在可以正常工作了。为长时间的回复道歉,需要一段时间才能正确设置。再次感谢您的帮助! 【参考方案1】:

不确定是什么错误,但是一旦我将提供程序移动到它自己的主机、域和正确的 SSL 设置,它就开始正常工作了。 (之前两者都在本地主机上,Provider 的 SSL 证书无效,但我已将其导入 JRE 中)。

谢谢@jzheaux!

【讨论】:

以上是关于Spring Security:OAuth 在获取访问令牌之前陷入重定向循环,但初始页面旁边的任何页面都不受保护的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security OAuth 2:如何在 oauth/token 请求后获取访问令牌和附加数据

如何在 spring-security-oauth2 中的资源服务器中获取自定义 UserDetailService 对象?

Spring Security OAuth 2 与传统 Spring MVC

Spring security oauth2 - 从 OAuth2 主体获取自定义数据

Spring security oauth2:在 REST Web 服务中获取用户名

Spring security oauth 令牌提取