✨ SAS 登录并发控制
This commit is contained in:
1 parent
984b1d94ec
commit
355b47279e
4 files changed
+174
-76
No files matched your search
@@ -30,6 +30,7 @@ import com.pig4cloud.pigx.auth.support.handler.PigAuthenticationFailureEventHand
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
|
||||
import com.pig4cloud.pigx.common.core.util.R;
|
||||
import com.pig4cloud.pigx.common.core.util.RetOps;
|
||||
import com.pig4cloud.pigx.common.core.util.SpringContextHolder;
|
||||
@@ -86,6 +87,8 @@ public class PigTokenEndpoint {
|
||||
|
||||
private final RemoteTenantService tenantService;
|
||||
|
||||
private final KeyStrResolver tenantKeyStrResolver;
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
/**
|
||||
@@ -196,7 +199,7 @@ public class PigTokenEndpoint {
|
||||
@PostMapping("/page")
|
||||
public R<Page<TokenVo>> tokenList(@RequestBody Map<String, Object> params) {
|
||||
// 根据分页参数获取对应数据
|
||||
String key = String.format("%s::*", CacheConstants.PROJECT_OAUTH_ACCESS);
|
||||
String key = String.format("%s::%s::*", tenantKeyStrResolver.key(), CacheConstants.PROJECT_OAUTH_ACCESS);
|
||||
int current = MapUtil.getInt(params, CommonConstants.CURRENT);
|
||||
int size = MapUtil.getInt(params, CommonConstants.SIZE);
|
||||
Set<String> keys = redisTemplate.keys(key);
|
||||
@@ -221,10 +224,17 @@ public class PigTokenEndpoint {
|
||||
tokenVo.setIssuedAt(issuedAt);
|
||||
|
||||
Map<String, Object> attributes = authorization.getAttributes();
|
||||
Authentication authentication = (Authentication)attributes.get(Principal.class.getName());
|
||||
Authentication authentication = (Authentication) attributes.get(Principal.class.getName());
|
||||
PigxUser pigxUser = (PigxUser) authentication.getPrincipal();
|
||||
tokenVo.setUserId(pigxUser.getId());
|
||||
return tokenVo;
|
||||
}).filter(tokenVo -> {
|
||||
// 根据用户名过滤
|
||||
String username = MapUtil.getStr(params, SecurityConstants.DETAILS_USERNAME);
|
||||
if (StrUtil.isBlank(username)) {
|
||||
return true;
|
||||
}
|
||||
return tokenVo.getUsername().contains(username);
|
||||
}).collect(Collectors.toList());
|
||||
result.setRecords(tokenVoList);
|
||||
result.setTotal(keys.size());
|
||||
|
||||
+102
-69
@@ -1,10 +1,14 @@
|
||||
package com.pig4cloud.pigx.auth.support.base;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pigx.common.security.service.PigRedisOAuth2AuthorizationService;
|
||||
import com.pig4cloud.pigx.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import com.pig4cloud.pigx.common.security.util.ScopeException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -141,78 +145,20 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
.principal(usernamePasswordAuthentication)
|
||||
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.authorizationGrant(resouceOwnerBaseAuthentication);
|
||||
// @formatter:on
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization
|
||||
.withRegisteredClient(registeredClient).principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (generatedAccessToken == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the access token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.id(accessToken.getTokenValue())
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
|
||||
Object onlineQuantity = registeredClient.getClientSettings().getSettings()
|
||||
.get(CommonConstants.ONLINE_QUANTITY);
|
||||
// 没有设置并发控制走原有逻辑生成 || 设置同时在线为 true
|
||||
if (Objects.isNull(onlineQuantity) || BooleanUtil.toBooleanObject((String) onlineQuantity)) {
|
||||
return generatAuthenticationToken(resouceOwnerBaseAuthentication, clientPrincipal, registeredClient,
|
||||
authorizedScopes, usernamePasswordAuthentication);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
|
||||
// Do not issue refresh token to public client
|
||||
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
|
||||
// 不允许同时在线,删除原有username 关联的所有token
|
||||
PigRedisOAuth2AuthorizationService redisOAuth2AuthorizationService = (PigRedisOAuth2AuthorizationService) this.authorizationService;
|
||||
redisOAuth2AuthorizationService.removeByUsername(usernamePasswordAuthentication);
|
||||
|
||||
if (this.refreshTokenGenerator != null) {
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
|
||||
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
|
||||
}
|
||||
else {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the refresh token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
|
||||
}
|
||||
authorizationBuilder.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims()));
|
||||
return generatAuthenticationToken(resouceOwnerBaseAuthentication, clientPrincipal, registeredClient,
|
||||
authorizedScopes, usernamePasswordAuthentication);
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
@@ -221,6 +167,93 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成新的令牌
|
||||
* @param resouceOwnerBaseAuthentication
|
||||
* @param clientPrincipal
|
||||
* @param registeredClient
|
||||
* @param authorizedScopes
|
||||
* @param usernamePasswordAuthentication
|
||||
* @return OAuth2AccessTokenAuthenticationToken
|
||||
*/
|
||||
@NotNull
|
||||
private OAuth2AccessTokenAuthenticationToken generatAuthenticationToken(T resouceOwnerBaseAuthentication,
|
||||
OAuth2ClientAuthenticationToken clientPrincipal, RegisteredClient registeredClient,
|
||||
Set<String> authorizedScopes, Authentication usernamePasswordAuthentication) {
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
.registeredClient(registeredClient)
|
||||
.principal(usernamePasswordAuthentication)
|
||||
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
.authorizationGrant(resouceOwnerBaseAuthentication);
|
||||
// @formatter:on
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||
.principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (generatedAccessToken == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the access token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.id(accessToken.getTokenValue())
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
|
||||
// Do not issue refresh token to public client
|
||||
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
|
||||
|
||||
if (this.refreshTokenGenerator != null) {
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
|
||||
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
|
||||
}
|
||||
else {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"The token generator failed to generate the refresh token.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;
|
||||
}
|
||||
authorizationBuilder.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken,
|
||||
Objects.requireNonNull(authorization.getAccessToken().getClaims()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录异常转换为oauth2异常
|
||||
* @param authentication 身份验证
|
||||
|
||||
+47
-1
@@ -1,9 +1,13 @@
|
||||
package com.pig4cloud.pigx.common.security.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||
@@ -17,6 +21,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -32,6 +37,8 @@ public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationSe
|
||||
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
private final KeyStrResolver tenantKeyStrResolver;
|
||||
|
||||
@Override
|
||||
public void save(OAuth2Authorization authorization) {
|
||||
Assert.notNull(authorization, "authorization cannot be null");
|
||||
@@ -68,6 +75,11 @@ public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationSe
|
||||
redisTemplate.setValueSerializer(RedisSerializer.java());
|
||||
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()),
|
||||
authorization, between, TimeUnit.SECONDS);
|
||||
|
||||
// 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
|
||||
String tokenUsername = String.format("%s::%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION,
|
||||
SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), accessToken.getTokenValue());
|
||||
redisTemplate.opsForValue().set(tokenUsername, accessToken.getTokenValue(), between, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +108,13 @@ public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationSe
|
||||
if (isAccessToken(authorization)) {
|
||||
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
|
||||
keys.add(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()));
|
||||
|
||||
// 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
|
||||
String key = String.format("%s::%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION,
|
||||
SecurityConstants.DETAILS_USERNAME, authorization.getPrincipalName(), accessToken.getTokenValue());
|
||||
keys.add(key);
|
||||
}
|
||||
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
|
||||
@@ -116,7 +134,7 @@ public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationSe
|
||||
}
|
||||
|
||||
private String buildKey(String type, String id) {
|
||||
return String.format("%s::%s::%s", AUTHORIZATION, type, id);
|
||||
return String.format("%s::%s::%s::%s", tenantKeyStrResolver.key(), AUTHORIZATION, type, id);
|
||||
}
|
||||
|
||||
private static boolean isState(OAuth2Authorization authorization) {
|
||||
@@ -137,4 +155,32 @@ public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationSe
|
||||
return Objects.nonNull(authorization.getAccessToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* 扩展方法根据 username 查询是否存在存储的
|
||||
* @param authentication
|
||||
* @return
|
||||
*/
|
||||
public void removeByUsername(Authentication authentication) {
|
||||
// 根据 username查询对应access-token
|
||||
String authenticationName = authentication.getName();
|
||||
|
||||
// 扩展记录 access-token 、username 的关系 1::token::username::admin::xxx
|
||||
String tokenUsernameKey = String.format("%s::%s::%s::%s::*", tenantKeyStrResolver.key(), AUTHORIZATION,
|
||||
SecurityConstants.DETAILS_USERNAME, authenticationName);
|
||||
Set<String> keys = redisTemplate.keys(tokenUsernameKey);
|
||||
if (CollUtil.isEmpty(keys)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Object> tokenList = redisTemplate.opsForValue().multiGet(keys);
|
||||
|
||||
for (Object token : tokenList) {
|
||||
// 根据token 查询存储的 OAuth2Authorization
|
||||
OAuth2Authorization authorization = this.findByToken((String) token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
// 根据 OAuth2Authorization 删除相关令牌
|
||||
this.remove(authorization);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+13
-4
@@ -2,12 +2,15 @@ package com.pig4cloud.pigx.common.security.service;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysOauthClientDetails;
|
||||
import com.pig4cloud.pigx.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.util.RetOps;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
@@ -20,6 +23,7 @@ import org.springframework.security.oauth2.server.authorization.settings.TokenSe
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@@ -81,8 +85,7 @@ public class PigRemoteRegisteredClientRepository implements RegisteredClientRepo
|
||||
*/
|
||||
@Override
|
||||
@SneakyThrows
|
||||
// @Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless =
|
||||
// "#result == null")
|
||||
@Cacheable(value = CacheConstants.CLIENT_DETAILS_KEY, key = "#clientId", unless = "#result == null")
|
||||
public RegisteredClient findByClientId(String clientId) {
|
||||
|
||||
SysOauthClientDetails clientDetails = RetOps
|
||||
@@ -107,6 +110,13 @@ public class PigRemoteRegisteredClientRepository implements RegisteredClientRepo
|
||||
Optional.ofNullable(clientDetails.getScope()).ifPresent(
|
||||
scope -> Arrays.stream(scope.split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).forEach(builder::scope));
|
||||
|
||||
// 注入扩展配置
|
||||
Optional.ofNullable(clientDetails.getAdditionalInformation()).ifPresent(ext -> {
|
||||
Map map = JSONUtil.parseObj(ext).toBean(Map.class);
|
||||
builder.clientSettings(ClientSettings.withSettings(map)
|
||||
.requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove())).build());
|
||||
});
|
||||
|
||||
return builder
|
||||
.tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE)
|
||||
.accessTokenTimeToLive(Duration.ofSeconds(Optional
|
||||
@@ -115,8 +125,7 @@ public class PigRemoteRegisteredClientRepository implements RegisteredClientRepo
|
||||
Duration.ofSeconds(Optional.ofNullable(clientDetails.getRefreshTokenValidity())
|
||||
.orElse(refreshTokenValiditySeconds)))
|
||||
.build())
|
||||
.clientSettings(ClientSettings.builder()
|
||||
.requireAuthorizationConsent(!BooleanUtil.toBoolean(clientDetails.getAutoapprove())).build())
|
||||
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user