♻️ Refactoring code. close #I5A40W PigRedisOAuth2AuthorizationService 重写

This commit is contained in:
lbw 2022-05-31 17:06:23 +08:00
parent 41de38c39a
commit b6c97c6132
10 changed files with 101 additions and 59 deletions

View File

@ -21,8 +21,8 @@ import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
import com.pig4cloud.pig.admin.api.feign.RemoteClientDetailsService;
import com.pig4cloud.pig.auth.support.OAuth2EndpointUtils;
import com.pig4cloud.pig.auth.support.OAuth2ErrorCodesExpand;
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
import com.pig4cloud.pig.common.core.constant.CacheConstants;
import com.pig4cloud.pig.common.core.constant.CommonConstants;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;

View File

@ -1,6 +1,5 @@
package com.pig4cloud.pig.auth.support;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import org.springframework.lang.Nullable;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.ClaimAccessor;
@ -86,9 +85,8 @@ public class CustomeOAuth2AccessTokenGenerator implements OAuth2TokenGenerator<O
OAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();
// 组装key PIG:client:username:uuid
String key = String.format("%s:%s:%s:%s", SecurityConstants.PROJECT_PREFIX,
SecurityContextHolder.getContext().getAuthentication().getPrincipal(), context.getPrincipal().getName(),
UUID.randomUUID());
String key = String.format("%s::%s::%s", SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
context.getPrincipal().getName(), UUID.randomUUID());
OAuth2AccessToken accessToken = new CustomeOAuth2AccessTokenGenerator.OAuth2AccessTokenClaims(
OAuth2AccessToken.TokenType.BEARER, key, accessTokenClaimsSet.getIssuedAt(),

View File

@ -1,6 +1,7 @@
package com.pig4cloud.pig.auth.support;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;

View File

@ -1,5 +1,6 @@
package com.pig4cloud.pig.auth.support;
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.support.MessageSourceAccessor;

View File

@ -1,6 +1,7 @@
package com.pig4cloud.pig.auth.support;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;

View File

@ -1,6 +1,7 @@
package com.pig4cloud.pig.auth.support;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.support.MessageSourceAccessor;

View File

@ -6,40 +6,44 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.util.Assert;
import java.util.concurrent.TimeUnit;
@RequiredArgsConstructor
public class PigRedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
private final RedisTemplate<String, String> redisTemplate;
private final RedisTemplate<String, Object> redisTemplate;
private final static Long TIMEOUT = 10L;
@Override
public void save(OAuth2AuthorizationConsent authorizationConsent) {
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
redisTemplate.opsForHash().put(buildKey(authorizationConsent), authorizationConsent.getRegisteredClientId(), authorizationConsent);
redisTemplate.opsForValue().set(buildKey(authorizationConsent), authorizationConsent, TIMEOUT,
TimeUnit.MINUTES);
}
@Override
public void remove(OAuth2AuthorizationConsent authorizationConsent) {
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
redisTemplate.opsForHash().delete(buildKey(authorizationConsent), authorizationConsent.getRegisteredClientId());
redisTemplate.delete(buildKey(authorizationConsent));
}
@Override
public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
Assert.hasText(principalName, "principalName cannot be empty");
return (OAuth2AuthorizationConsent) redisTemplate.opsForHash().get(buildKey(registeredClientId, principalName), registeredClientId);
return (OAuth2AuthorizationConsent) redisTemplate.opsForValue()
.get(buildKey(registeredClientId, principalName));
}
private static String buildKey(String registeredClientId, String principalName) {
return "OAC::" + registeredClientId + "::" + principalName;
return "token:consent:" + registeredClientId + ":" + principalName;
}
private static String buildKey(OAuth2AuthorizationConsent authorizationConsent) {
return buildKey(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());
}
}

View File

