我正在构建一个 grails 应用程序,其中包括:
- Spring Security(Spring MVC 项目;不是 Grails 插件)
- “OAuth for Spring Security”实现 OAuth2 提供者
为此,我遵循了以下步骤:
- grails install-templates [见这里]
- 在 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 beans,包括以下用于处理 /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 映射的任何想法?