前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring security oauth2授权服务刷新令牌报错UserDetailsService is required

spring security oauth2授权服务刷新令牌报错UserDetailsService is required

作者头像
路过君
发布2022-04-26 15:49:54
7040
发布2022-04-26 15:49:54
举报
文章被收录于专栏:路过君BLOG from CSDN

刷新令牌流程

  1. 调用刷新令牌端点
  • org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
代码语言:javascript
复制
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
	...
	if (isRefreshTokenRequest(parameters)) {
		// A refresh token has its own default scopes, so we should ignore any added by the factory here.
		tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
	}
	// 获取新令牌
	OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
	if (token == null) {
		throw new UnsupportedGrantTypeException("Unsupported grant type");
	}
	return getResponse(token);
}
  1. 根据grant_type=refresh_token,获取RefreshTokenGranter,并调用TokenGranter.grant
  2. RefreshTokenGranter
  • org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter
代码语言:javascript
复制
public class RefreshTokenGranter extends AbstractTokenGranter {
	...	
	@Override
	protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
		String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
		return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
	}	
}
  1. DefaultTokenServices 通过refresh_token获取认证信息,并创建预认证token,以预认证形式尝试获取userdetails
  • org.springframework.security.oauth2.provider.token.DefaultTokenServices
代码语言:javascript
复制
@Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
		throws AuthenticationException {

	if (!supportRefreshToken) {
		throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
	}

	OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
	if (refreshToken == null) {
		throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
	}

	OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
	if (this.authenticationManager != null && !authentication.isClientOnly()) {
		// The client has already been authenticated, but the user authentication might be old now, so give it a
		// chance to re-authenticate.
		// 此处创建预认证token,并通过authenticationManager认证此token获取userdetails
		Authentication user = new PreAuthenticatedAuthenticationToken(authentication.getUserAuthentication(), "", authentication.getAuthorities());
		user = authenticationManager.authenticate(user);
		Object details = authentication.getDetails();
		authentication = new OAuth2Authentication(authentication.getOAuth2Request(), user);
		authentication.setDetails(details);
	}
	String clientId = authentication.getOAuth2Request().getClientId();
	if (clientId == null || !clientId.equals(tokenRequest.getClientId())) {
		throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
	}

	// clear out any access tokens already associated with the refresh
	// token.
	tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);

	if (isExpired(refreshToken)) {
		tokenStore.removeRefreshToken(refreshToken);
		throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
	}

	authentication = createRefreshedAuthentication(authentication, tokenRequest);

	if (!reuseRefreshToken) {
		tokenStore.removeRefreshToken(refreshToken);
		refreshToken = createRefreshToken(authentication);
	}

	OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
	tokenStore.storeAccessToken(accessToken, authentication);
	if (!reuseRefreshToken) {
		tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
	}
	return accessToken;
}

5.PreAuthenticatedAuthenticationProvider 获取userdetails

  • org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider
代码语言:javascript
复制
public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
	...
	UserDetails ud = preAuthenticatedUserDetailsService
			.loadUserDetails((PreAuthenticatedAuthenticationToken) authentication);

	userDetailsChecker.check(ud);

	PreAuthenticatedAuthenticationToken result = new PreAuthenticatedAuthenticationToken(
			ud, authentication.getCredentials(), ud.getAuthorities());
	result.setDetails(authentication.getDetails());

	return result;
}

默认配置情况

  1. 默认tokenServices配置
  • org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer
代码语言:javascript
复制
private AuthorizationServerTokenServices tokenServices() {
	if (tokenServices != null) {
		return tokenServices;
	}
	this.tokenServices = createDefaultTokenServices();
	return tokenServices;
}
public AuthorizationServerTokenServices getDefaultAuthorizationServerTokenServices() {
	if (defaultTokenServices != null) {
		return defaultTokenServices;
	}
	this.defaultTokenServices = createDefaultTokenServices();
	return this.defaultTokenServices;
}

private DefaultTokenServices createDefaultTokenServices() {
	DefaultTokenServices tokenServices = new DefaultTokenServices();
	tokenServices.setTokenStore(tokenStore());
	tokenServices.setSupportRefreshToken(true);
	tokenServices.setReuseRefreshToken(reuseRefreshToken);
	tokenServices.setClientDetailsService(clientDetailsService());
	tokenServices.setTokenEnhancer(tokenEnhancer());
	addUserDetailsService(tokenServices, this.userDetailsService);
	return tokenServices;
}
  1. 默认PreAuthenticatedAuthenticationProvider配置
  • org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer
代码语言:javascript
复制
private ClientDetailsService clientDetailsService() {
	if (clientDetailsService == null) {
		this.clientDetailsService = new InMemoryClientDetailsService();
	}
	if (this.defaultTokenServices != null) {
		addUserDetailsService(defaultTokenServices, userDetailsService);
	}
	return this.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)));
	}
}
  1. 默认UserDetailsService配置
  • org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerSecurityConfiguration
代码语言:javascript
复制
@Override
protected void configure(HttpSecurity http) throws Exception {
	...
	// 如果没有配置UserDetailsService
	if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
		UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
		endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
	}
	...
}

解决方案

3种方案选一即可

  1. AuthorizationServer增加配置UserDetailsService
代码语言:javascript
复制
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	@Autowired
    UserDetailsService userDetailsService;
	...
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    	...
        endpoints.userDetailsService(userDetailsService);
        ...
    }
    ...
}
  1. WebSecurity将UserDetailsService注入AuthorizationServerEndpointsConfigurer 此方案需要AuthorizationServerConfig 的Order高于WebSecurityConfig,否则无法注入
代码语言:javascript
复制
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
@Override
protected void configure(HttpSecurity http) throws Exception {
    if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
        UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
        endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
    }
    ...
}
  1. AuthorizationServer增加配置自定义TokenService
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 刷新令牌流程
  • 默认配置情况
  • 解决方案
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档