🔖 Releasing / Version tags. 3.4.0

🔖 Releasing / Version tags. 3.4.0
This commit is contained in:
MicLng 2021-10-18 09:53:39 +08:00
parent 14eb346f1c
commit 3ce61d2f7a
40 changed files with 1089 additions and 228 deletions

View File

@ -7,5 +7,4 @@
### 回显步骤
### 报错信息
### 报错信息 、截图 截图

View File

@ -55,12 +55,3 @@ jobs:
# 💤🤷‍♀️ failure 🙅‍♂️💣 [pig-mesh/pig](https://github.com/pig-mesh/pig)
> Github Action: https://github.com/pig-mesh/pig failure
> (⋟﹏⋞) from github action message
- uses: shaowenchen/debugger-action@v2
if: github.repository == 'pig-mesh/pig'
name: debugger
continue-on-error: true
with:
frp_server_addr: ${{ secrets.FRP_SERVER_ADDR }}
frp_server_port: ${{ secrets.FRP_SERVER_PORT }}
frp_token: ${{ secrets.FRP_TOKEN }}
ssh_port: ${{ secrets.SSH_PORT }}

1
.gitignore vendored
View File

@ -34,6 +34,7 @@ target/
*.zip
*.tar
*.tar.gz
*.versionsBackup
### vscode ###
.vscode

View File

