/oauth/token 的 URL 映射失败 - Grails 应用程序中的 Spring 安全性 + OAuth

Posted

技术标签:

【中文标题】/oauth/token 的 URL 映射失败 - Grails 应用程序中的 Spring 安全性 + OAuth【英文标题】:Url Mapping fails for /oauth/token - Spring security + OAuth in a Grails application 【发布时间】:2013-06-14 03:12:26 【问题描述】:

我正在构建一个 grails 应用程序,其中包括:

    Spring Security(Spring MVC 项目;不是 Grails 插件) “OAuth for Spring Security”实现 OAuth2 提供者

为此,我遵循了以下步骤:

grails 安装模板 [see here] 在 src/templates/war/web.xml 中,添加 Spring Security 过滤器如下:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
在 WEB-INF/applicationContext.xml 文件中定义 Spring Security 和 OAuth bean,包括处理 /oauth/token 的以下内容

<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
 xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
    <anonymous enabled="false" />
    <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
    <!-- include this only if you need to authenticate clients via request parameters -->     
    <custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
</http>
....
....
<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
    <oauth:authorization-code />
    <oauth:implicit />
    <oauth:refresh-token />
    <oauth:client-credentials />
    <oauth:password />
</oauth:authorization-server>

问题: 我面临的问题是 Spring Security 过滤器正确触发并成功验证了客户端。 但在那之后,GrailsDispatcherServlet 无法找到 POST 到 /oauth/token 的处理程序并返回“404 Resource not found”错误。

在调试日志中,我可以看到 /oauth/token 映射到一个处理程序

2013-06-17 19:21:04,469 [localhost-startStop-1] INFO  endpoint.FrameworkEndpointHandlerMapping  - Mapped "[/oauth/token],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]" onto public org.springframework.http.ResponseEntity org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.getAccessToken(java.security.Principal,java.lang.String,java.util.Map)

我怀疑,这是因为在创建 GrailsDispatcherServlet 和 ApplicationContext 时,Grails 的 DefaultUrlMappingsHolder 在该上下文中创建了一组新的 URL 映射并替换了之前的一组映射。例如,我还在调试日志中看到以下内容


2013-06-17 19:31:01,339 [localhost-startStop-1] DEBUG mapping.DefaultUrlMappingsHolder  - Reverse mapping: [DefaultUrlMappingsHolder.UrlMappingKey@250f9a46 controller = 'account', action = [null], plugin = [null], params = set['API_VERSION']] -> /()/provisioning/order/account/()?

这是我向 //oauth/token 发送 HTTP 帖子时的调试日志


2013-06-17 19:31:05,798 [http-bio-8080-exec-1] DEBUG util.AntPathRequestMatcher  - Checking match of request : '/oauth/token'; against '/oauth/token'
2013-06-17 19:31:05,804 [http-bio-8080-exec-1] DEBUG web.FilterChainProxy  - /oauth/token at position 1 of 5 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2013-06-17 19:31:05,805 [http-bio-8080-exec-1] DEBUG web.FilterChainProxy  - /oauth/token at position 2 of 5 in additional filter chain; firing Filter: 'BasicAuthenticationFilter'
2013-06-17 19:31:05,807 [http-bio-8080-exec-1] DEBUG www.BasicAuthenticationFilter  - Basic Authentication Authorization header found for user 'j2'
2013-06-17 19:31:05,808 [http-bio-8080-exec-1] DEBUG authentication.ProviderManager  - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider
2013-06-17 19:31:05,813 [http-bio-8080-exec-1] DEBUG www.BasicAuthenticationFilter  - Authentication success: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ffff9a33: Principal: org.springframework.security.core.userdetails.User@d08: Username: j2; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ALL; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ALL
2013-06-17 19:31:05,813 [http-bio-8080-exec-1] DEBUG web.FilterChainProxy  - /oauth/token at position 3 of 5 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
2013-06-17 19:31:05,814 [http-bio-8080-exec-1] DEBUG web.FilterChainProxy  - /oauth/token at position 4 of 5 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
2013-06-17 19:31:05,814 [http-bio-8080-exec-1] DEBUG web.FilterChainProxy  - /oauth/token at position 5 of 5 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
2013-06-17 19:31:05,814 [http-bio-8080-exec-1] DEBUG util.AntPathRequestMatcher  - Checking match of request : '/oauth/token'; against '/oauth/token'
2013-06-17 19:31:05,815 [http-bio-8080-exec-1] DEBUG intercept.FilterSecurityInterceptor  - Secure object: FilterInvocation: URL: /oauth/token; Attributes: [IS_AUTHENTICATED_FULLY]
2013-06-17 19:31:05,815 [http-bio-8080-exec-1] DEBUG intercept.FilterSecurityInterceptor  - Previously Authenticated: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@ffff9a33: Principal: org.springframework.security.core.userdetails.User@d08: Username: j2; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ALL; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ALL
2013-06-17 19:31:05,815 [http-bio-8080-exec-1] DEBUG vote.AffirmativeBased  - Voter: org.springframework.security.access.vote.RoleVoter@35f3198f, returned: 0
2013-06-17 19:31:05,815 [http-bio-8080-exec-1] DEBUG vote.AffirmativeBased  - Voter: org.springframework.security.access.vote.AuthenticatedVoter@6b1316f4, returned: 1
2013-06-17 19:31:05,815 [http-bio-8080-exec-1] DEBUG intercept.FilterSecurityInterceptor  - Authorization successful
2013-06-17 19:31:05,816 [http-bio-8080-exec-1] DEBUG intercept.FilterSecurityInterceptor  - RunAsManager did not change Authentication object
2013-06-17 19:31:05,816 [http-bio-8080-exec-1] DEBUG web.FilterChainProxy  - /oauth/token reached end of additional filter chain; proceeding with original chain
2013-06-17 19:31:05,826 [http-bio-8080-exec-1] DEBUG mvc.GrailsWebRequestFilter  - Bound Grails request context to thread: SecurityContextHolderAwareRequestWrapper[ FirewalledRequest[ org.apache.catalina.connector.RequestFacade@519cea9e]]
2013-06-17 19:31:05,846 [http-bio-8080-exec-1] DEBUG filter.UrlMappingsFilter  - Executing URL mapping filter...
2013-06-17 19:31:05,847 [http-bio-8080-exec-1] DEBUG filter.UrlMappingsFilter  - URL Mappings
------------
/
/(*)/provisioning/order/account/(*)?
/(*)/provisioning/order/demographics/(*)?
/(*)/provisioning/inventory/phone_numbers/(*)?
/(*)/billing/regions/(*)?
/(*)/billing/countries/(*)?
/(*)/provisioning/credit_cards/(*)?
/(*)/provisioning/states/(*)?
/(*)/provisioning/countries/(*)?
/(*)/provisioning/phone_cities/(*)?
/(*)/general/languages/(*)?
/(*)/docs/constraints/(*)?

