mirror of
https://gitee.com/log4j/pig.git
synced 2025-01-03 23:42:22 +08:00
♻️ Refactoring code. close #I5A40W PigRedisOAuth2AuthorizationService 重写
This commit is contained in:
parent
41de38c39a
commit
b6c97c6132
@ -21,8 +21,8 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
|
import com.pig4cloud.pig.admin.api.entity.SysOauthClientDetails;
|
||||||
import com.pig4cloud.pig.admin.api.feign.RemoteClientDetailsService;
|
import com.pig4cloud.pig.admin.api.feign.RemoteClientDetailsService;
|
||||||
import com.pig4cloud.pig.auth.support.OAuth2EndpointUtils;
|
import com.pig4cloud.pig.common.security.util.OAuth2EndpointUtils;
|
||||||
import com.pig4cloud.pig.auth.support.OAuth2ErrorCodesExpand;
|
import com.pig4cloud.pig.common.security.util.OAuth2ErrorCodesExpand;
|
||||||
import com.pig4cloud.pig.common.core.constant.CacheConstants;
|
import com.pig4cloud.pig.common.core.constant.CacheConstants;
|
||||||
import com.pig4cloud.pig.common.core.constant.CommonConstants;
|
import com.pig4cloud.pig.common.core.constant.CommonConstants;
|
||||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
package com.pig4cloud.pig.auth.support;
|
||||||
|
|
||||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.oauth2.core.ClaimAccessor;
|
import org.springframework.security.oauth2.core.ClaimAccessor;
|
||||||
@ -86,9 +85,8 @@ public class CustomeOAuth2AccessTokenGenerator implements OAuth2TokenGenerator<O
|
|||||||
OAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();
|
OAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();
|
||||||
|
|
||||||
// 组装key PIG:client:username:uuid
|
// 组装key PIG:client:username:uuid
|
||||||
String key = String.format("%s:%s:%s:%s", SecurityConstants.PROJECT_PREFIX,
|
String key = String.format("%s::%s::%s", SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
|
||||||
SecurityContextHolder.getContext().getAuthentication().getPrincipal(), context.getPrincipal().getName(),
|
context.getPrincipal().getName(), UUID.randomUUID());
|
||||||
UUID.randomUUID());
|
|
||||||
|
|
||||||
OAuth2AccessToken accessToken = new CustomeOAuth2AccessTokenGenerator.OAuth2AccessTokenClaims(
|
OAuth2AccessToken accessToken = new CustomeOAuth2AccessTokenGenerator.OAuth2AccessTokenClaims(
|
||||||
OAuth2AccessToken.TokenType.BEARER, key, accessTokenClaimsSet.getIssuedAt(),
|
OAuth2AccessToken.TokenType.BEARER, key, accessTokenClaimsSet.getIssuedAt(),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
package com.pig4cloud.pig.auth.support;
|
||||||
|
|
||||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
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.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
package com.pig4cloud.pig.auth.support;
|
||||||
|
|
||||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
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.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
package com.pig4cloud.pig.auth.support;
|
||||||
|
|
||||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.springframework.context.support.MessageSourceAccessor;
|
import org.springframework.context.support.MessageSourceAccessor;
|
||||||
|
@ -6,40 +6,44 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
|||||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PigRedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
|
public class PigRedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
|
||||||
|
|
||||||
private final RedisTemplate<String, String> redisTemplate;
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
private final static Long TIMEOUT = 10L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(OAuth2AuthorizationConsent authorizationConsent) {
|
public void save(OAuth2AuthorizationConsent authorizationConsent) {
|
||||||
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
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
|
@Override
|
||||||
public void remove(OAuth2AuthorizationConsent authorizationConsent) {
|
public void remove(OAuth2AuthorizationConsent authorizationConsent) {
|
||||||
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
||||||
redisTemplate.opsForHash().delete(buildKey(authorizationConsent), authorizationConsent.getRegisteredClientId());
|
redisTemplate.delete(buildKey(authorizationConsent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
|
public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
|
||||||
Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
|
Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
|
||||||
Assert.hasText(principalName, "principalName 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) {
|
private static String buildKey(String registeredClientId, String principalName) {
|
||||||
return "OAC::" + registeredClientId + "::" + principalName;
|
return "token:consent:" + registeredClientId + ":" + principalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String buildKey(OAuth2AuthorizationConsent authorizationConsent) {
|
private static String buildKey(OAuth2AuthorizationConsent authorizationConsent) {
|
||||||
return buildKey(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());
|
return buildKey(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,10 +7,17 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
|||||||
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
|
import org.springframework.security.oauth2.core.OAuth2AuthorizationCode;
|
||||||
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
import org.springframework.security.oauth2.core.OAuth2RefreshToken;
|
||||||
import org.springframework.security.oauth2.core.OAuth2TokenType;
|
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.OAuth2Authorization;
|
||||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||||
import org.springframework.util.Assert;
|
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
|
* @author lengleng
|
||||||
* @date 2022/5/27
|
* @date 2022/5/27
|
||||||
@ -18,81 +25,110 @@ import org.springframework.util.Assert;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
|
public class PigRedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
|
||||||
|
|
||||||
|
private final static Long TIMEOUT = 10L;
|
||||||
|
|
||||||
private static final String AUTHORIZATION = "token";
|
private static final String AUTHORIZATION = "token";
|
||||||
|
|
||||||
private final RedisTemplate<String, String> redisTemplate;
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(OAuth2Authorization authorization) {
|
public void save(OAuth2Authorization authorization) {
|
||||||
Assert.notNull(authorization, "authorization cannot be null");
|
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
|
@Override
|
||||||
public void remove(OAuth2Authorization authorization) {
|
public void remove(OAuth2Authorization authorization) {
|
||||||
Assert.notNull(authorization, "authorization cannot be null");
|
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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2Authorization findById(String id) {
|
public OAuth2Authorization findById(String id) {
|
||||||
Assert.hasText(id, "id cannot be empty");
|
throw new UnsupportedOperationException();
|
||||||
return (OAuth2Authorization) redisTemplate.opsForHash().get(AUTHORIZATION, id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
|
public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
|
||||||
Assert.hasText(token, "token cannot be empty");
|
Assert.hasText(token, "token cannot be empty");
|
||||||
for (Object authorization : redisTemplate.opsForHash().values(AUTHORIZATION)) {
|
Assert.notNull(tokenType, "tokenType cannot be empty");
|
||||||
if (hasToken((OAuth2Authorization) authorization, token, tokenType)) {
|
return (OAuth2Authorization) redisTemplate.opsForValue().get(buildKey(tokenType.getValue(), token));
|
||||||
return (OAuth2Authorization) authorization;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasToken(OAuth2Authorization authorization, String token,
|
private String buildKey(String type, String id) {
|
||||||
@Nullable OAuth2TokenType tokenType) {
|
return String.format("%s::%s::%s", AUTHORIZATION, type, id);
|
||||||
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 static boolean matchesState(OAuth2Authorization authorization, String token) {
|
private static boolean isState(OAuth2Authorization authorization) {
|
||||||
return token.equals(authorization.getAttribute("state"));
|
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
|
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||||
.getToken(OAuth2AuthorizationCode.class);
|
.getToken(OAuth2AuthorizationCode.class);
|
||||||
return authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token);
|
return Objects.nonNull(authorizationCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) {
|
private static boolean isRefreshToken(OAuth2Authorization authorization) {
|
||||||
OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getToken(OAuth2AccessToken.class);
|
return Objects.nonNull(authorization.getRefreshToken());
|
||||||
return accessToken != null && accessToken.getToken().getTokenValue().equals(token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {
|
private static boolean isAccessToken(OAuth2Authorization authorization) {
|
||||||
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getToken(OAuth2RefreshToken.class);
|
return Objects.nonNull(authorization.getAccessToken());
|
||||||
return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
package com.pig4cloud.pig.common.security.util;
|
||||||
|
|
||||||
import lombok.experimental.UtilityClass;
|
import lombok.experimental.UtilityClass;
|
||||||
import org.springframework.security.oauth2.core.*;
|
import org.springframework.security.oauth2.core.*;
|
||||||
@ -21,7 +21,7 @@ import java.util.Map;
|
|||||||
@UtilityClass
|
@UtilityClass
|
||||||
public class OAuth2EndpointUtils {
|
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) {
|
public MultiValueMap<String, String> getParameters(HttpServletRequest request) {
|
||||||
Map<String, String[]> parameterMap = request.getParameterMap();
|
Map<String, String[]> parameterMap = request.getParameterMap();
|
@ -1,4 +1,4 @@
|
|||||||
package com.pig4cloud.pig.auth.support;
|
package com.pig4cloud.pig.common.security.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jumuning
|
* @author jumuning
|
Loading…
Reference in New Issue
Block a user