@ -13,6 +13,20 @@
- 提供对常见容器化支持 Docker、Kubernetes、Rancher2 支持
- 提供 lambda 、stream api 、webflux 的生产实践
## 文档视频
[文档视频 wiki.pig4cloud.com](https://wiki.pig4cloud.com)
[PIGX 在线体验 pigx.pig4cloud.com](http://pigx.pig4cloud.com)
[产品白皮书 paper.pig4cloud.com](https://paper.pig4cloud.com)
## 微信群 [禁广告]
![1628762721](https://minio.pigx.vip/oss/1628762721.png)
## 快速开始
### 核心依赖
| 依赖 | 版本 |
@ -22,7 +36,7 @@
| Spring Cloud Alibaba | 2021.1 |
| Spring Security OAuth2 | 2.3.6 |
| Mybatis Plus | 3.4.3.5 |
| hutool | 5.7.13 |
| hutool | 5.7.14 |
| Avue | 2.6.18 |
### 模块说明
@ -55,16 +69,6 @@ pig
└── pig-xxl-job-admin -- 分布式定时任务管理台 [5004]
```
## 文档视频
[文档视频 wiki.pig4cloud.com](https://wiki.pig4cloud.com)
[PIGX 在线体验 pigx.pig4cloud.com](http://pigx.pig4cloud.com)
[产品白皮书 paper.pig4cloud.com](https://paper.pig4cloud.com)
## 快速开始
### 本地开发 运行
pig 提供了详细的[部署文档 wiki.pig4cloud.com](https://www.yuque.com/pig4cloud/pig/vsdox9),包括开发环境安装、服务端代码运行、前端代码运行等。
@ -74,6 +78,7 @@ pig 提供了详细的[部署文档 wiki.pig4cloud.com](https://www.yuque.com/pi
### 定制自己微服务
[PIG DIY](https://diy.pig4cloud.com)
[PIG ARCHETYPE](https://archetype.pig4cloud.com)
### Docker 运行
@ -105,11 +110,6 @@ npm run build:docker && docker-compose up -d
</tr>
</table>
## 微信群 [禁广告]
![1628762721](https://minio.pigx.vip/oss/1628762721.png)
## 开源共建
### 开源协议

View File

@ -12,7 +12,7 @@ services:
- 3306:3306
pig-redis:
image: redis:6.0
image: redis:6.2.6
ports:
- 6379:6379
restart: always

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-auth</artifactId>

View File

@ -16,19 +16,19 @@
package com.pig4cloud.pig.auth.config;
import com.pig4cloud.pig.common.core.constant.CacheConstants;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.component.PigWebResponseExceptionTranslator;
import com.pig4cloud.pig.common.security.grant.ResourceOwnerCustomeAppTokenGranter;
import com.pig4cloud.pig.common.security.service.PigClientDetailsService;
import com.pig4cloud.pig.common.security.service.PigUser;
import com.pig4cloud.pig.common.security.service.PigCustomTokenServices;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
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.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
@ -36,17 +36,15 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.A
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
/**
* @author lengleng
@ -63,7 +61,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
private final AuthenticationManager authenticationManager;
private final RedisConnectionFactory redisConnectionFactory;
private final TokenStore redisTokenStore;
@Override
@SneakyThrows
@ -78,8 +76,8 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST).tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer()).userDetailsService(userDetailsService)
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST).tokenServices(tokenServices())
.tokenStore(redisTokenStore).tokenEnhancer(tokenEnhancer()).userDetailsService(userDetailsService)
.authenticationManager(authenticationManager).reuseRefreshTokens(false)
.pathMapping("/oauth/confirm_access", "/token/confirm_access")
.exceptionTranslator(new PigWebResponseExceptionTranslator());
@ -98,33 +96,42 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
endpoints.tokenGranter(compositeTokenGranter);
}
@Bean
public TokenStore tokenStore() {
RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
tokenStore.setPrefix(CacheConstants.PROJECT_OAUTH_ACCESS);
return tokenStore;
}
@Bean
public TokenEnhancer tokenEnhancer() {
return (accessToken, authentication) -> {
final Map<String, Object> additionalInfo = new HashMap<>(4);
PigUser pigUser = (PigUser) authentication.getUserAuthentication().getPrincipal();
additionalInfo.put(SecurityConstants.DETAILS_LICENSE, SecurityConstants.PROJECT_LICENSE);
additionalInfo.put(SecurityConstants.DETAILS_USER_ID, pigUser.getId());
additionalInfo.put(SecurityConstants.DETAILS_USERNAME, pigUser.getUsername());
additionalInfo.put(SecurityConstants.DETAILS_DEPT_ID, pigUser.getDeptId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
};
}
@Bean
public PigClientDetailsService pigClientDetailsService() {
public ClientDetailsService pigClientDetailsService() {
PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource);
clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
return clientDetailsService;
}
@Bean
public PigCustomTokenServices tokenServices() {
PigCustomTokenServices tokenServices = new PigCustomTokenServices();
tokenServices.setTokenStore(redisTokenStore);
tokenServices.setSupportRefreshToken(true);
tokenServices.setReuseRefreshToken(false);
tokenServices.setClientDetailsService(pigClientDetailsService());
tokenServices.setTokenEnhancer(tokenEnhancer());
addUserDetailsService(tokenServices, userDetailsService);
return tokenServices;
}
private void addUserDetailsService(PigCustomTokenServices tokenServices, UserDetailsService userDetailsService) {
if (userDetailsService != null) {
PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
tokenServices.setAuthenticationManager(new ProviderManager(Collections.singletonList(provider)));
}
}
}

View File

@ -6,7 +6,7 @@
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common-bom</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
<packaging>pom</packaging>
<name>pig-common-bom</name>
@ -20,9 +20,9 @@
<security.oauth.version>2.1.8.RELEASE</security.oauth.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<git.commit.plugin>2.2.5</git.commit.plugin>
<spring.checkstyle.plugin>0.0.28</spring.checkstyle.plugin>
<fastjson.version>1.2.75</fastjson.version>
<git.commit.plugin>5.0.0</git.commit.plugin>
<spring.checkstyle.plugin>0.0.29</spring.checkstyle.plugin>
<fastjson.version>1.2.78</fastjson.version>
<swagger.core.version>1.5.24</swagger.core.version>
<mybatis-plus.version>3.4.3.4</mybatis-plus.version>
<nacos.version>2.0.3</nacos.version>
@ -150,8 +150,8 @@
<plugins>
<!--打包jar 与git commit 关联插件-->
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>${git.commit.plugin}</version>
</plugin>
<!--代码格式插件默认使用spring 规则-->

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-core</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<artifactId>pig-common</artifactId>
<groupId>com.pig4cloud</groupId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -23,7 +23,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-job</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-log</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-mybatis</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-security</artifactId>

View File

@ -0,0 +1,66 @@
package com.pig4cloud.pig.common.security.component;
import com.pig4cloud.pig.common.security.exception.UnauthorizedException;
import com.pig4cloud.pig.common.security.service.PigUser;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
/**
* @author lengleng
* @date 2020/9/29
*/
@RequiredArgsConstructor
public class PigLocalResourceServerTokenServices implements ResourceServerTokenServices {
private final TokenStore tokenStore;
private final UserDetailsService userDetailsService;
@Override
public OAuth2Authentication loadAuthentication(String accessToken)
throws AuthenticationException, InvalidTokenException {
OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(accessToken);
if (oAuth2Authentication == null) {
return null;
}
OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
if (!(oAuth2Authentication.getPrincipal() instanceof PigUser)) {
return oAuth2Authentication;
}
// 根据 username 查询 spring cache 最新的值 并返回
PigUser pigxUser = (PigUser) oAuth2Authentication.getPrincipal();
UserDetails userDetails;
try {
userDetails = userDetailsService.loadUserByUsername(pigxUser.getUsername());
}
catch (UsernameNotFoundException notFoundException) {
throw new UnauthorizedException(String.format("%s username not found", pigxUser.getUsername()),
notFoundException);
}
Authentication userAuthentication = new UsernamePasswordAuthenticationToken(userDetails, "N/A",
userDetails.getAuthorities());
OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication);
authentication.setAuthenticated(true);
return authentication;
}
@Override
public OAuth2AccessToken readAccessToken(String accessToken) {
throw new UnsupportedOperationException("Not supported: read access token");
}
}

View File

@ -0,0 +1,478 @@
package com.pig4cloud.pig.common.security.component;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author efenderbosch
* @date 2020/9/30
* <p>
* @link https://github.com/spring-projects/spring-security-oauth/pull/1660
* 重写RedisTokenStore ,主要解决 #1814 oauth2中client_id_to_access数据膨胀问题
*/
public class PigRedisTokenStore implements TokenStore {
private static final String ACCESS = "access:";
private static final String AUTH_TO_ACCESS = "auth_to_access:";
private static final String AUTH = "auth:";
private static final String REFRESH_AUTH = "refresh_auth:";
private static final String REFRESH = "refresh:";
private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access_z:";
private static final String UNAME_TO_ACCESS = "uname_to_access_z:";
private static final boolean springDataRedis_2_0 = ClassUtils.isPresent(
"org.springframework.data.redis.connection.RedisStandaloneConfiguration",
RedisTokenStore.class.getClassLoader());
private final RedisConnectionFactory connectionFactory;
private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
private String prefix = "";
private Method redisConnectionSet_2_0;
public PigRedisTokenStore(RedisConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
if (springDataRedis_2_0) {
this.loadRedisConnectionMethods_2_0();
}
}
public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
this.authenticationKeyGenerator = authenticationKeyGenerator;
}
public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
this.serializationStrategy = serializationStrategy;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
private void loadRedisConnectionMethods_2_0() {
this.redisConnectionSet_2_0 = ReflectionUtils.findMethod(RedisConnection.class, "set", byte[].class,
byte[].class);
}
private RedisConnection getConnection() {
return connectionFactory.getConnection();
}
private byte[] serialize(Object object) {
return serializationStrategy.serialize(object);
}
private byte[] serializeKey(String object) {
return serialize(object);
}
private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
return serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
}
private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
return serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
}
private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
return serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
}
private byte[] serialize(String string) {
return serializationStrategy.serialize(string);
}
private String deserializeString(byte[] bytes) {
return serializationStrategy.deserializeString(bytes);
}
@Override
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = serializeKey(AUTH_TO_ACCESS + key);
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(serializedKey);
}
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue());
if ((storedAuthentication == null
|| !key.equals(authenticationKeyGenerator.extractKey(storedAuthentication)))) {
// Keep the stores consistent (maybe the same user is
// represented by this authentication but the details have
// changed)
storeAccessToken(accessToken, authentication);
}
}
return accessToken;
}
@Override
public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
return readAuthentication(token.getValue());
}
@Override
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(serializeKey(AUTH + token));
}
return deserializeAuthentication(bytes);
}
@Override
public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
return readAuthenticationForRefreshToken(token.getValue());
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
try (RedisConnection conn = getConnection()) {
byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
return deserializeAuthentication(bytes);
}
}
@Override
public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
byte[] serializedAccessToken = serialize(token);
byte[] serializedAuth = serialize(authentication);
byte[] accessKey = serializeKey(ACCESS + token.getValue());
byte[] authKey = serializeKey(AUTH + token.getValue());
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, accessKey, serializedAccessToken);
this.redisConnectionSet_2_0.invoke(conn, authKey, serializedAuth);
this.redisConnectionSet_2_0.invoke(conn, authToAccessKey, serializedAccessToken);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
else {
conn.set(accessKey, serializedAccessToken);
conn.set(authKey, serializedAuth);
conn.set(authToAccessKey, serializedAccessToken);
}
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
long expirationTime = token.getExpiration().getTime();
if (!authentication.isClientOnly()) {
conn.zAdd(approvalKey, expirationTime, serializedAccessToken);
}
conn.zAdd(clientId, expirationTime, serializedAccessToken);
conn.expire(accessKey, seconds);
conn.expire(authKey, seconds);
conn.expire(authToAccessKey, seconds);
conn.expire(clientId, seconds);
conn.expire(approvalKey, seconds);
}
else {
conn.zAdd(clientId, -1, serializedAccessToken);
if (!authentication.isClientOnly()) {
conn.zAdd(approvalKey, -1, serializedAccessToken);
}
}
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null && refreshToken.getValue() != null) {
byte[] auth = serialize(token.getValue());
byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue());
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, refreshToAccessKey, auth);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
else {
conn.set(refreshToAccessKey, auth);
}
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
.intValue();
conn.expire(refreshToAccessKey, seconds);
}
}
}
conn.closePipeline();
}
}
private static String getApprovalKey(OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication() == null ? ""
: authentication.getUserAuthentication().getName();
return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
}
private static String getApprovalKey(String clientId, String userName) {
return clientId + (userName == null ? "" : ":" + userName);
}
@Override
public void removeAccessToken(OAuth2AccessToken accessToken) {
removeAccessToken(accessToken.getValue());
}
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
byte[] key = serializeKey(ACCESS + tokenValue);
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(key);
}
return deserializeAccessToken(bytes);
}
public void removeAccessToken(String tokenValue) {
byte[] accessKey = serializeKey(ACCESS + tokenValue);
byte[] authKey = serializeKey(AUTH + tokenValue);
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
conn.get(accessKey);
conn.get(authKey);
conn.del(accessKey);
// Don't remove the refresh token - it's up to the caller to do that
conn.del(authKey);
List<Object> results = conn.closePipeline();
byte[] access = (byte[]) results.get(0);
byte[] auth = (byte[]) results.get(1);
OAuth2Authentication authentication = deserializeAuthentication(auth);
if (authentication != null) {
String key = authenticationKeyGenerator.extractKey(authentication);
byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + key);
byte[] unameKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
conn.openPipeline();
conn.del(authToAccessKey);
conn.zRem(unameKey, access);
conn.zRem(clientId, access);
conn.del(serialize(ACCESS + key));
conn.closePipeline();
}
}
}
@Override
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue());
byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue());
byte[] serializedRefreshToken = serialize(refreshToken);
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
if (springDataRedis_2_0) {
try {
this.redisConnectionSet_2_0.invoke(conn, refreshKey, serializedRefreshToken);
this.redisConnectionSet_2_0.invoke(conn, refreshAuthKey, serialize(authentication));
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
else {
conn.set(refreshKey, serializedRefreshToken);
conn.set(refreshAuthKey, serialize(authentication));
}
if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
Date expiration = expiringRefreshToken.getExpiration();
if (expiration != null) {
int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L).intValue();
conn.expire(refreshKey, seconds);
conn.expire(refreshAuthKey, seconds);
}
}
conn.closePipeline();
}
}
@Override
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
byte[] key = serializeKey(REFRESH + tokenValue);
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(key);
}
return deserializeRefreshToken(bytes);
}
@Override
public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
removeRefreshToken(refreshToken.getValue());
}
public void removeRefreshToken(String tokenValue) {
byte[] refreshKey = serializeKey(REFRESH + tokenValue);
byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + tokenValue);
byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue);
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
conn.del(refreshKey);
conn.del(refreshAuthKey);
conn.del(refresh2AccessKey);
conn.closePipeline();
}
}
@Override
public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
removeAccessTokenUsingRefreshToken(refreshToken.getValue());
}
private void removeAccessTokenUsingRefreshToken(String refreshToken) {
byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken);
List<Object> results;
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
conn.get(key);
conn.del(key);
results = conn.closePipeline();
}
byte[] bytes = (byte[]) results.get(0);
String accessToken = deserializeString(bytes);
if (accessToken != null) {
removeAccessToken(accessToken);
}
}
private List<byte[]> getZByteLists(byte[] key, RedisConnection conn) {
// Sorted Set expiration maintenance
long currentTime = System.currentTimeMillis();
conn.zRemRangeByScore(key, 0, currentTime);
List<byte[]> byteList;
Long size = conn.zCard(key);
assert size != null;
byteList = new ArrayList<>(size.intValue());
Cursor<RedisZSetCommands.Tuple> cursor = conn.zScan(key, ScanOptions.NONE);
while (cursor.hasNext()) {
RedisZSetCommands.Tuple t = cursor.next();
// Probably not necessary because of the maintenance at the beginning but why
// not...
if (t.getScore() == -1 || t.getScore() > currentTime) {
byteList.add(t.getValue());
}
}
return byteList;
}
/**
* Runs a maintenance of the RedisTokenStore.
* <p>
* SortedSets UNAME_TO_ACCESS and CLIENT_ID_TO_ACCESS contains access tokens that can
* expire. This expiration is set as a score of the Redis SortedSet data structure.
* Redis does not support expiration of items in a container data structure. It
* supports only expiration of whole key. In case there is still new access tokens
* being stored into the RedisTokenStore before whole key gets expired, the expiration
* is prolonged and the key is not effectively deleted. To do "garbage collection"
* this method should be called once upon a time.
* @return how many items were removed
*/
public long doMaintenance() {
long removed = 0;
try (RedisConnection conn = getConnection()) {
// client_id_to_acccess maintenance
Cursor<byte[]> clientToAccessKeys = conn
.scan(ScanOptions.scanOptions().match(prefix + CLIENT_ID_TO_ACCESS + "*").build());
while (clientToAccessKeys.hasNext()) {
byte[] clientIdToAccessKey = clientToAccessKeys.next();
removed += conn.zRemRangeByScore(clientIdToAccessKey, 0, System.currentTimeMillis());
}
// uname_to_access maintenance
Cursor<byte[]> unameToAccessKeys = conn
.scan(ScanOptions.scanOptions().match(prefix + UNAME_TO_ACCESS + "*").build());
while (unameToAccessKeys.hasNext()) {
byte[] unameToAccessKey = unameToAccessKeys.next();
removed += conn.zRemRangeByScore(unameToAccessKey, 0, System.currentTimeMillis());
}
}
return removed;
}
@Override
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName));
List<byte[]> byteList;
try (RedisConnection conn = getConnection()) {
byteList = getZByteLists(approvalKey, conn);
}
if (byteList.size() == 0) {
return Collections.emptySet();
}
List<OAuth2AccessToken> accessTokens = new ArrayList<>(byteList.size());
for (byte[] bytes : byteList) {
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
}
@Override
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId);
List<byte[]> byteList;
try (RedisConnection conn = getConnection()) {
byteList = getZByteLists(key, conn);
}
if (byteList.size() == 0) {
return Collections.emptySet();
}
List<OAuth2AccessToken> accessTokens = new ArrayList<>(byteList.size());
for (byte[] bytes : byteList) {
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
accessTokens.add(accessToken);
}
return Collections.unmodifiableCollection(accessTokens);
}
}

View File

@ -17,19 +17,12 @@
package com.pig4cloud.pig.common.security.component;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.SneakyThrows;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
/**
* @author lengleng
@ -60,32 +53,9 @@ public class PigResourceServerAutoConfiguration {
@Bean
@Primary
@LoadBalanced
public RestTemplate lbRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
// 传递ACCEPT JSON
restTemplate.setInterceptors(Collections.singletonList((request, body, execution) -> {
request.getHeaders().set(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
return execution.execute(request, body);
}));
// 处理400 异常
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
@SneakyThrows
public void handleError(ClientHttpResponse response) {
// 当认证中心返回 400 或者 424 错误码不抛异常交给资源服务自行处理
if (response.getRawStatusCode() == HttpStatus.FAILED_DEPENDENCY.value()
|| response.getRawStatusCode() == HttpStatus.BAD_REQUEST.value()) {
return;
}
// 原有异常处理逻辑
super.handleError(response);
}
});
return restTemplate;
public ResourceServerTokenServices resourceServerTokenServices(TokenStore tokenStore,
UserDetailsService userDetailsService) {
return new PigLocalResourceServerTokenServices(tokenStore, userDetailsService);
}
}

View File

@ -23,11 +23,9 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.web.client.RestTemplate;
/**
* @author lengleng
@ -52,10 +50,10 @@ public class PigResourceServerConfigurerAdapter extends ResourceServerConfigurer
private PermitAllUrlProperties permitAllUrl;
@Autowired
private RestTemplate lbRestTemplate;
private PigBearerTokenExtractor pigBearerTokenExtractor;
@Autowired
private PigBearerTokenExtractor pigBearerTokenExtractor;
private ResourceServerTokenServices resourceServerTokenServices;
/**
* 默认的配置对外暴露
@ -74,14 +72,8 @@ public class PigResourceServerConfigurerAdapter extends ResourceServerConfigurer
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
UserAuthenticationConverter userTokenConverter = new PigUserAuthenticationConverter();
accessTokenConverter.setUserTokenConverter(userTokenConverter);
remoteTokenServices.setRestTemplate(lbRestTemplate);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
resources.authenticationEntryPoint(resourceAuthExceptionEntryPoint).tokenExtractor(pigBearerTokenExtractor)
.accessDeniedHandler(pigAccessDeniedHandler).tokenServices(remoteTokenServices);
.accessDeniedHandler(pigAccessDeniedHandler).tokenServices(resourceServerTokenServices);
}
}

View File

@ -0,0 +1,31 @@
package com.pig4cloud.pig.common.security.component;
import com.pig4cloud.pig.common.core.util.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
/**
* @author lengleng
* @date 2020/9/29
* <p>
* redis token store 自动配置
*/
@Slf4j
@EnableScheduling
@ConditionalOnBean(AuthorizationServerConfigurerAdapter.class)
public class PigTokenStoreAutoCleanSchedule {
/**
* 每小时执行一致避免 redis zset 容量问题
*/
@Scheduled(cron = "@hourly")
public void doMaintenance() {
PigRedisTokenStore tokenStore = SpringContextHolder.getBean(PigRedisTokenStore.class);
long maintenance = tokenStore.doMaintenance();
log.debug("清理Redis ZADD 过期 token 数量: {}", maintenance);
}
}

View File

@ -0,0 +1,21 @@
package com.pig4cloud.pig.common.security.component;
import com.pig4cloud.pig.common.core.constant.CacheConstants;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
/**
* @author lengleng
* @date 2021/10/16
*/
public class PigTokenStoreAutoConfiguration {
@Bean
public TokenStore tokenStore(RedisConnectionFactory redisConnectionFactory) {
PigRedisTokenStore tokenStore = new PigRedisTokenStore(redisConnectionFactory);
tokenStore.setPrefix(CacheConstants.PROJECT_OAUTH_ACCESS);
return tokenStore;
}
}

View File

@ -1,90 +0,0 @@
/*
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.pig4cloud.pig.common.security.component;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.security.service.PigUser;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author lengleng
* @date 2019-03-07
* <p>
* 根据checktoken 的结果转化用户信息
*/
public class PigUserAuthenticationConverter implements UserAuthenticationConverter {
private static final String N_A = "N/A";
/**
* Extract information about the user to be used in an access token (i.e. for resource
* servers).
* @param authentication an authentication representing a user
* @return a map of key values representing the unique information about the user
*/
@Override
public Map<String, ?> convertUserAuthentication(Authentication authentication) {
Map<String, Object> response = new LinkedHashMap<>();
response.put(USERNAME, authentication.getName());
if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
}
return response;
}
/**
* Inverse of {@link #convertUserAuthentication(Authentication)}. Extracts an
* Authentication from a map.
* @param map a map of user information
* @return an Authentication representing the user or null if there is none
*/
@Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
String username = (String) map.get(SecurityConstants.DETAILS_USERNAME);
Integer id = (Integer) map.get(SecurityConstants.DETAILS_USER_ID);
Integer deptId = (Integer) map.get(SecurityConstants.DETAILS_DEPT_ID);
PigUser user = new PigUser(id, deptId, username, N_A, true, true, true, true, authorities);
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
}
return null;
}
private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
Object authorities = map.get(AUTHORITIES);
if (authorities instanceof String) {
return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
}
if (authorities instanceof Collection) {
return AuthorityUtils.commaSeparatedStringToAuthorityList(
StringUtils.collectionToCommaDelimitedString((Collection<?>) authorities));
}
return AuthorityUtils.NO_AUTHORITIES;
}
}

View File

@ -0,0 +1,382 @@
package com.pig4cloud.pig.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 PigCustomTokenServices 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;
}
}

View File

@ -1,4 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pig4cloud.pig.common.security.service.PigUserDetailsServiceImpl,\
com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect
com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect,\
com.pig4cloud.pig.common.security.component.PigTokenStoreAutoConfiguration,\
com.pig4cloud.pig.common.security.component.PigTokenStoreAutoCleanSchedule

View File

@ -24,7 +24,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-swagger</artifactId>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-common</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common-test</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-common</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-gateway</artifactId>

View File

@ -18,7 +18,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-register</artifactId>

View File

@ -40,7 +40,6 @@ public interface ConfigConstants {
*/
String LOG_BASEDIR = "server.tomcat.basedir";
/**
* access_log日志开关
*/

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-upms</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-upms-api</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-upms</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-upms-biz</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-upms</artifactId>

View File

@ -22,7 +22,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-codegen</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-monitor</artifactId>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-sentinel-dashboard</artifactId>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig-visual</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-xxl-job-admin</artifactId>

View File

@ -21,7 +21,7 @@
<parent>
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<version>3.3.5</version>
<version>3.4.0</version>
</parent>
<artifactId>pig-visual</artifactId>

26
pom.xml
View File

@ -22,7 +22,7 @@
<groupId>com.pig4cloud</groupId>
<artifactId>pig</artifactId>
<name>${project.artifactId}</name>
<version>3.3.5</version>
<version>3.4.0</version>
<packaging>pom</packaging>
<url>https://www.pig4cloud.com</url>
@ -34,9 +34,9 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-boot-admin.version>2.5.2</spring-boot-admin.version>
<hutool.version>5.7.13</hutool.version>
<hutool.version>5.7.14</hutool.version>
<dynamic-ds.version>3.4.1</dynamic-ds.version>
<captcha.version>2.2.0</captcha.version>
<captcha.version>2.2.1</captcha.version>
<velocity.version>1.7</velocity.version>
<configuration.version>1.10</configuration.version>
<nacos.version>2.0.3</nacos.version>
@ -49,8 +49,8 @@
<docker.namespace>pig4cloud</docker.namespace>
<docker.username>username</docker.username>
<docker.password>password</docker.password>
<git.commit.plugin>2.2.5</git.commit.plugin>
<spring.checkstyle.plugin>0.0.28</spring.checkstyle.plugin>
<git.commit.plugin>5.0.0</git.commit.plugin>
<spring.checkstyle.plugin>0.0.29</spring.checkstyle.plugin>
</properties>
<!-- 以下依赖 全局所有的模块都会引入 -->
@ -203,9 +203,21 @@
<plugins>
<!--打包jar 与git commit 关联插件-->
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>${git.commit.plugin}</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
</configuration>
</plugin>
<!--代码格式插件默认使用spring 规则-->
<plugin>