ZPY博客

Spring Security Oauth2.0报错Authentication is required to obtain an access token (anonymous not allowed)

Spring Security Oauth2.0报错

org.springframework.security.authentication.InsufficientAuthenticationException: Authentication is required to obtain an access token (anonymous not allowed)

anonymous这个单词是匿名的意思,也就是说不允许匿名访问access token。

出问题的代码如下:

@Bean
       public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext context, OAuth2ProtectedResourceDetails details) {
           OAuth2RestTemplate template = new OAuth2RestTemplate(details, context);

           AuthorizationCodeAccessTokenProvider authCodeProvider = new AuthorizationCodeAccessTokenProvider();
           authCodeProvider.setStateMandatory(false);
           AccessTokenProviderChain provider = new AccessTokenProviderChain(
                   Arrays.asList(authCodeProvider));
           template.setAccessTokenProvider(provider);
           return template;
       }

 

google了很久都没有解决问题。最后在一个英文评论里找到了解决方法。

翻译过来,原因是AccessTokenProvider接口有多个实现,OAuth2RestTemplate 实例化了 AccessTokenProviderChain,是为了重用login context。但是如果客户端没有login画面的话,就会报上面的错。

而我的客户端正好是没有login画面的!!!解决方法就是将上面的代码改为:

@Bean
       public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext context, OAuth2ProtectedResourceDetails details) {
           OAuth2RestTemplate template = new OAuth2RestTemplate(details, context);

           template.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider());
           return template;
       }

英文原版如下:

I had the same problem and concluded that the AccessTokenProvider interface, which has multiple implementors is the culprit. By default, the OAuth2RestTemplate instantiates the AccessTokenProviderChain which tries to re-use the existing login context. However, if no such login exists, this is bound to fail. Try

restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());
in your factory method. This uses a token provider which owns its credentials and doesn't reference the thread local SecurityContextHolder in its implementation at all.

 

※由于我用的是code方式换取token,所以它这里的ResourceOwnerPasswordAccessTokenProvider要改成AuthorizationCodeAccessTokenProvider。不然会报下面的错:

java.lang.ClassCastException: org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails cannot be cast to org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails