✨ Introducing new features. close #3233 添加登录踢出功能
This commit is contained in:
1 parent
0ac7776cbb
commit
dac44bc2fb
3 files changed
+414
-6
No files matched your search
+29
-2
@@ -22,11 +22,15 @@ package com.pig4cloud.pigx.auth.config;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxCommenceAuthExceptionEntryPoint;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxWebResponseExceptionTranslator;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxCustomTokenServices;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
|
||||
@@ -37,6 +41,9 @@ import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@@ -76,11 +83,31 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
|
||||
@Override
|
||||
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST).tokenStore(redisTokenStore)
|
||||
.tokenEnhancer(tokenEnhancer).userDetailsService(pigxUserDetailsService)
|
||||
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST).tokenServices(tokenServices())
|
||||
.tokenStore(redisTokenStore).tokenEnhancer(tokenEnhancer).userDetailsService(pigxUserDetailsService)
|
||||
.authorizationCodeServices(authorizationCodeServices).authenticationManager(authenticationManagerBean)
|
||||
.reuseRefreshTokens(false).pathMapping("/oauth/confirm_access", "/token/confirm_access")
|
||||
.exceptionTranslator(new PigxWebResponseExceptionTranslator());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PigxCustomTokenServices tokenServices() {
|
||||
PigxCustomTokenServices tokenServices = new PigxCustomTokenServices();
|
||||
tokenServices.setTokenStore(redisTokenStore);
|
||||
tokenServices.setSupportRefreshToken(true);
|
||||
tokenServices.setReuseRefreshToken(false);
|
||||
tokenServices.setClientDetailsService(pigxClientDetailsServiceImpl);
|
||||
tokenServices.setTokenEnhancer(tokenEnhancer);
|
||||
addUserDetailsService(tokenServices, pigxUserDetailsService);
|
||||
return tokenServices;
|
||||
}
|
||||
|
||||
private void addUserDetailsService(PigxCustomTokenServices tokenServices, UserDetailsService userDetailsService) {
|
||||
if (userDetailsService != null) {
|
||||
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
|
||||
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
|
||||
tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-4
@@ -19,6 +19,7 @@ package com.pig4cloud.pigx.common.security.handler;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxCustomTokenServices;
|
||||
import com.pig4cloud.pigx.common.security.util.AuthUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -33,7 +34,6 @@ import org.springframework.security.oauth2.common.exceptions.InvalidClientExcept
|
||||
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -61,7 +61,7 @@ public class MobileLoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private AuthorizationServerTokenServices defaultAuthorizationServerTokenServices;
|
||||
private PigxCustomTokenServices tokenServices;
|
||||
|
||||
/**
|
||||
* Called when a user has been successfully authenticated. 调用spring security oauth API
|
||||
@@ -99,8 +99,7 @@ public class MobileLoginSuccessHandler implements AuthenticationSuccessHandler {
|
||||
new DefaultOAuth2RequestValidator().validateScope(tokenRequest, clientDetails);
|
||||
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
|
||||
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
|
||||
OAuth2AccessToken oAuth2AccessToken = defaultAuthorizationServerTokenServices
|
||||
.createAccessToken(oAuth2Authentication);
|
||||
OAuth2AccessToken oAuth2AccessToken = tokenServices.createAccessToken(oAuth2Authentication);
|
||||
log.info("获取token 成功:{}", oAuth2AccessToken.getValue());
|
||||
|
||||
response.setCharacterEncoding(CharsetUtil.UTF_8);
|
||||
|
||||
+382
@@ -0,0 +1,382 @@
|
||||
package com.pig4cloud.pigx.common.security.service;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.*;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidScopeException;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.token.*;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 自定义 token 放发处理逻辑
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2021/10/15
|
||||
*/
|
||||
public class PigxCustomTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
|
||||
ConsumerTokenServices, InitializingBean {
|
||||
|
||||
private int refreshTokenValiditySeconds = 60 * 60 * 24 * 30; // default 30 days.
|
||||
|
||||
private int accessTokenValiditySeconds = 60 * 60 * 12; // default 12 hours.
|
||||
|
||||
private boolean supportRefreshToken = false;
|
||||
|
||||
private boolean reuseRefreshToken = true;
|
||||
|
||||
private TokenStore tokenStore;
|
||||
|
||||
private ClientDetailsService clientDetailsService;
|
||||
|
||||
private TokenEnhancer accessTokenEnhancer;
|
||||
|
||||
private AuthenticationManager authenticationManager;
|
||||
|
||||
/**
|
||||
* Initialize these token services. If no random generator is set, one will be
|
||||
* created.
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(tokenStore, "tokenStore must be set");
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
|
||||
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
|
||||
// 若已产生token , 过期时删除相关token,执行下边的重新生成逻辑
|
||||
if (existingAccessToken != null) {
|
||||
tokenStore.removeAccessToken(existingAccessToken);
|
||||
if (existingAccessToken.getRefreshToken() != null) {
|
||||
refreshToken = existingAccessToken.getRefreshToken();
|
||||
tokenStore.removeRefreshToken(refreshToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (refreshToken == null) {
|
||||
refreshToken = createRefreshToken(authentication);
|
||||
}
|
||||
|
||||
else if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
|
||||
ExpiringOAuth2RefreshToken expiring = (ExpiringOAuth2RefreshToken) refreshToken;
|
||||
if (System.currentTimeMillis() > expiring.getExpiration().getTime()) {
|
||||
refreshToken = createRefreshToken(authentication);
|
||||
}
|
||||
}
|
||||
|
||||
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
|
||||
tokenStore.storeAccessToken(accessToken, authentication);
|
||||
|
||||
refreshToken = accessToken.getRefreshToken();
|
||||
if (refreshToken != null) {
|
||||
tokenStore.storeRefreshToken(refreshToken, authentication);
|
||||
}
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@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.
|
||||
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;
|
||||
}
|
||||
|
||||
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
|
||||
return tokenStore.getAccessToken(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a refreshed authentication.
|
||||
* @param authentication The authentication.
|
||||
* @param request The scope for the refreshed token.
|
||||
* @return The refreshed authentication.
|
||||
* @throws InvalidScopeException If the scope requested is invalid or wider than the
|
||||
* original scope.
|
||||
*/
|
||||
private OAuth2Authentication createRefreshedAuthentication(OAuth2Authentication authentication,
|
||||
TokenRequest request) {
|
||||
OAuth2Authentication narrowed = authentication;
|
||||
Set<String> scope = request.getScope();
|
||||
OAuth2Request clientAuth = authentication.getOAuth2Request().refresh(request);
|
||||
if (scope != null && !scope.isEmpty()) {
|
||||
Set<String> originalScope = clientAuth.getScope();
|
||||
if (originalScope == null || !originalScope.containsAll(scope)) {
|
||||
throw new InvalidScopeException(
|
||||
"Unable to narrow the scope of the client authentication to " + scope + ".", originalScope);
|
||||
}
|
||||
else {
|
||||
clientAuth = clientAuth.narrowScope(scope);
|
||||
}
|
||||
}
|
||||
narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
|
||||
return narrowed;
|
||||
}
|
||||
|
||||
protected boolean isExpired(OAuth2RefreshToken refreshToken) {
|
||||
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
|
||||
ExpiringOAuth2RefreshToken expiringToken = (ExpiringOAuth2RefreshToken) refreshToken;
|
||||
return expiringToken.getExpiration() == null
|
||||
|| System.currentTimeMillis() > expiringToken.getExpiration().getTime();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public OAuth2AccessToken readAccessToken(String accessToken) {
|
||||
return tokenStore.readAccessToken(accessToken);
|
||||
}
|
||||
|
||||
public OAuth2Authentication loadAuthentication(String accessTokenValue)
|
||||
throws AuthenticationException, InvalidTokenException {
|
||||
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
|
||||
if (accessToken == null) {
|
||||
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
|
||||
}
|
||||
else if (accessToken.isExpired()) {
|
||||
tokenStore.removeAccessToken(accessToken);
|
||||
throw new InvalidTokenException("Access token expired: " + accessTokenValue);
|
||||
}
|
||||
|
||||
OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
|
||||
if (result == null) {
|
||||
// in case of race condition
|
||||
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
|
||||
}
|
||||
if (clientDetailsService != null) {
|
||||
String clientId = result.getOAuth2Request().getClientId();
|
||||
try {
|
||||
clientDetailsService.loadClientByClientId(clientId);
|
||||
}
|
||||
catch (ClientRegistrationException e) {
|
||||
throw new InvalidTokenException("Client not valid: " + clientId, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getClientId(String tokenValue) {
|
||||
OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
|
||||
if (authentication == null) {
|
||||
throw new InvalidTokenException("Invalid access token: " + tokenValue);
|
||||
}
|
||||
OAuth2Request clientAuth = authentication.getOAuth2Request();
|
||||
if (clientAuth == null) {
|
||||
throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue);
|
||||
}
|
||||
return clientAuth.getClientId();
|
||||
}
|
||||
|
||||
public boolean revokeToken(String tokenValue) {
|
||||
OAuth2AccessToken accessToken = tokenStore.readAccessToken(tokenValue);
|
||||
if (accessToken == null) {
|
||||
return false;
|
||||
}
|
||||
if (accessToken.getRefreshToken() != null) {
|
||||
tokenStore.removeRefreshToken(accessToken.getRefreshToken());
|
||||
}
|
||||
tokenStore.removeAccessToken(accessToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
|
||||
if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
|
||||
return null;
|
||||
}
|
||||
int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
|
||||
String value = UUID.randomUUID().toString();
|
||||
if (validitySeconds > 0) {
|
||||
return new DefaultExpiringOAuth2RefreshToken(value,
|
||||
new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
|
||||
}
|
||||
return new DefaultOAuth2RefreshToken(value);
|
||||
}
|
||||
|
||||
private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
|
||||
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
|
||||
int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
|
||||
if (validitySeconds > 0) {
|
||||
token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
|
||||
}
|
||||
token.setRefreshToken(refreshToken);
|
||||
token.setScope(authentication.getOAuth2Request().getScope());
|
||||
|
||||
return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
|
||||
}
|
||||
|
||||
/**
|
||||
* The access token validity period in seconds
|
||||
* @param clientAuth the current authorization request
|
||||
* @return the access token validity period in seconds
|
||||
*/
|
||||
protected int getAccessTokenValiditySeconds(OAuth2Request clientAuth) {
|
||||
if (clientDetailsService != null) {
|
||||
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
|
||||
Integer validity = client.getAccessTokenValiditySeconds();
|
||||
if (validity != null) {
|
||||
return validity;
|
||||
}
|
||||
}
|
||||
return accessTokenValiditySeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* The refresh token validity period in seconds
|
||||
* @param clientAuth the current authorization request
|
||||
* @return the refresh token validity period in seconds
|
||||
*/
|
||||
protected int getRefreshTokenValiditySeconds(OAuth2Request clientAuth) {
|
||||
if (clientDetailsService != null) {
|
||||
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
|
||||
Integer validity = client.getRefreshTokenValiditySeconds();
|
||||
if (validity != null) {
|
||||
return validity;
|
||||
}
|
||||
}
|
||||
return refreshTokenValiditySeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a refresh token supported for this client (or the global setting if
|
||||
* {@link #setClientDetailsService(ClientDetailsService) clientDetailsService} is not
|
||||
* set.
|
||||
* @param clientAuth the current authorization request
|
||||
* @return boolean to indicate if refresh token is supported
|
||||
*/
|
||||
protected boolean isSupportRefreshToken(OAuth2Request clientAuth) {
|
||||
if (clientDetailsService != null) {
|
||||
ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
|
||||
return client.getAuthorizedGrantTypes().contains("refresh_token");
|
||||
}
|
||||
return this.supportRefreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* An access token enhancer that will be applied to a new token before it is saved in
|
||||
* the token store.
|
||||
* @param accessTokenEnhancer the access token enhancer to set
|
||||
*/
|
||||
public void setTokenEnhancer(TokenEnhancer accessTokenEnhancer) {
|
||||
this.accessTokenEnhancer = accessTokenEnhancer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The validity (in seconds) of the refresh token. If less than or equal to zero then
|
||||
* the tokens will be non-expiring.
|
||||
* @param refreshTokenValiditySeconds The validity (in seconds) of the refresh token.
|
||||
*/
|
||||
public void setRefreshTokenValiditySeconds(int refreshTokenValiditySeconds) {
|
||||
this.refreshTokenValiditySeconds = refreshTokenValiditySeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default validity (in seconds) of the access token. Zero or negative for
|
||||
* non-expiring tokens. If a client details service is set the validity period will be
|
||||
* read from the client, defaulting to this value if not defined by the client.
|
||||
* @param accessTokenValiditySeconds The validity (in seconds) of the access token.
|
||||
*/
|
||||
public void setAccessTokenValiditySeconds(int accessTokenValiditySeconds) {
|
||||
this.accessTokenValiditySeconds = accessTokenValiditySeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to support the refresh token.
|
||||
* @param supportRefreshToken Whether to support the refresh token.
|
||||
*/
|
||||
public void setSupportRefreshToken(boolean supportRefreshToken) {
|
||||
this.supportRefreshToken = supportRefreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to reuse refresh tokens (until expired).
|
||||
* @param reuseRefreshToken Whether to reuse refresh tokens (until expired).
|
||||
*/
|
||||
public void setReuseRefreshToken(boolean reuseRefreshToken) {
|
||||
this.reuseRefreshToken = reuseRefreshToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* The persistence strategy for token storage.
|
||||
* @param tokenStore the store for access and refresh tokens.
|
||||
*/
|
||||
public void setTokenStore(TokenStore tokenStore) {
|
||||
this.tokenStore = tokenStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* An authentication manager that will be used (if provided) to check the user
|
||||
* authentication when a token is refreshed.
|
||||
* @param authenticationManager the authenticationManager to set
|
||||
*/
|
||||
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* The client details service to use for looking up clients (if necessary). Optional
|
||||
* if the access token expiry is set globally via
|
||||
* {@link #setAccessTokenValiditySeconds(int)}.
|
||||
* @param clientDetailsService the client details service
|
||||
*/
|
||||
public void setClientDetailsService(ClientDetailsService clientDetailsService) {
|
||||
this.clientDetailsService = clientDetailsService;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user