2
votes

I got problem when try to integrate our web app with Spring oauth2
the end point /oauth/token is mapped for both GET and POST method

o.s.s.o.p.e.FrameworkEndpointHandlerMapping- Mapped "{[/oauth/token],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.getAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException
o.s.s.o.p.e.FrameworkEndpointHandlerMapping- Mapped "{[/oauth/token],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<org.springframework.security.oauth2.common.OAuth2AccessToken> org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(java.security.Principal,java.util.Map<java.lang.String, java.lang.String>) throws org.springframework.web.HttpRequestMethodNotSupportedException

when access: https://192.168.70.19:8072/oauth/token?grant_type=password&client_id=7CA42EA39288EC73212716FC6A51B8A2&username=admin&password=switch

**server return:**Bad client credentials

its ok, however when I add the client_secret https://192.168.70.19:8072/oauth/token?grant_type=password&client_id=7CA42EA39288EC73212716FC6A51B8A2&client_secret=client_secret&username=admin&password=switch

server return 404 error (as I know this should happen when the API is not mapped only)

Some of my configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/security
    http://www.springframework.org/schema/security/spring-security-3.2.xsd
    http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetClass"
                  value="org.springframework.security.core.context.SecurityContextHolder" />
        <property name="targetMethod" value="setStrategyName" />
        <property name="arguments">
            <list>
                <value>MODE_INHERITABLETHREADLOCAL</value>
            </list>
        </property>
    </bean>

    <bean id="authenticationEntryPoint"
          class="com.alu.ov.ngnms.appserver.login.AuthenticationEntryPoint">
        <constructor-arg name="loginUrl" value="/login.html" />
    </bean>
    <bean name="customUserDetailsAuthenticationProvider" class="com.alu.ov.ngnms.appserver.login.CustomUserDetailsAuthenticationProvider">
        <property name="aaaServerRepository" ref="AAAServerRepository"></property>
    </bean>
    <security:authentication-manager alias="authenticationManager"
                                     erase-credentials="false">
             <security:authentication-provider ref="customUserDetailsAuthenticationProvider" />
    </security:authentication-manager>
    <bean id="checkTokenEndPoint"
          class="org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint">
        <constructor-arg name="resourceServerTokenServices" ref="tokenServices"/>      
    </bean>
    <bean id="customAccessDeniedHandler"
          class="com.alu.ov.ngnms.appserver.login.CustomAccessDeniedHandler"></bean>

    <security:http pattern="/oauth/token" create-session="stateless" entry-point-ref="authenticationEntryPoint" authentication-manager-ref="clientAuthenticationManager">
        <security:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
        <security:anonymous enabled="false" />
        <security:custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />
        <security:access-denied-handler ref="customAccessDeniedHandler" />
    </security:http>

    <security:http use-expressions="true" entry-point-ref="authenticationEntryPoint" create-session="never">
        <!-- for all users (login & non-login) -->
        <security:intercept-url pattern="/favicon.ico" access="permitAll" />
        <security:intercept-url pattern="/bower_components/**" access="permitAll"/>
        <security:intercept-url pattern="/locales/**" access="permitAll"/>
        <security:intercept-url pattern="/ov_components/**" access="permitAll"/>
        <security:intercept-url pattern="/scripts/**" access="permitAll"/>
        <security:intercept-url pattern="/styles/**" access="permitAll"/>
        <security:intercept-url pattern="/template/**" access="permitAll" />
        <security:intercept-url pattern="/webstart/classes/**" access="permitAll" />
        <security:intercept-url pattern="/assets/**" access="permitAll" />

        <!-- only for non-login users -->
        <security:intercept-url pattern="/login.html" access="!isAuthenticated()" />
        <security:intercept-url pattern="/upgrade.html" access="!isAuthenticated()" />
        <security:intercept-url pattern="/api/login" access="!isAuthenticated()" />

        <!-- for all login users -->

        <!-- only for admin user & no-license OV -->
        <security:intercept-url pattern="/noLicense.html" access="hasAnyRole('ROLE_ADMIN_NO_LICENSE')" />

        <security:intercept-url pattern="/**" access="isAuthenticated() and !hasRole('ROLE_ADMIN_NO_LICENSE')" />
        <security:access-denied-handler ref="customAccessDeniedHandler"/>
        <!-- Add filter to extract access token from request and perform authentication -->
        <security:custom-filter ref="customOAuth2AuthenProcessingFilter" before="PRE_AUTH_FILTER" />
        <security:expression-handler ref="oauthWebExpressionHandler" />
    </security:http>

    <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <property name="authenticationManager" ref="clientAuthenticationManager" />
    </bean>

       <security:global-method-security authentication-manager-ref="authenticationManager" pre-post-annotations="enabled"
        secured-annotations="enabled">
    </security:global-method-security>

        <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 authentication-manager-ref="authenticationManager"/>

    </oauth:authorization-server>
    <security:authentication-manager id="clientAuthenticationManager">
        <security:authentication-provider user-service-ref="clientDetailsUserService" />
    </security:authentication-manager>

    <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <constructor-arg ref="clientDetails" />
    </bean>

    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices" >
        <property name="tokenStore" ref="mongoDBTokenStore" />
        <property name="supportRefreshToken" value="true" />
        <property name="clientDetailsService" ref="clientDetails" />
        <!--  Access token & Refresh token will be expired in 1 year 1 second after being granted -->
        <property name="accessTokenValiditySeconds" value="31536001"></property>
        <property name="refreshTokenValiditySeconds" value="31536001"></property>
    </bean>

    <bean id="ovTokenExtractor" class="com.alu.ov.ngnms.appserver.login.OVTokenExtractor"></bean>
    <bean id="oauth2AuthenticationManager" class="org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager">
        <property name="tokenServices" ref="tokenServices"></property>
    </bean>
    <bean id="customOAuth2AuthenProcessingFilter" class="org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter">
        <property name="authenticationEntryPoint" ref="authenticationEntryPoint"></property>
        <property name="authenticationManager" ref="oauth2AuthenticationManager"></property>
        <property name="tokenExtractor" ref="ovTokenExtractor"></property>
    </bean>

    <bean id="clientDetails" class="com.alu.ov.ngnms.appserver.token.CustomClientDetailsService"/>

    <security:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
        <!--you could also wire in the expression handler up at the layer of the http filters. See https://jira.springsource.org/browse/SEC-1452 -->
        <security:expression-handler ref="oauthExpressionHandler" />
    </security:global-method-security>

    <oauth:expression-handler id="oauthExpressionHandler" />

    <mvc:annotation-driven />

    <mvc:default-servlet-handler />

    <oauth:web-expression-handler id="oauthWebExpressionHandler" />
