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

刷新令牌流程

  1. 调用刷新令牌端点
  • org.springframework.security.oauth2.provider.endpoint.TokenEndpoint
@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
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
@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
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
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
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
@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
@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,否则无法注入
@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
原文地址:https://www.cnblogs.com/luguojun/p/14294690.html