I would like to implement OAuth 2.0 for my Spring 3.1 and RESTEasy project. The project is a JSON based REST service. I use Spring Security 3.1 and spring-security-oauth2 version 1.0.0.RC2 (which should be the latest). So far I have the spring security setup with default settings. I also have very basic (default) configuration for OAuth 2.0.

I used the REST service before, it works perfect. The Spring security also seems to work just fine. I am redirected to a login page if I open a link to my REST service. After logging in I can make REST calls that give the expected result.

When I open het urls localhost:8080/tools-service/oauth/token or localhost:8080/tools-service/oauth/error, to test the OAuth, I get an error 500. The following error is show when I access /oauth/token. The error for /oauth/error is simular.

HTTP Status 500 - No adapter for handler [public org.springframework.http.ResponseEntity org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.getAccessToken(java.security.Principal,java.lang.String,java.util.Map)]: Does your handler implement a supported interface like Controller?

If I am correct this implies that there is an error in the TokenEndpoint.getAccessToken function? Since that class is part of the Spring framework (and I looked up the code, which looks fine) I don't think the problem is actually related to those classes. Which leaves me clueless.

Now I would like to know why this happens and how I can solve this. I considered the fact that I, maybe, am not allowed to visit those URL's in a browser. However if I try the same with Sparklr2 (the Spring OAuth 2.0 sample application) I get an XML message (for /oauth2/token) and an error page (for /oauth2/error), which is as expected.

Any help or tip would be greatly be appreciated.


Security related snippet from web.xml:

<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>

My application context loads the following security-config.xml file I created:

<?xml version="1.0" encoding="UTF-8"?>

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:sec="http://www.springframework.org/schema/security"
    xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/security
                        http://www.springframework.org/schema/security/spring-security-3.1.xsd
                        http://www.springframework.org/schema/security/oauth2
                        http://www.springframework.org/schema/security/spring-security-oauth2.xsd">

    <sec:http auto-config="true">
        <sec:intercept-url pattern="/**" access="ROLE_USER" />
    </sec:http>

    <sec:authentication-manager>
        <sec:authentication-provider>
            <sec:user-service>
                <sec:user name="user1" password="test123" authorities="ROLE_USER" />
                <sec:user name="user2" password="hello123" authorities="ROLE_USER" />
            </sec:user-service>
        </sec:authentication-provider>
    </sec:authentication-manager>

    <sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
        <sec:expression-handler ref="oauthExpressionHandler" />
    </sec:global-method-security>


    <bean id="clientDetailsService" class="be.collectortools.rest.service.security.CollectorDetailsServiceImpl" />

    <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />

    <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <property name="tokenStore" ref="tokenStore" />
        <property name="supportRefreshToken" value="true" />
        <property name="clientDetailsService" ref="clientDetailsService"/>
    </bean>

    <oauth:authorization-server
            client-details-service-ref="clientDetailsService"
            token-services-ref="tokenServices">
        <oauth:authorization-code />
        <oauth:implicit />
        <oauth:refresh-token />
        <oauth:client-credentials />
        <oauth:password />
    </oauth:authorization-server>

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

</beans>

The CollectorClientDetails implementation is only dummy code:

@Service
public class CollectorDetailsServiceImpl implements ClientDetailsService {

    @Resource
    private CollectorClientDetailsRepository collectorClientDetailsRepository;

    @Override
    public ClientDetails loadClientByClientId(final String clientId) throws OAuth2Exception {
        CollectorClientDetails dummyClient = new CollectorClientDetails();
        dummyClient.setClientId(clientId);

        return dummyClient;
    }

}
有帮助吗?

解决方案

After letting this problem cool down for a few days I did a new Google search. This led me to the Spring Source forum: http://forum.springsource.org/showthread.php?130684-OAuth2-No-adapter-for-handler-exception.

Here I found that banifou had the same problem. Dave Syer answered the question like this:

It looks like you removed the <mvc:annnotation-driven/> from the vanilla sparklr. I think if you put that back in the handler adapter will be defined for you.

Spring security relies on the Spring MVC framework to deal with requests and responses. Therefor the MVC framework needs to be included and properly setup for Spring security OAuth to work.

The solution is that I added 2 tags to my application context:

  • <mvc:annotation-driven />

Make the MVC framwork ready to handle annotations. This makes @FrameworkEndpoint work properly. The later one being used in the public class TokenEndpoint, which gave the error 500.

  • <mvc:default-servlet-handler />

This handler will forward all requests to the default Servlet. Therefore it is important that it remains last in the order of all other URL HandlerMappings. That will be the case if you use <mvc:annotation-driven>. (Quote from the Spring documentation.)

More information can be found here: annotation-driven, default-servlet-handler.

<?xml version='1.0' encoding='UTF-8'?>

<beans  xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        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.1.xsd
                            http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.1.xsd
                            http://www.springframework.org/schema/mvc
                            http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">

    <context:component-scan base-package="be.collectortools.rest"/>
    <context:annotation-config/>

    <mvc:annotation-driven />
    <mvc:default-servlet-handler />

    <import resource="classpath:springmvc-resteasy.xml"/>
    <import resource="mongo-config.xml"/>
    <import resource="security-config.xml"/>

</beans>
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top