ZPY博客

spring security oauth2 0报错authentication is required to obtain an access token anonymous not allowed

---
title: Spring Security Oauth2.0报错Authentication is required to obtain an access token (anonymous not allowed)
date: 2019-01-10 17:04:39
categories: Spring Security
tags:
- 报错
- token
- access
- Required
- 客户端
- security
- oauth2.0
- Authentication
- obtain
- anonymous
- 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