Question

I am building a grails application that includes:

  1. Spring Security (Spring MVC project; NOT the Grails plugin)
  2. "OAuth for Spring Security" to implement an OAuth2 provider

To accomplish this, I followed the following steps:

  • grails install-templates [see here]
  • in src/templates/war/web.xml, add the Spring Security filter as below:

<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>
  • define Spring Security and OAuth beans in WEB-INF/applicationContext.xml file including the following for handling /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>

Issue: The issue that I am facing is that Spring Security filters fire correctly and successfully authenticate the client. But after that, the GrailsDispatcherServlet is unable to find a handler for the POST to /oauth/token and returns a "404 Resource not found" error.

In the debug log, I can see that /oauth/token is mapped to a handler

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)

I suspect, this happens because when GrailsDispatcherServlet and ApplicationContext is created, the Grails' DefaultUrlMappingsHolder creates a new set of URL mappings in that context and replaces the previous set of mappings. For e.g., I also see the following in my debug log


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/()?

Here's the debug log for when I make an HTTP post to //oauth/token


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

Any ideas on how to "share"/"propagate" those Spring /oauth/token mappings with the Grails Dispatcher?

Was it helpful?

Solution

I ran into similar symptoms when trying to get my /oauth/authorize endpoint to work properly. In order to get things going, I had to add the following to UrlMappings.groovy:

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

This solution came from examining the source for a grails spring-security-oauth provider plugin:

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

Note that getting this setup to work completely might also involve updating the grails cache plugin: when using 1.0.1, I received a 500 after I got the mapping to work (when trying to load /oauth/authorize). Upgrading my cache plugin to 1.1.1 fixed that issue for me.

Hope something in there is useful.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top