【问题标题】:Spring security OAuth2 UserRedirectRequiredException春季安全 OAuth2 UserRedirectRequiredException
【发布时间】:2015-04-17 02:51:28
【问题描述】:

我正在尝试使用 Spring Security 为 Google App Engine 应用程序设置 OAuth2。这是我的 OAuth2 配置:

@Configuration
@EnableOAuth2Client
public class OAuth2Config {

@Bean
public OAuth2ProtectedResourceDetails googleOAuth2Details() {
    AuthorizationCodeResourceDetails googleOAuth2Details = new AuthorizationCodeResourceDetails();

    googleOAuth2Details.setAuthenticationScheme(form);
    googleOAuth2Details.setClientAuthenticationScheme(form);
    googleOAuth2Details.setClientId("*********************");
    googleOAuth2Details.setClientSecret("*********************");
    googleOAuth2Details.setUserAuthorizationUri("https://accounts.google.com/o/oauth2/auth");
    googleOAuth2Details.setAccessTokenUri("https://www.googleapis.com/oauth2/v3/token");
    googleOAuth2Details.setScope(asList("openid"));
    return googleOAuth2Details;
}

@SuppressWarnings("SpringJavaAutowiringInspection")
@Resource
private OAuth2ClientContext oAuth2ClientContext;

@Bean
@Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public OAuth2RestOperations googleOAuth2RestTemplate() {
    return new OAuth2RestTemplate(googleOAuth2Details(), oAuth2ClientContext);
}

}

Scpring 安全工作正常,但是当我尝试登录时,出现以下异常:

org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.getRedirectForAuthorization(AuthorizationCodeAccessTokenProvider.java:347)
at org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider.obtainAccessToken(AuthorizationCodeAccessTokenProvider.java:194)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainNewAccessTokenInternal(AccessTokenProviderChain.java:142)
at org.springframework.security.oauth2.client.token.AccessTokenProviderChain.obtainAccessToken(AccessTokenProviderChain.java:118)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.acquireAccessToken(OAuth2RestTemplate.java:221)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.getAccessToken(OAuth2RestTemplate.java:173)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.createRequest(OAuth2RestTemplate.java:105)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:515)
at org.springframework.security.oauth2.client.OAuth2RestTemplate.doExecute(OAuth2RestTemplate.java:128)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:482)
at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:264)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:115)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:132)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at com.sun.proxy.$Proxy54.getForEntity(Unknown Source)
at com.evogro.farmino.security.OpenIDConnectAuthenticationFilter.attemptAuthentication(OpenIDConnectAuthenticationFilter.java:45)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:127)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:63)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectRequest(DevAppServerModulesFilter.java:366)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doDirectModuleRequest(DevAppServerModulesFilter.java:349)
at com.google.appengine.tools.development.DevAppServerModulesFilter.doFilter(DevAppServerModulesFilter.java:116)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:98)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:491)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

【问题讨论】:

  • 为什么你的 rest 模板请求的范围是有限的(除非你可以在每个请求中注入访问令牌,否则它不会起作用)?
  • 在其他情况下,谷歌应用引擎会抛出 RestTemplate is not Serializable 的异常。知道如何解决吗?
  • 那是什么“其他情况”?您使用的是什么版本的 Spring OAuth?
  • 另一种情况是休息模板会话范围。所以 Spring 尝试将 rest 模板保存到 session,但是应用引擎抛出了 RestTemplate is not Serializable 的异常。 Spring OAuth 版本 = 2.0.6.RELEASE
  • 所以不要把它放在会话范围内。 AFAIK 单例范围很好(您注入的 OAuth2ClientContext 已经是会话范围)。

标签: java google-app-engine spring-security spring-security-oauth2


【解决方案1】:

除了 cmets 中讨论的范围问题外,我想您的应用缺少 OAuth2ClientContextFilter(这通常是处理这些异常的方法)。如果有的话,只要把它放在你的 web.xml 中,或者在 servlet 上下文中注册它。来自user guide

创建一个过滤器 bean(ID 为 oauth2ClientContextFilter)来存储当前请求和上下文。 ...过滤器必须连接到应用程序中(例如,为同名的 DelegatingFilterProxy 使用 Servlet 初始化程序或 web.xml 配置)。

【讨论】:

  • 另一个可能的原因是您的过滤器存在但放置在过滤器链中的错误位置。在部署到应用程序服务器时,例如 Glassfish 过滤器似乎以与在独立应用程序中运行时定义的顺序相反的顺序注册,这可能会导致大量问题。
  • @DaveSyer 在 spring 5 中,OAuth2ClientContextFilter 已被删除。 Spring 使用 OAuth2AuthorizationRequestRedirectFilter 和 OAuth2LoginAuthenticationFilter。但是 OAuth2AuthorizationRequestRedirectFilter 需要一个带有 {registrationId} 的显式 URL 才能向 Auth 服务器发出正确的请求。有什么办法,当用户尝试访问我的网页时,我可以隐式重定向吗?我不希望他点击“用谷歌登录”然后传递注册 ID。我希望它是隐式的。
  • 我很确定重定向到身份验证服务器是 Spring Security 5 oauth2Login() 功能的默认设置。也许在另一篇文章中明确询问会更好?
猜你喜欢
  • 2018-09-11
  • 2017-10-22
  • 2015-04-04
  • 2018-08-01
  • 2013-12-22
  • 2014-02-02
  • 2012-12-13
相关资源
最近更新 更多