✨ Introducing new features. oauth2.0 序列化

✨ Introducing new features. oauth2.0 序列化

✨ Introducing new features. oauth2.0 序列化

✨ Introducing new features. oauth2.0 序列化
This commit is contained in:
冷冷 authored and pig666 committed 2020-10-22 21:27:00 +08:00
1 parent 841cc622da
commit e3e63731d3
21 files changed
+660 -93

No files matched your search

@@ -102,25 +102,28 @@ public class PermitAllUrlResolver implements InitializingBean {
/**
* 过滤 Inner 设置
*
* <p>1. 路径转换: 如果为restful(/xx/{xx}) --> /xx/* ant 表达式</p>
* <p> 2. 构建表达式:允许暴露的接口|允许暴露的方法类型,允许暴露的方法类型 URL|GET,POST,DELETE,PUT</p>
*
* <p>
* 1. 路径转换: 如果为restful(/xx/{xx}) --> /xx/* ant 表达式
* </p>
* <p>
* 2. 构建表达式:允许暴露的接口|允许暴露的方法类型,允许暴露的方法类型 URL|GET,POST,DELETE,PUT
* </p>
* @param url mapping路径
* @param rq 请求犯法
* @param rq 请求犯法
*/
private void filterPath(String url, RequestMapping rq) {
List<String> methodList = Arrays.stream(rq.method()).map(RequestMethod::name).collect(Collectors.toList());
String resultUrl = ReUtil.replaceAll(url, PATTERN, "*");
if (CollUtil.isEmpty(methodList)) {
ignoreUrls.add(resultUrl);
} else {
}
else {
ignoreUrls.add(String.format("%s|%s", resultUrl, CollUtil.join(methodList, StrUtil.COMMA)));
}
}
/**
* 获取对外暴露的URL,注册到 spring security
*
* @param registry spring security context
*/
public void registry(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry) {
@@ -136,7 +139,7 @@ public class PermitAllUrlResolver implements InitializingBean {
// 当配置对外的URL|GET,POST 这种形式,则获取方法列表 并注册到 spring security
if (strings.length == 2) {
for (String method : StrUtil.split(strings[1], StrUtil.COMMA)) {
registry.antMatchers(HttpMethod.valueOf(method), strings[0]);
registry.antMatchers(HttpMethod.valueOf(method), strings[0]).permitAll();
}
continue;
}
@@ -125,14 +125,10 @@ public class PigxRedisTokenStore implements TokenStore {
public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
String key = authenticationKeyGenerator.extractKey(authentication);
byte[] serializedKey = serializeKey(AUTH_TO_ACCESS + key);
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(serializedKey);
}
finally {
conn.close();
}
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
if (accessToken != null) {
OAuth2Authentication storedAuthentication = readAuthentication(accessToken.getValue());
@@ -156,15 +152,10 @@ public class PigxRedisTokenStore implements TokenStore {
@Override
public OAuth2Authentication readAuthentication(String token) {
byte[] bytes;
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
bytes = conn.get(serializeKey(AUTH + token));
}
finally {
conn.close();
}
OAuth2Authentication auth = deserializeAuthentication(bytes);
return auth;
return deserializeAuthentication(bytes);
}
@Override
@@ -173,14 +164,9 @@ public class PigxRedisTokenStore implements TokenStore {
}
public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
OAuth2Authentication auth = deserializeAuthentication(bytes);
return auth;
}
finally {
conn.close();
return deserializeAuthentication(bytes);
}
}
@@ -194,8 +180,7 @@ public class PigxRedisTokenStore implements TokenStore {
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
if (springDataRedis_2_0) {
try {
@@ -236,7 +221,6 @@ public class PigxRedisTokenStore implements TokenStore {
}
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null && refreshToken.getValue() != null) {
byte[] refresh = serialize(token.getRefreshToken().getValue());
byte[] auth = serialize(token.getValue());
byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue());
if (springDataRedis_2_0) {
@@ -262,9 +246,6 @@ public class PigxRedisTokenStore implements TokenStore {
}
conn.closePipeline();
}
finally {
conn.close();
}
}
private static String getApprovalKey(OAuth2Authentication authentication) {
@@ -285,23 +266,17 @@ public class PigxRedisTokenStore implements TokenStore {
@Override
public OAuth2AccessToken readAccessToken(String tokenValue) {
byte[] key = serializeKey(ACCESS + tokenValue);
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(key);
}
finally {
conn.close();
}
OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
return accessToken;
return deserializeAccessToken(bytes);
}
public void removeAccessToken(String tokenValue) {
byte[] accessKey = serializeKey(ACCESS + tokenValue);
byte[] authKey = serializeKey(AUTH + tokenValue);
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
conn.get(accessKey);
conn.get(authKey);
@@ -326,9 +301,6 @@ public class PigxRedisTokenStore implements TokenStore {
conn.closePipeline();
}
}
finally {
conn.close();
}
}
@Override
@@ -336,8 +308,7 @@ public class PigxRedisTokenStore implements TokenStore {
byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue());
byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue());
byte[] serializedRefreshToken = serialize(refreshToken);
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
if (springDataRedis_2_0) {
try {
@@ -363,24 +334,16 @@ public class PigxRedisTokenStore implements TokenStore {
}
conn.closePipeline();
}
finally {
conn.close();
}
}
@Override
public OAuth2RefreshToken readRefreshToken(String tokenValue) {
byte[] key = serializeKey(REFRESH + tokenValue);
byte[] bytes = null;
RedisConnection conn = getConnection();
try {
byte[] bytes;
try (RedisConnection conn = getConnection()) {
bytes = conn.get(key);
}
finally {
conn.close();
}
OAuth2RefreshToken refreshToken = deserializeRefreshToken(bytes);
return refreshToken;
return deserializeRefreshToken(bytes);
}
@Override
@@ -392,17 +355,13 @@ public class PigxRedisTokenStore implements TokenStore {
byte[] refreshKey = serializeKey(REFRESH + tokenValue);
byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + tokenValue);
byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue);
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
conn.del(refreshKey);
conn.del(refreshAuthKey);
conn.del(refresh2AccessKey);
conn.closePipeline();
}
finally {
conn.close();
}
}
@Override
@@ -412,17 +371,13 @@ public class PigxRedisTokenStore implements TokenStore {
private void removeAccessTokenUsingRefreshToken(String refreshToken) {
byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken);
List<Object> results = null;
RedisConnection conn = getConnection();
try {
List<Object> results;
try (RedisConnection conn = getConnection()) {
conn.openPipeline();
conn.get(key);
conn.del(key);
results = conn.closePipeline();
}
finally {
conn.close();
}
byte[] bytes = (byte[]) results.get(0);
String accessToken = deserializeString(bytes);
if (accessToken != null) {
@@ -437,6 +392,7 @@ public class PigxRedisTokenStore implements TokenStore {
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);
@@ -466,8 +422,7 @@ public class PigxRedisTokenStore implements TokenStore {
*/
public long doMaintenance() {
long removed = 0;
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
// client_id_to_acccess maintenance
Cursor<byte[]> clientToAccessKeys = conn.scan(ScanOptions.scanOptions()
.match(keyStrResolver.extract(prefix + CLIENT_ID_TO_ACCESS + "*", StrUtil.COLON)).build());
@@ -486,9 +441,6 @@ public class PigxRedisTokenStore implements TokenStore {
removed += conn.zRemRangeByScore(unameToAccessKey, 0, System.currentTimeMillis());
}
}
finally {
conn.close();
}
return removed;
}
@@ -496,13 +448,9 @@ public class PigxRedisTokenStore implements TokenStore {
public Collection<OAuth2AccessToken> findTokensByClientIdAndUserName(String clientId, String userName) {
byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName));
List<byte[]> byteList;
RedisConnection conn = getConnection();
try {
try (RedisConnection conn = getConnection()) {
byteList = getZByteLists(approvalKey, conn);
}
finally {
conn.close();
}
if (byteList.size() == 0) {
return Collections.emptySet();
}
@@ -517,14 +465,10 @@ public class PigxRedisTokenStore implements TokenStore {
@Override
public Collection<OAuth2AccessToken> findTokensByClientId(String clientId) {
byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId);
List<byte[]> byteList = null;
RedisConnection conn = getConnection();
try {
List<byte[]> byteList;
try (RedisConnection conn = getConnection()) {
byteList = getZByteLists(key, conn);
}
finally {
conn.close();
}
if (byteList.size() == 0) {
return Collections.emptySet();
}
@@ -1,3 +1,4 @@
/*
* Copyright (c) 2018-2025, lengleng All rights reserved.
*
@@ -17,6 +17,8 @@
package com.pig4cloud.pigx.common.security.mobile;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.SneakyThrows;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
@@ -40,7 +42,9 @@ public class MobileAuthenticationToken extends AbstractAuthenticationToken {
setAuthenticated(false);
}
public MobileAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
@JsonCreator
public MobileAuthenticationToken(@JsonProperty("principal") Object principal,
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true);
@@ -0,0 +1,117 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import java.io.IOException;
import java.util.*;
/**
* 反序列化
*
* @author dream.lu
*/
public final class AccessTokenJackson2Deserializer extends StdDeserializer<OAuth2AccessToken> {
public AccessTokenJackson2Deserializer() {
super(OAuth2AccessToken.class);
}
@Override
public Object deserializeWithType(JsonParser p, DeserializationContext ctxt, TypeDeserializer typeDeserializer)
throws IOException {
return myDeserializeWithType(p);
}
private OAuth2AccessToken myDeserializeWithType(JsonParser jp) throws IOException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode jsonNode = mapper.readTree(jp);
String tokenValue = jsonNode.get(OAuth2AccessToken.ACCESS_TOKEN).asText();
String tokenType = jsonNode.get(OAuth2AccessToken.TOKEN_TYPE).asText();
String refreshToken = jsonNode.get(OAuth2AccessToken.REFRESH_TOKEN).asText();
long expiresIn = jsonNode.get(OAuth2AccessToken.EXPIRES_IN).asLong();
DefaultOAuth2AccessToken accessToken = new DefaultOAuth2AccessToken(tokenValue);
accessToken.setTokenType(tokenType);
accessToken.setExpiration(new Date(System.currentTimeMillis() + expiresIn * 1000));
if (refreshToken != null) {
accessToken.setRefreshToken(new DefaultOAuth2RefreshToken(refreshToken));
}
// scope
JsonNode scopeNode = jsonNode.get(OAuth2AccessToken.SCOPE);
accessToken.setScope(parseScope(scopeNode));
// 扩展字段
accessToken.setAdditionalInformation(parseAdditionalInformation(mapper, jsonNode));
return accessToken;
}
@Override
public OAuth2AccessToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
return myDeserializeWithType(jp);
}
/**
* 解析 scope
* @param scopeNode scope json
* @return String Set
*/
private static Set<String> parseScope(JsonNode scopeNode) {
// 空处理
if (scopeNode == null || scopeNode.isNull()) {
return Collections.emptySet();
}
// 字符串 x xx xxx 格式
if (scopeNode.isTextual()) {
return OAuth2Utils.parseParameterList(scopeNode.asText());
}
// 集合模型
Set<String> scope = new TreeSet<>();
Iterator<JsonNode> elements = scopeNode.elements();
while (elements.hasNext()) {
JsonNode next = elements.next();
scope.add(next.asText());
}
return scope;
}
/**
* 解析扩展字段
* @param mapper jackson 工具
* @param jsonNode json String
* @return map
* @see com.pig4cloud.pigx.common.security.component.PigxTokenEnhancer
*/
private Map<String, Object> parseAdditionalInformation(ObjectMapper mapper, JsonNode jsonNode) {
LinkedHashMap<String, Object> additionalInformation = new LinkedHashMap<>();
// 处理 user_info
JsonNode userNode = jsonNode.get(SecurityConstants.DETAILS_USER);
User user = mapper.convertValue(userNode, User.class);
int deptId = userNode.get(SecurityConstants.DETAILS_DEPT_ID).asInt();
int tenantId = userNode.get(SecurityConstants.DETAILS_TENANT_ID).asInt();
int userId = userNode.get(SecurityConstants.DETAILS_USER_ID).asInt();
String phone = userNode.get(SecurityConstants.DETAILS_PHONE).asText();
String avatar = userNode.get(SecurityConstants.DETAILS_AVATAR).asText();
PigxUser pigxUser = new PigxUser(userId, deptId, phone, avatar, tenantId, user.getUsername(), "N/A",
user.isEnabled(), user.isAccountNonExpired(), user.isCredentialsNonExpired(), user.isAccountNonLocked(),
user.getAuthorities());
additionalInformation.put(SecurityConstants.DETAILS_USER, pigxUser);
// 处理 license 等扩展字段 【去掉OAuth2AccessToken 字段避免重复】
additionalInformation.put(SecurityConstants.DETAILS_LICENSE,
jsonNode.get(SecurityConstants.DETAILS_LICENSE).asText());
additionalInformation.put(SecurityConstants.ACTIVE, jsonNode.get(SecurityConstants.ACTIVE).asBoolean());
return additionalInformation;
}
}
@@ -0,0 +1,75 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.util.Assert;
import java.io.IOException;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* 序列化
*
* @author dream.lu
*/
public final class AccessTokenJackson2Serializer extends StdSerializer<OAuth2AccessToken> {
public AccessTokenJackson2Serializer() {
super(OAuth2AccessToken.class);
}
@Override
public void serializeWithType(OAuth2AccessToken value, JsonGenerator gen, SerializerProvider serializers,
TypeSerializer typeSer) throws IOException {
mySerializeWithType(value, gen, serializers, typeSer);
}
private void mySerializeWithType(OAuth2AccessToken token, JsonGenerator gen, SerializerProvider serializers,
TypeSerializer typeSer) throws IOException {
gen.writeStartObject();
if (typeSer != null) {
gen.writeStringField(typeSer.getPropertyName(), token.getClass().getName());
}
Map<String, Object> tokenMap = new LinkedHashMap<>();
tokenMap.put(OAuth2AccessToken.ACCESS_TOKEN, token.getValue());
tokenMap.put(OAuth2AccessToken.TOKEN_TYPE, token.getTokenType());
OAuth2RefreshToken refreshToken = token.getRefreshToken();
if (refreshToken != null) {
tokenMap.put(OAuth2AccessToken.REFRESH_TOKEN, refreshToken.getValue());
}
Date expiration = token.getExpiration();
if (expiration != null) {
long now = System.currentTimeMillis();
tokenMap.put(OAuth2AccessToken.EXPIRES_IN, (expiration.getTime() - now) / 1000);
}
Set<String> scope = token.getScope();
if (scope != null && !scope.isEmpty()) {
StringBuilder scopes = new StringBuilder();
for (String s : scope) {
Assert.hasLength(s, "Scopes cannot be null or empty. Got " + scope);
scopes.append(s);
scopes.append(" ");
}
tokenMap.put(OAuth2AccessToken.SCOPE, scopes.substring(0, scopes.length() - 1));
}
Map<String, Object> additionalInformation = token.getAdditionalInformation();
tokenMap.putAll(additionalInformation);
for (String key : tokenMap.keySet()) {
gen.writeObjectField(key, tokenMap.get(key));
}
gen.writeEndObject();
}
@Override
public void serialize(OAuth2AccessToken token, JsonGenerator gen, SerializerProvider provider) throws IOException {
mySerializeWithType(token, gen, provider, null);
}
}
@@ -0,0 +1,17 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
/**
* AccessTokenMixIn
*
* @author dream.lu
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
@JsonSerialize(using = AccessTokenJackson2Serializer.class)
@JsonDeserialize(using = AccessTokenJackson2Deserializer.class)
public class AccessTokenMixIn {
}
@@ -0,0 +1,72 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.pig4cloud.pigx.common.security.mobile.MobileAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.jackson2.CoreJackson2Module;
import org.springframework.security.jackson2.SimpleGrantedAuthorityMixin;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.store.redis.BaseRedisTokenStoreSerializationStrategy;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* jackson 序列化
*
* @author dream.lu
*/
public class JacksonRedisTokenStoreSerializationStrategy extends BaseRedisTokenStoreSerializationStrategy {
private final ObjectMapper mapper;
public JacksonRedisTokenStoreSerializationStrategy() {
this.mapper = new ObjectMapper();
this.mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.PROPERTY);
this.mapper.addMixIn(OAuth2AccessToken.class, AccessTokenMixIn.class);
this.mapper.addMixIn(OAuth2Authentication.class, OAuth2AuthenticationMixin.class);
this.mapper.addMixIn(PreAuthenticatedAuthenticationToken.class, PreAuthenticatedAuthenticationTokenMixin.class);
this.mapper.addMixIn(MobileAuthenticationToken.class, MobileAuthenticationTokenMixin.class);
this.mapper.addMixIn(SimpleGrantedAuthority.class, SimpleGrantedAuthorityMixin.class);
this.mapper.registerModule(new CoreJackson2Module());
this.mapper.addMixIn(DefaultExpiringOAuth2RefreshToken.class, OAuth2RefreshTokenMixin.class);
}
@Override
protected <T> T deserializeInternal(byte[] bytes, Class<T> clazz) {
try {
return mapper.readValue(bytes, clazz);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected String deserializeStringInternal(byte[] bytes) {
return new String(bytes, StandardCharsets.UTF_8);
}
@Override
protected byte[] serializeInternal(Object object) {
try {
return mapper.writeValueAsBytes(object);
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@Override
protected byte[] serializeInternal(String data) {
return data.getBytes(StandardCharsets.UTF_8);
}
}
@@ -0,0 +1,54 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.pig4cloud.pigx.common.security.mobile.MobileAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author lengleng
* @date 2020/10/21
* <p>
* MobileAuthenticationToken 反序列化处理
*/
public class MobileAuthenticationTokenDeserializer extends StdDeserializer<MobileAuthenticationToken> {
protected MobileAuthenticationTokenDeserializer() {
super(MobileAuthenticationToken.class);
}
@Override
public MobileAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode jsonNode = mapper.readTree(jp);
Set<GrantedAuthority> authorities = getAuthorities(mapper, jsonNode);
User principal = mapper.convertValue(jsonNode.get("principal"), User.class);
return new MobileAuthenticationToken(principal, authorities);
}
private Set<GrantedAuthority> getAuthorities(ObjectMapper mapper, JsonNode parentNode) {
JsonNode authoritiesNode = parentNode.get("authorities");
Set<GrantedAuthority> authorities = new HashSet<>(authoritiesNode.size());
for (Iterator<JsonNode> authoritiesIt = authoritiesNode.get(1).elements(); authoritiesIt.hasNext();) {
JsonNode authorityNode = authoritiesIt.next();
SimpleGrantedAuthority authority = mapper.convertValue(authorityNode, SimpleGrantedAuthority.class);
authorities.add(authority);
}
return authorities;
}
}
@@ -0,0 +1,17 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
/**
* @author lengleng
* @date 2020/10/21
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonDeserialize(using = MobileAuthenticationTokenDeserializer.class)
public class MobileAuthenticationTokenMixin {
}
@@ -0,0 +1,93 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* OAuth2Authentication 反序列化
*
* @author dream.lu
*/
public class OAuth2AuthenticationDeserializer extends StdDeserializer<OAuth2Authentication> {
protected OAuth2AuthenticationDeserializer() {
super(OAuth2Authentication.class);
}
@Override
public OAuth2Authentication deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode jsonNode = mapper.readTree(jp);
Set<GrantedAuthority> authorities = getAuthorities(mapper, jsonNode);
JsonNode requestNode = jsonNode.get("storedRequest");
Map<String, String> requestParameters = getMap(mapper, requestNode.get("requestParameters"));
String clientId = requestNode.get("clientId").asText();
boolean approved = requestNode.get("approved").asBoolean();
Set<String> scope = getSet(mapper, requestNode.get("scope"));
Set<String> resourceIds = getSet(mapper, requestNode.get("resourceIds"));
String redirectUri = requestNode.get("redirectUri").asText(null);
Set<String> responseTypes = getSet(mapper, requestNode.get("responseTypes"));
Map<String, Serializable> extensionProperties = getMap(mapper, jsonNode.get("extensions"), Serializable.class);
// 构造 OAuth2Request
OAuth2Request request = new OAuth2Request(requestParameters, clientId, authorities, approved, scope,
resourceIds, redirectUri, responseTypes, extensionProperties);
JsonNode userAuthenticationNode = jsonNode.get("userAuthentication");
// UsernamePasswordAuthenticationToken 反序列化
Authentication authentication = null;
if (userAuthenticationNode != null && !userAuthenticationNode.isMissingNode()) {
authentication = (Authentication) mapper.convertValue(userAuthenticationNode, Object.class);
}
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(request, authentication);
oAuth2Authentication.setAuthenticated(true);
return oAuth2Authentication;
}
private static Set<String> getSet(ObjectMapper mapper, JsonNode jsonNode) {
return getSet(mapper, jsonNode, String.class);
}
private static <T> Set<T> getSet(ObjectMapper mapper, JsonNode jsonNode, Class<T> valueClass) {
CollectionType collectionType = mapper.getTypeFactory().constructCollectionType(Set.class, valueClass);
return mapper.convertValue(jsonNode, collectionType);
}
private static Map<String, String> getMap(ObjectMapper mapper, JsonNode jsonNode) {
return getMap(mapper, jsonNode, String.class);
}
private static <T> Map<String, T> getMap(ObjectMapper mapper, JsonNode jsonNode, Class<T> valueClass) {
MapType mapType = mapper.getTypeFactory().constructMapType(Map.class, String.class, valueClass);
return mapper.convertValue(jsonNode, mapType);
}
private Set<GrantedAuthority> getAuthorities(ObjectMapper mapper, JsonNode parentNode) {
JsonNode authoritiesNode = parentNode.get("authorities");
Set<GrantedAuthority> authorities = new HashSet<>(authoritiesNode.size());
for (Iterator<JsonNode> authoritiesIt = authoritiesNode.get(1).elements(); authoritiesIt.hasNext();) {
JsonNode authorityNode = authoritiesIt.next();
SimpleGrantedAuthority authority = mapper.convertValue(authorityNode, SimpleGrantedAuthority.class);
authorities.add(authority);
}
return authorities;
}
}
@@ -0,0 +1,13 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonDeserialize(using = OAuth2AuthenticationDeserializer.class)
public class OAuth2AuthenticationMixin {
}
@@ -0,0 +1,37 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import java.io.IOException;
import java.util.Date;
/**
* DefaultExpiringOAuth2RefreshToken 反序列化
*
* @author dream.lu
*/
public class OAuth2RefreshTokenDeserializer extends StdDeserializer<DefaultExpiringOAuth2RefreshToken> {
protected OAuth2RefreshTokenDeserializer() {
super(DefaultExpiringOAuth2RefreshToken.class);
}
@Override
public DefaultExpiringOAuth2RefreshToken deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// {"@class":"org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken","value":"a94d2f04-d9be-49f9-9d9c-2bc7b34c8618","expiration":["java.util.Date",1605237988760]}
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode jsonNode = mapper.readTree(jp);
String value = jsonNode.get("value").asText();
JsonNode expirationNode = jsonNode.get("expiration");
Date date = mapper.convertValue(expirationNode, Date.class);
return new DefaultExpiringOAuth2RefreshToken(value, date);
}
}
@@ -0,0 +1,10 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
@JsonDeserialize(using = OAuth2RefreshTokenDeserializer.class)
public class OAuth2RefreshTokenMixin {
}
@@ -1,8 +1,9 @@
package com.pig4cloud.pigx.common.security.component;
package com.pig4cloud.pigx.common.security.serializer;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import com.pig4cloud.pigx.common.security.component.PigxRedisTokenStore;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -28,8 +29,8 @@ public class PigxTokenStoreAutoConfiguration {
@Bean
public TokenStore tokenStore() {
PigxRedisTokenStore tokenStore = new PigxRedisTokenStore(connectionFactory, resolver);
tokenStore.setSerializationStrategy(new JacksonRedisTokenStoreSerializationStrategy());
tokenStore.setPrefix(SecurityConstants.PIGX_PREFIX + SecurityConstants.OAUTH_PREFIX);
tokenStore.setAuthenticationKeyGenerator(new DefaultAuthenticationKeyGenerator() {
@Override
public String extractKey(OAuth2Authentication authentication) {
@@ -0,0 +1,57 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
* @author lengleng
* @date 2020/10/21
* <p>
* PreAuthenticatedAuthenticationToken 反序列化处理
*/
public class PreAuthenticatedAuthenticationTokenDeserializer
extends StdDeserializer<PreAuthenticatedAuthenticationToken> {
protected PreAuthenticatedAuthenticationTokenDeserializer() {
super(PreAuthenticatedAuthenticationToken.class);
}
@Override
public PreAuthenticatedAuthenticationToken deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode jsonNode = mapper.readTree(jp);
Set<GrantedAuthority> authorities = getAuthorities(mapper, jsonNode);
User principal = mapper.convertValue(jsonNode.get("principal"), User.class);
return new PreAuthenticatedAuthenticationToken(principal, "N/A", authorities);
}
private Set<GrantedAuthority> getAuthorities(ObjectMapper mapper, JsonNode parentNode) {
JsonNode authoritiesNode = parentNode.get("authorities");
Set<GrantedAuthority> authorities = new HashSet<>(authoritiesNode.size());
for (Iterator<JsonNode> authoritiesIt = authoritiesNode.get(1).elements(); authoritiesIt.hasNext();) {
JsonNode authorityNode = authoritiesIt.next();
SimpleGrantedAuthority authority = mapper.convertValue(authorityNode, SimpleGrantedAuthority.class);
authorities.add(authority);
}
return authorities;
}
}
@@ -0,0 +1,17 @@
package com.pig4cloud.pigx.common.security.serializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
/**
* @author lengleng
* @date 2020/10/21
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "@class")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonDeserialize(using = PreAuthenticatedAuthenticationTokenDeserializer.class)
public class PreAuthenticatedAuthenticationTokenMixin {
}
@@ -17,6 +17,8 @@
package com.pig4cloud.pigx.common.security.service;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.GrantedAuthority;
@@ -83,9 +85,15 @@ public class PigxUser extends User {
* @throws IllegalArgumentException if a <code>null</code> value was passed either as
* a parameter or as an element in the <code>GrantedAuthority</code> collection
*/
public PigxUser(Integer id, Integer deptId, String phone, String avatar, Integer tenantId, String username,
String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
@JsonCreator
public PigxUser(@JsonProperty("id") Integer id, @JsonProperty("deptId") Integer deptId,
@JsonProperty("phone") String phone, @JsonProperty("avatar") String avatar,
@JsonProperty("tenantId") Integer tenantId, @JsonProperty("username") String username,
@JsonProperty("password") String password, @JsonProperty("enabled") boolean enabled,
@JsonProperty("accountNonExpired") boolean accountNonExpired,
@JsonProperty("credentialsNonExpired") boolean credentialsNonExpired,
@JsonProperty("accountNonLocked") boolean accountNonLocked,
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
this.id = id;
this.deptId = deptId;
@@ -1,7 +1,7 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pig4cloud.pigx.common.security.component.PigxTokenEnhancer,\
com.pig4cloud.pigx.common.security.component.PigxSecurityInnerAspect,\
com.pig4cloud.pigx.common.security.component.PigxTokenStoreAutoConfiguration,\
com.pig4cloud.pigx.common.security.serializer.PigxTokenStoreAutoConfiguration,\
com.pig4cloud.pigx.common.security.listener.AuthenticationFailureEventListener,\
com.pig4cloud.pigx.common.security.listener.AuthenticationSuccessEventListener
@@ -0,0 +1,27 @@
package com.pig4cloud.pigx.admin.controller;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import org.springframework.web.bind.annotation.*;
/**
* @author lengleng
* @date 2020/10/13
*/
@Inner
@RequestMapping(value = "/demo", method = RequestMethod.POST)
@RestController
public class DemoController {
@Inner(false)
@GetMapping("/test")
public String demo() {
return "ssucess";
}
@Inner(false)
@GetMapping("/test/{id}")
public String demo2(@PathVariable String id) {
return "ssucess";
}
}