2013-06-17 19:31:05,847 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/]
2013-06-17 19:31:05,847 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/order/account/(*)?]
2013-06-17 19:31:05,847 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/order/demographics/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/inventory/phone_numbers/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/billing/regions/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/billing/countries/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/credit_cards/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/states/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/countries/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/provisioning/phone_cities/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/general/languages/(*)?]
2013-06-17 19:31:05,848 [http-bio-8080-exec-1] DEBUG mapping.DefaultUrlMappingsHolder  - Attempting to match URI [/oauth/token] with pattern [/(*)/docs/constraints/(*)?]
2013-06-17 19:31:05,857 [http-bio-8080-exec-1] DEBUG filter.UrlMappingsFilter  - No match found, processing remaining filter chain.
2013-06-17 19:31:05,860 [http-bio-8080-exec-1] DEBUG mvc.GrailsWebRequestFilter  - Cleared Grails thread-bound request context: SecurityContextHolderAwareRequestWrapper[ FirewalledRequest[ org.apache.catalina.connector.RequestFacade@519cea9e]]
2013-06-17 19:31:05,860 [http-bio-8080-exec-1] DEBUG access.ExceptionTranslationFilter  - Chain processed normally
2013-06-17 19:31:05,860 [http-bio-8080-exec-1] DEBUG context.SecurityContextPersistenceFilter  - SecurityContextHolder now cleared, as request processing completed

关于如何使用 Grails Dispatcher“共享”/“传播”这些 Spring /oauth/token 映射的任何想法?

【问题讨论】:

这有点不正常,因为您正在做的是建立两个不同的应用程序上下文,然后尝试仅与另一个上下文共享一个上下文的一部分。您能否改为在 Grails 项目中包含必要的依赖项,然后将 spring oauth applicationContext 配置添加到 grails-app/conf/resources.xml? 感谢丹尼尔的评论。当我将所有 Spring 和 OAuth 配置移动到 grails-app/conf/resources.xml 中时,结果仍然相同。过滤器按预期触发,客户端已通过身份验证,但 Grails 找不到 /oauth/token 的处理程序。调试日志与上述问题完全相同。 另外,查看 OAuth2 的文档 TokenEndpoint /oauth/token is mapped to that class using @RequestMapping(value="/oauth/token") 从调试日志中,它看起来像这样在初始化期间检测到映射,但不知何故 Grails 不知道它。 这是因为 grails 不知道您的过滤器。您也许可以通过编程方式注册它:gist.github.com/danveloper/bc94f74e2d151825c584 【参考方案1】:

我在尝试让我的 /oauth/authorize 端点正常工作时遇到了类似的症状。为了让事情顺利进行,我不得不在 UrlMappings.groovy 中添加以下内容:

    "/oauth/authorize" (uri:"/oauth/authorize.dispatch")
    "/oauth/token" (uri:"/oauth/token.dispatch")

此解决方案来自检查 grails spring-security-oauth 提供程序插件的源代码:

https://github.com/adaptivecomputing/grails-spring-security-oauth2-provider

请注意,让此设置完全工作可能还涉及更新 grails 缓存插件:使用 1.0.1 时,我在映射工作后收到 500(尝试加载 / oauth/授权)。将我的缓存插件升级到 1.1.1 为我解决了这个问题。

希望里面的东西有用。

【讨论】:

以上是关于/oauth/token 的 URL 映射失败 - Grails 应用程序中的 Spring 安全性 + OAuth的主要内容,如果未能解决你的问题,请参考以下文章

在 Spring 服务器中使用 OAuth 身份验证的 CORS 失败

Spring oauth2 ver 2.0.7,端点 /oauth/token 上出现 404 错误

Spring security 4.2.3,OAUTH 2,/oauth/token 端点,CORS 不工作

CSP 拒绝 API Twitter

在 laravel5.8 中来自 guzzle 的 oauth2 访问密钥请求

在 oAuth 2.0 中替代将详细信息作为 url 查询字符串传递?