7

I'm trying to run an sample application using spring boot and spring security oauth with a configured JdbcTokenStore and a DefaultTokenServices with infinite lifetime access tokens.

Running this application with gradle bootRun, the application won't start and throws an "Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy51 cannot be cast to org.springframework.security.oauth2.provider.token.DefaultTokenServices"

Why is there a proxy wrapped around the DefaultTokenServices bean?

A strange thing is - running the application with the InMemoryTokenStore ... everything works fine (See inmemory branch).

Source Code https://github.com/grafjo/oauth_demo/blob/master/src/main/java/demo/AuthorizationServerConfiguration.java

Full Trace: http://pastebin.com/SUcwz4S5

joo
  • 170
  • 1
  • 9
  • I encountered this when I had transaction support enabled for JPA. Your `@SpringBootApplication` is probably auto-configuring a transactional datasource, yes? I definitely tracked it down in my non-spring-boot project to ``. Once I commented that out, my authorization service configured fine. Not sure what the precise conflict is, but I'm fairly certain there's a systemic bug here. Try disabling transactions just to verify. – MattSenter Apr 26 '15 at 14:29
  • Are you sure you want infinite lifetime access tokens?! This defeats pretty much the purpose of having oauth. Maybe infinite lifetime for the refresh token. – nucatus May 20 '15 at 21:09

5 Answers5

9

A quick peek inside DefaultTokenService reveals it is annotated with @Transactional. Spring is going to wrap it in a proxy to service the transactions - as a result you need to be interacting with the class by its Interface.

For your tokenService bean:

@Bean
public DefaultTokenServices tokenServices() {
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setAccessTokenValiditySeconds(-1);
    defaultTokenServices.setTokenStore(tokenStore());
    return defaultTokenServices;
}

try changing it to this:

@Bean
public AuthorizationServerTokenServices tokenServices() {
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setAccessTokenValiditySeconds(-1);
    defaultTokenServices.setTokenStore(tokenStore());
    return defaultTokenServices;
}
psuthe
  • 106
  • 1
  • yes that works. But how is this bean autowired inside the ResourceServerConfiguration that looks for a ResourceServerTokenServices interface? Of course DefaultTokenServices implements that interface too, but when we expose it as a bean returning only the AuthorizationServerTokenServices, shouldn't it be autowired only in classes that look for AuthorizationServerTokenServices implementations? – Marios Apr 29 '15 at 09:40
  • 1
    ok, i found the answer to previous comment myself. in spring reference docs it mentions that "If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created." Therefore the bean is actually injected to any classes that need any of its interfaces, not just the one returned. Assuming this was not true I hadn't even tried your solution before you post it :) – Marios Apr 29 '15 at 09:52
3

I had a similar exception in my application too, and when changing spring oauth version from 2.0.7.RELEASE to 2.0.3.RELEASE it worked. Perhaps it is a bug in latests versions?

EDIT: from the error it seems that the problem is related to the proxies created by spring. When I change the proxy type to CGLIB instead of default dynamic proxies, then it works with version 2.0.7 too. This setting can be set via proxyTargetClass property @EnableTransactionManagement(proxyTargetClass = true)

However this solution is not attractive to me as I prefer default proxying method rather than CGLIB. Here is also an article explaining proxying methods http://thecafetechno.com/tutorials/spring/spring-proxying-mechanisms/

Marios
  • 1,947
  • 1
  • 15
  • 24
1

This works with version 2.0.7.RELEASE

 @Primary
 @Bean
 protected AuthorizationServerTokenServices tokenServices() throws Exception{

After you change DefaultTokenServices to AuthorizationServerTokenServices, Spring will throw an error:

No qualifying bean of type [org.springframework.security.oauth2.provider.token.ResourceServerTokenServices] is defined: expected single matching bean but found 3: defaultAuthorizationServerTokenServices,consumerTokenServices,tokenServices"}}

Alexander
  • 3,019
  • 1
  • 20
  • 24
0

I had the same Problem using 2.0.9.RELEASE in following combination:

pom.xml:

...    
<spring.version>4.1.4.RELEASE</spring.version>       
<spring-security.version>3.2.5.RELEASE</spring-security.version>
<spring-security-oauth2.version>2.0.9.RELEASE</spring-security-oauth2.version>
...

and had the same Exception.

Downgrading to

...
<spring-security-oauth2.version>2.0.3.RELEASE</spring-security-oauth2.version>
...

solved the Problem for me.

0

add

<aop:config proxy-target-class="true"/> 

on your spring configuration.

Robert
  • 5,278
  • 43
  • 65
  • 115
peikuo
  • 665
  • 6
  • 9