</beans>

--------EDIT 1-------- added web.xml:

<servlet>
        <servlet-name>atmoSpring</servlet-name>
        <servlet-class>org.atmosphere.cpr.MeteorServlet</servlet-class>


        <init-param>
            <!-- When MeteorServlet is used, this is the parameter that will be looked 
                and all requests will be delegated to this servlet, Of course, since we are 
                using, Spring MVC, we delegate to DispatcherServlet -->
            <param-name>org.atmosphere.servlet</param-name>
            <param-value>org.springframework.web.servlet.DispatcherServlet</param-value>
        </init-param>

        <!-- Bunch of Atmosphere specific properties -->
        <init-param>
            <param-name>org.atmosphere.cpr.broadcasterClass</param-name>
            <param-value>org.atmosphere.cpr.DefaultBroadcaster</param-value>
        </init-param>

        <!-- Set Atmosphere to use the container native Comet support. -->
        <init-param>
            <param-name>org.atmosphere.useNative</param-name>
            <param-value>true</param-value>
        </init-param>

        <!-- Force Atmosphere to use stream when writing bytes. -->
        <init-param>
            <param-name>org.atmosphere.useStream</param-name>
            <param-value>true</param-value>
        </init-param>

        <init-param>
            <param-name>org.atmosphere.cpr.AtmosphereInterceptor</param-name>
            <param-value>org.atmosphere.interceptor.SSEAtmosphereInterceptor</param-value>
        </init-param>

        <init-param>
            <param-name>org.atmosphere.interceptor.SSEAtmosphereInterceptor.contentType</param-name>
            <param-value>text/event-stream</param-value>
        </init-param>

        <!-- Use this interceptor to prevent firewall/proxies from canceling the 
            connection after a specific idle time -->
        <init-param>
            <param-name>org.atmosphere.cpr.AtmosphereInterceptor</param-name>
            <param-value>org.atmosphere.interceptor.HeartbeatInterceptor</param-value>
        </init-param>
        <init-param>
            <param-name>org.atmosphere.interceptor.HeartbeatInterceptor.heartbeatFrequencyInSeconds</param-name>
            <param-value>30</param-value>
        </init-param>

        <init-param>
            <param-name>org.atmosphere.useWebSocketAndServlet3</param-name>
            <param-value>false</param-value>
        </init-param>

        <init-param>
            <param-name>org.atmosphere.cpr.AtmosphereInterceptor.disableDefaults</param-name>
            <param-value>true</param-value>
        </init-param>

        <init-param>
            <param-name>org.atmosphere.cpr.broadcasterCacheClass</param-name>
            <param-value>org.atmosphere.cache.UUIDBroadcasterCache</param-value>
        </init-param>

        <init-param>
            <param-name>org.atmosphere.cpr.broadcaster.shareableThreadPool</param-name>
            <param-value>true</param-value>
        </init-param>


        <init-param>
            <param-name>org.atmosphere.cpr.sessionSupport</param-name>
            <param-value>true</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>atmoSpring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/webContext.xml</param-value>
    </context-param>

    <!-- Spring Security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher> 
        <dispatcher>ASYNC</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

    <filter>
        <filter-name>cacheControlFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
    </filter>

    <filter-mapping>
        <filter-name>cacheControlFilterChain</filter-name>
        <url-pattern>/api/*</url-pattern>
    </filter-mapping>

    <!-- Session Listener for Webstart -->
    <listener>
        <listener-class>com.alu.ov.ngnms.appserver.controller.SessionListener</listener-class>
    </listener>
2
Probably you didn't define the endpoint and map it to a servlet. You didn't show any of that configuration, or describe how you create the application context.Dave Syer
@DaveSyer thanks for your reply, the configuration file's quite long, so I just copied some related beans, I added web.xmlLong nh
edited - add the full security context and web.xml; actually, our web app used the spring oauth2 1.0.0 before, and now I try to migrate it to the newest version for new supported features however the sample of oauth2 in github just contained annotation base configuration, so I m not sure about my new config. Do you know any migration tutorial for oauth2 from 1.x to 2.x?Long nh
Try to run your oauth2 server in debug mode and provide log.Pratik Shah
@prtk_shah thanks for your support, but the problem has been solved by re-configuring the web.xml as Dave's suggestionLong nh

2 Answers

0
votes

You can't define the Authorization Server endpoints in a ContextLoaderListener. I don't really know how that MeteorServlet works, but you have to get the configuration into the DispatcherServlet so it can handle the "/oauth/token" requests (the handler is created by the <authorization-server/> declaration).

0
votes

I also got 404 on /servlet/oauth/token requests. In my case I had the DispatcherServlet mapped to /servlet/*, instead of /* in the web.xml file. I had to update the AuthorizationServerConfigurerAdapter to use a prefix, so that both the authorization filter and the request mapping handler adapter can handle the oauth requests (ie: /servlet/oauth/token for me) @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.prefix("/servlet").authenticationManager(authenticationManager); }

It was also important to set the DispatcherWebApplicationContextSuffix in the AbstractSecurityWebApplicationInitializer to reference the Dispatcher servlet name from web.xml file.