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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With