@ -7,10 +7,17 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
import org.springframework.security.oauth2.core.OAuth2TokenType;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.util.Assert;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author lengleng
* @date 2022/5/27
@ -18,81 +25,110 @@ import org.springframework.util.Assert;
@RequiredArgsConstructor
public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
private final static Long TIMEOUT = 10L;
private static final String AUTHORIZATION = "token";
private final RedisTemplate<String, String> redisTemplate;
private final RedisTemplate<String, Object> redisTemplate;
@Override
public void save(OAuth2Authorization authorization) {
Assert.notNull(authorization, "authorization cannot be null");
redisTemplate.opsForHash().put(AUTHORIZATION, authorization.getId(), authorization);
if (isState(authorization)) {
String token = authorization.getAttribute("state");
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.STATE, token), authorization, TIMEOUT,
TimeUnit.MINUTES);
}
if (isCode(authorization)) {
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
.getToken(OAuth2AuthorizationCode.class);
OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken();
long between = ChronoUnit.MINUTES.between(authorizationCodeToken.getIssuedAt(),
authorizationCodeToken.getExpiresAt());
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()),
authorization, between, TimeUnit.MINUTES);
}
if (isRefreshToken(authorization)) {
OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
long between = ChronoUnit.SECONDS.between(refreshToken.getIssuedAt(), refreshToken.getExpiresAt());
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()),
authorization, between, TimeUnit.SECONDS);
}
if (isAccessToken(authorization)) {
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
long between = ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt());
redisTemplate.opsForValue().set(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()),
authorization, between, TimeUnit.SECONDS);
}
}
@Override
public void remove(OAuth2Authorization authorization) {
Assert.notNull(authorization, "authorization cannot be null");
redisTemplate.opsForHash().delete(AUTHORIZATION, authorization.getId());
List<String> keys = new ArrayList<>();
if (isState(authorization)) {
String token = authorization.getAttribute("state");
keys.add(buildKey(OAuth2ParameterNames.STATE, token));
}
if (isCode(authorization)) {
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
.getToken(OAuth2AuthorizationCode.class);
OAuth2AuthorizationCode authorizationCodeToken = authorizationCode.getToken();
keys.add(buildKey(OAuth2ParameterNames.CODE, authorizationCodeToken.getTokenValue()));
}
if (isRefreshToken(authorization)) {
OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken();
keys.add(buildKey(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken.getTokenValue()));
}
if (isAccessToken(authorization)) {
OAuth2AccessToken accessToken = authorization.getAccessToken().getToken();
keys.add(buildKey(OAuth2ParameterNames.ACCESS_TOKEN, accessToken.getTokenValue()));
}
redisTemplate.delete(keys);
}
@Override
@Nullable
public OAuth2Authorization findById(String id) {
Assert.hasText(id, "id cannot be empty");
return (OAuth2Authorization) redisTemplate.opsForHash().get(AUTHORIZATION, id);
throw new UnsupportedOperationException();
}
@Override
@Nullable
public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
Assert.hasText(token, "token cannot be empty");
for (Object authorization : redisTemplate.opsForHash().values(AUTHORIZATION)) {
if (hasToken((OAuth2Authorization) authorization, token, tokenType)) {
return (OAuth2Authorization) authorization;
}
}
return null;
Assert.notNull(tokenType, "tokenType cannot be empty");
return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
}
private static boolean hasToken(OAuth2Authorization authorization, String token,
@Nullable OAuth2TokenType tokenType) {
if (tokenType != null) {
if ("state".equals(tokenType.getValue())) {
return matchesState(authorization, token);
}
else if ("code".equals(tokenType.getValue())) {
return matchesAuthorizationCode(authorization, token);
}
else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
return matchesAccessToken(authorization, token);
}
else {
return OAuth2TokenType.REFRESH_TOKEN.equals(tokenType) && matchesRefreshToken(authorization, token);
}
}
else {
return matchesState(authorization, token) || matchesAuthorizationCode(authorization, token)
|| matchesAccessToken(authorization, token) || matchesRefreshToken(authorization, token);
}
private String buildKey(String type, String id) {
return String.format("%s::%s::%s", AUTHORIZATION, type, id);
}
private static boolean matchesState(OAuth2Authorization authorization, String token) {
return token.equals(authorization.getAttribute("state"));
private static boolean isState(OAuth2Authorization authorization) {
return Objects.nonNull(authorization.getAttribute("state"));
}
private static boolean matchesAuthorizationCode(OAuth2Authorization authorization, String token) {
private static boolean isCode(OAuth2Authorization authorization) {
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
.getToken(OAuth2AuthorizationCode.class);
return authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token);
return Objects.nonNull(authorizationCode);
}
private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) {
OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getToken(OAuth2AccessToken.class);
return accessToken != null && accessToken.getToken().getTokenValue().equals(token);
private static boolean isRefreshToken(OAuth2Authorization authorization) {
return Objects.nonNull(authorization.getRefreshToken());
}
private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getToken(OAuth2RefreshToken.class);
return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
private static boolean isAccessToken(OAuth2Authorization authorization) {
return Objects.nonNull(authorization.getAccessToken());
}
}

View File

@ -1,4 +1,4 @@
package com.pig4cloud.pig.auth.support;
package com.pig4cloud.pig.common.security.util;
import lombok.experimental.UtilityClass;
import org.springframework.security.oauth2.core.*;
@ -21,7 +21,7 @@ import java.util.Map;
@UtilityClass
public class OAuth2EndpointUtils {
static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
public final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
public MultiValueMap<String, String> getParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();

View File

@ -1,4 +1,4 @@
package com.pig4cloud.pig.auth.support;
package com.pig4cloud.pig.common.security.util;
/**
* @author jumuning