Spring OAuth2中Token Endpoint的自定义
Posted
技术标签:
【中文标题】Spring OAuth2中Token Endpoint的自定义【英文标题】:Customization of TokenEndpoint in Sprin OAuth2 【发布时间】:2019-08-07 21:24:30 【问题描述】:我想在 Spring 框架中提供 TokenEndpoint 类的自定义实现。
我已经复制了 spring 的 TokenEndpoint 类,并对需要的地方进行了更改。但是当应用程序启动时,我总是收到错误
Caused by: java.lang.IllegalStateException: TokenGranter must be provided
我在我的 OAuthConfig 中为 TokenGranter 提供了一个实现,但是 spring 没有接受它
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
endpoints.pathMapping("/oauth/token", "/oauth/token/v1")
.tokenServices(tokenServices())
.tokenGranter(tokenGranter())
.authenticationManager(authenticationManager).tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer()).accessTokenConverter(accessTokenConverter());
@Bean
@Primary
public TokenGranter tokenGranter()
TokenGranter tokenGranter = null;
if (tokenGranter == null)
tokenGranter = new TokenGranter()
private CompositeTokenGranter delegate;
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest)
if (delegate == null)
delegate = new CompositeTokenGranter(getDefaultTokenGranters());
return delegate.grant(grantType, tokenRequest);
;
return tokenGranter;
我什至尝试在我的自定义 TokenEndpoint 类中提供这个实现。 目前,自定义 TokenEndpoint 的实现与 Spring 的 TokenEndpoint 完全相同。
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
private List<TokenGranter> getDefaultTokenGranters()
ClientDetailsService clientDetails = clientDetailsService();
AuthorizationServerTokenServices tokenServices = tokenServices();
AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices();
OAuth2RequestFactory requestFactory = requestFactory();
List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails,
requestFactory));
tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
tokenGranters.add(implicit);
tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
if (authenticationManager != null)
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails,
requestFactory));
return tokenGranters;
private DefaultTokenServices createDefaultTokenServices()
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService());
tokenServices.setTokenEnhancer(tokenEnhancer());
addUserDetailsService(tokenServices, new CustomDetailsService());
return tokenServices;
private ClientDetailsService clientDetailsService()
ClientDetailsService clientDetailsService = null;
clientDetailsService = new InMemoryClientDetailsService();
addUserDetailsService(createDefaultTokenServices(), new CustomDetailsService());
return clientDetailsService;
private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService)
if (userDetailsService != null)
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>(
userDetailsService));
tokenServices
.setAuthenticationManager(new ProviderManager(Arrays.<AuthenticationProvider> asList(provider)));
private AuthorizationCodeServices authorizationCodeServices()
AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
return authorizationCodeServices;
private OAuth2RequestFactory requestFactory()
OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService());
return requestFactory;
@Bean
public JwtTokenStore tokenStore()
JwtTokenStore jwtTokenStore = new JwtTokenStore(accessTokenConverter());
return jwtTokenStore;
@Bean
@Primary
public AuthorizationServerTokenServices tokenServices()
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setAccessTokenValiditySeconds(-1);
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
@Bean
public TokenEnhancer tokenEnhancer()
return new CustomTokenEnhancer();
@Bean
public JwtAccessTokenConverter accessTokenConverter()
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter()
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)
return accessToken;
;
return converter;
这几天我一直在尝试解决这个问题,但没有任何运气。因此,我们将不胜感激。
【问题讨论】:
我已经复制了 spring 的 TokenEndpoint 类,并对需要的地方进行了更改。 这是否意味着您有两个TokenEndpoints
同时运行?将堆栈跟踪添加到您的问题中,然后我们将查看哪个实现引发了异常。
【参考方案1】:
我知道这个问题已经很老了,但是我遇到了同样的问题并且没有找到关于自定义 TokenEndpoint 的完整指南。我无法使用 TokenEnhancer,因为我需要更改响应的标头。所以,这是适合我的版本。
您像往常一样定义覆盖的控制器:
@RequestMapping(value = "/oauth/token")
public class CustomTokenEndpoint extends TokenEndpoint
@PostMapping
public ResponseEntity<OAuth2AccessToken> postAccessToken(
Principal principal,
@RequestParam Map<String, String> parameters
) throws HttpRequestMethodNotSupportedException
ResponseEntity<OAuth2AccessToken> defaultResponse = super.postAccessToken(principal, parameters);
// do some work
return defaultResponse;
并且您需要创建自己的 TokenEndpoint bean:
@Bean
@Primary
public TokenEndpoint tokenEndpoint(AuthorizationServerEndpointsConfiguration conf)
TokenEndpoint tokenEndpoint = new CustomTokenEndpoint();
tokenEndpoint.setClientDetailsService(conf.getEndpointsConfigurer().getClientDetailsService());
tokenEndpoint.setProviderExceptionHandler(conf.getEndpointsConfigurer().getExceptionTranslator());
tokenEndpoint.setTokenGranter(conf.getEndpointsConfigurer().getTokenGranter());
tokenEndpoint.setOAuth2RequestFactory(conf.getEndpointsConfigurer().getOAuth2RequestFactory());
tokenEndpoint.setOAuth2RequestValidator(conf.getEndpointsConfigurer().getOAuth2RequestValidator());
tokenEndpoint.setAllowedRequestMethods(conf.getEndpointsConfigurer().getAllowedTokenEndpointRequestMethods());
return tokenEndpoint;
这是踢球者。您需要允许在 application.properties
中覆盖 spring bean:
spring.main.allow-bean-definition-overriding: true
希望这对某人有所帮助
【讨论】:
【参考方案2】:为什么需要再次实现TokenEndpoint
?
您可以创建一个TokenGranter
bean 并将其注入到默认端点。
getDefaultTokenGranters()
方法在哪里?
您的 AuthorizationServerEndpointsConfigurer
源代码似乎不完整。
更新:
如果您想自定义令牌响应,请使用TokenEnhancer
。
例如:
public class CustomTokenEnhancer implements TokenEnhancer
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)
OurUser user = (OurUser) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
Map<String, Object> userDetails = new HashMap<>();
userDetails.put(USERID, user.getId().getId());
userDetails.put(NAME, user.getName());
userDetails.put(MOBILE, user.getMobile());
userDetails.put(EMAIL, user.getEmail());
additionalInfo.put(USERINFO, userDetails);
// Set additional information in token for retriving in #org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
在 OAuth2 配置中:
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
super.configure(endpoints);
endpoints.
.....
// Include additional information to OAuth2 Access token with custom token enhancer
.tokenEnhancer(tokenEnhancer());
@Bean
public TokenEnhancer tokenEnhancer()
return new CustomTokenEnhancer();
https://***.com/a/28512607/4377110
【讨论】:
我的目标是自定义返回带有访问令牌的响应的方式。因此,我正在实施 TokenEndpoint。我已经为 AuthorizationServerEndpointsConfiguration 中的所有必需方法提供了实现。没有贴到这里,因为方法太多了。我的最终目标是调整从 Endpoint 返回响应的方式,而不是提供新的 TokenGranter。 你应该使用 TokenEnhancer。以上是关于Spring OAuth2中Token Endpoint的自定义的主要内容,如果未能解决你的问题,请参考以下文章
Spring Cloud云架构 - SSO单点登录之OAuth2.0 根据token获取用户信息
(十三) 整合spring cloud云架构 - SSO单点登录之OAuth2.0 根据token获
Spring Security OAuth2 将 access_token 存储在 cookie 中
整合spring cloud云架构-SSO单点登录之OAuth2.0根据token获取用户信息