diff --git a/hsweb-authorization/hsweb-authorization-api/pom.xml b/hsweb-authorization/hsweb-authorization-api/pom.xml
index 10c12a3ec..4e6f2c446 100644
--- a/hsweb-authorization/hsweb-authorization-api/pom.xml
+++ b/hsweb-authorization/hsweb-authorization-api/pom.xml
@@ -5,7 +5,7 @@
hsweb-authorization
org.hswebframework.web
- 4.0.7
+ 4.0.8-SNAPSHOT
4.0.0
@@ -43,7 +43,7 @@
- io.swagger
+ io.swagger.core.v3
swagger-annotations
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java
index ebb5b5b3d..3719c58a1 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Authentication.java
@@ -22,6 +22,8 @@ import reactor.core.publisher.Mono;
import java.io.Serializable;
import java.util.*;
+import java.util.function.BiPredicate;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
@@ -207,4 +209,13 @@ public interface Authentication extends Serializable {
*/
Authentication merge(Authentication source);
+ /**
+ * copy为新的权限信息
+ *
+ * @param permissionFilter 权限过滤
+ * @param dimension 维度过滤
+ * @return 新的权限信息
+ */
+ Authentication copy(BiPredicate permissionFilter,
+ Predicate dimension);
}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java
index 803f38e3c..d8d9fb076 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/Permission.java
@@ -153,7 +153,7 @@ public interface Permission extends Serializable {
* @see FieldFilterDataAccessConfig#getFields()
*/
default Optional findFieldFilter(String action) {
- return findDataAccess(conf -> FieldFilterDataAccessConfig.class.isInstance(conf) && conf.getAction().equals(action));
+ return findDataAccess(conf -> conf instanceof FieldFilterDataAccessConfig && conf.getAction().equals(action));
}
/**
@@ -164,7 +164,7 @@ public interface Permission extends Serializable {
*/
default Set findDenyFields(String action) {
return findFieldFilter(action)
- .filter(conf -> DENY_FIELDS.equals(conf.getType()))
+ .filter(conf -> DENY_FIELDS.equals(conf.getType().getId()))
.map(FieldFilterDataAccessConfig::getFields)
.orElseGet(Collections::emptySet);
}
@@ -210,6 +210,8 @@ public interface Permission extends Serializable {
Permission copy();
+ Permission copy(Predicate actionFilter,Predicate dataAccessFilter);
+
/**
* 数据权限查找判断逻辑接口
*
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java
index befc744d9..229ef8c50 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/DefaultAuthorizationAutoConfiguration.java
@@ -6,10 +6,7 @@ import org.hswebframework.web.authorization.builder.DataAccessConfigBuilderFacto
import org.hswebframework.web.authorization.simple.builder.DataAccessConfigConverter;
import org.hswebframework.web.authorization.simple.builder.SimpleAuthenticationBuilderFactory;
import org.hswebframework.web.authorization.simple.builder.SimpleDataAccessConfigBuilderFactory;
-import org.hswebframework.web.authorization.token.DefaultUserTokenManager;
-import org.hswebframework.web.authorization.token.UserTokenAuthenticationSupplier;
-import org.hswebframework.web.authorization.token.UserTokenReactiveAuthenticationSupplier;
-import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.authorization.token.*;
import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
import org.hswebframework.web.authorization.twofactor.defaults.DefaultTwoFactorValidatorManager;
import org.hswebframework.web.convert.CustomMessageConverter;
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java
index c9d0318a7..356ec9d4e 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java
@@ -23,7 +23,9 @@ import org.hswebframework.web.authorization.*;
import java.io.Serializable;
import java.util.*;
+import java.util.function.BiPredicate;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
@Getter
@@ -40,9 +42,10 @@ public class SimpleAuthentication implements Authentication {
private Map attributes = new HashMap<>();
- public static Authentication of(){
+ public static Authentication of() {
return new SimpleAuthentication();
}
+
@Override
@SuppressWarnings("unchecked")
public Optional getAttribute(String name) {
@@ -77,4 +80,19 @@ public class SimpleAuthentication implements Authentication {
}
return this;
}
+
+ @Override
+ public Authentication copy(BiPredicate permissionFilter,
+ Predicate dimension) {
+ SimpleAuthentication authentication = new SimpleAuthentication();
+ authentication.setUser(user);
+ authentication.setDimensions(dimensions.stream().filter(dimension).collect(Collectors.toList()));
+ authentication.setPermissions(permissions
+ .stream()
+ .map(permission -> permission.copy(action -> permissionFilter.test(permission, action), conf -> true))
+ .filter(per -> !per.getActions().isEmpty())
+ .collect(Collectors.toList())
+ );
+ return authentication;
+ }
}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java
index 098efb7d5..c2d46908c 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimplePermission.java
@@ -5,6 +5,8 @@ import org.hswebframework.web.authorization.Permission;
import org.hswebframework.web.authorization.access.DataAccessConfig;
import java.util.*;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* @author zhouhao
@@ -42,16 +44,22 @@ public class SimplePermission implements Permission {
return dataAccesses;
}
- public Permission copy() {
+ @Override
+ public Permission copy(Predicate actionFilter,
+ Predicate dataAccessFilter) {
SimplePermission permission = new SimplePermission();
permission.setId(id);
permission.setName(name);
- permission.setActions(new HashSet<>(getActions()));
- permission.setDataAccesses(new HashSet<>(getDataAccesses()));
+ permission.setActions(getActions().stream().filter(actionFilter).collect(Collectors.toSet()));
+ permission.setDataAccesses(getDataAccesses().stream().filter(dataAccessFilter).collect(Collectors.toSet()));
if (options != null) {
permission.setOptions(new HashMap<>(options));
}
return permission;
}
+
+ public Permission copy() {
+ return copy(action -> true, conf -> true);
+ }
}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ParsedToken.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ParsedToken.java
index 8bb30a009..e058775da 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ParsedToken.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ParsedToken.java
@@ -15,4 +15,8 @@ public interface ParsedToken {
* @return 令牌类型
*/
String getType();
+
+ static ParsedToken of(String type, String token) {
+ return SimpleParsedToken.of(type, token);
+ }
}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java
new file mode 100644
index 000000000..bd7b59516
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/ReactiveTokenAuthenticationSupplier.java
@@ -0,0 +1,32 @@
+package org.hswebframework.web.authorization.token;
+
+import lombok.AllArgsConstructor;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier;
+import org.hswebframework.web.context.ContextKey;
+import org.hswebframework.web.context.ContextUtils;
+import org.hswebframework.web.logger.ReactiveLogger;
+import reactor.core.publisher.Mono;
+
+@AllArgsConstructor
+public class ReactiveTokenAuthenticationSupplier implements ReactiveAuthenticationSupplier {
+
+ private final TokenAuthenticationManager tokenManager;
+
+ @Override
+ public Mono get(String userId) {
+ return Mono.empty();
+ }
+
+ @Override
+ public Mono get() {
+ return ContextUtils.reactiveContext()
+ .flatMap(context ->
+ context.get(ContextKey.of(ParsedToken.class))
+ .map(t -> tokenManager.getByToken(t.getToken()))
+ .orElseGet(Mono::empty))
+ .flatMap(auth -> ReactiveLogger.mdc("userId", auth.getUser().getId())
+ .then(ReactiveLogger.mdc("username", auth.getUser().getName()))
+ .thenReturn(auth));
+ }
+}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleParsedToken.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleParsedToken.java
new file mode 100644
index 000000000..cedcac0cd
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/SimpleParsedToken.java
@@ -0,0 +1,17 @@
+package org.hswebframework.web.authorization.token;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+@AllArgsConstructor(staticName = "of")
+public class SimpleParsedToken implements ParsedToken{
+
+ private String type;
+
+ private String token;
+
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/TokenAuthenticationManager.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/TokenAuthenticationManager.java
new file mode 100644
index 000000000..c806a9353
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/TokenAuthenticationManager.java
@@ -0,0 +1,40 @@
+package org.hswebframework.web.authorization.token;
+
+import org.hswebframework.web.authorization.Authentication;
+import reactor.core.publisher.Mono;
+
+import java.time.Duration;
+
+/**
+ * token 权限管理器,根据token来进行权限关联.
+ *
+ * @author zhouhao
+ * @since 4.0.7
+ */
+public interface TokenAuthenticationManager {
+
+ /**
+ * 根据token获取认证信息
+ *
+ * @param token token
+ * @return 认证信息
+ */
+ Mono getByToken(String token);
+
+ /**
+ * 设置token认证信息
+ *
+ * @param token token
+ * @param auth 认证信息
+ * @param ttl 有效期
+ * @return void
+ */
+ Mono putAuthentication(String token, Authentication auth, Duration ttl);
+
+ /**
+ * 删除token
+ * @param token token
+ * @return void
+ */
+ Mono removeToken(String token);
+}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java
index 2b400cf46..f43423ad1 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/UserTokenReactiveAuthenticationSupplier.java
@@ -19,13 +19,14 @@ import java.util.Map;
*/
public class UserTokenReactiveAuthenticationSupplier implements ReactiveAuthenticationSupplier {
- private ReactiveAuthenticationManager defaultAuthenticationManager;
+ private final ReactiveAuthenticationManager defaultAuthenticationManager;
- private UserTokenManager userTokenManager;
+ private final UserTokenManager userTokenManager;
- private Map thirdPartAuthenticationManager = new HashMap<>();
+ private final Map thirdPartAuthenticationManager = new HashMap<>();
- public UserTokenReactiveAuthenticationSupplier(UserTokenManager userTokenManager, ReactiveAuthenticationManager defaultAuthenticationManager) {
+ public UserTokenReactiveAuthenticationSupplier(UserTokenManager userTokenManager,
+ ReactiveAuthenticationManager defaultAuthenticationManager) {
this.defaultAuthenticationManager = defaultAuthenticationManager;
this.userTokenManager = userTokenManager;
}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java
index 2606c4b9b..b78184dff 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/event/UserTokenChangedEvent.java
@@ -5,7 +5,7 @@ import org.hswebframework.web.authorization.token.UserToken;
import org.springframework.context.ApplicationEvent;
public class UserTokenChangedEvent extends ApplicationEvent implements AuthorizationEvent {
- private UserToken before, after;
+ private final UserToken before, after;
public UserTokenChangedEvent(UserToken before, UserToken after) {
super(after);
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisTokenAuthenticationManager.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisTokenAuthenticationManager.java
new file mode 100644
index 000000000..31874ea98
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisTokenAuthenticationManager.java
@@ -0,0 +1,61 @@
+package org.hswebframework.web.authorization.token.redis;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.token.TokenAuthenticationManager;
+import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
+import org.springframework.data.redis.core.ReactiveRedisOperations;
+import org.springframework.data.redis.core.ReactiveRedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import reactor.core.publisher.Mono;
+
+import java.time.Duration;
+
+public class RedisTokenAuthenticationManager implements TokenAuthenticationManager {
+
+ private final ReactiveRedisOperations operations;
+
+ @SuppressWarnings("all")
+ public RedisTokenAuthenticationManager(ReactiveRedisConnectionFactory connectionFactory) {
+ this(new ReactiveRedisTemplate<>(
+ connectionFactory, RedisSerializationContext.newSerializationContext()
+ .key(RedisSerializer.string())
+ .value((RedisSerializer) RedisSerializer.java())
+ .hashKey(RedisSerializer.string())
+ .hashValue(RedisSerializer.java())
+ .build()
+ ));
+ }
+
+ public RedisTokenAuthenticationManager(ReactiveRedisOperations operations) {
+ this.operations = operations;
+ }
+
+ @Override
+ public Mono getByToken(String token) {
+ return operations
+ .opsForValue()
+ .get("token-auth:" + token);
+ }
+
+ @Override
+ public Mono removeToken(String token) {
+ return operations
+ .delete(token)
+ .then();
+ }
+
+ @Override
+ public Mono putAuthentication(String token, Authentication auth, Duration ttl) {
+ return ttl.isNegative()
+ ? operations
+ .opsForValue()
+ .set("token-auth:" + token, auth)
+ .then()
+ : operations
+ .opsForValue()
+ .set("token-auth:" + token, auth, ttl)
+ .then()
+ ;
+ }
+}
diff --git a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManager.java b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManager.java
index ab7327114..06e2916e2 100644
--- a/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManager.java
+++ b/hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/token/redis/RedisUserTokenManager.java
@@ -2,21 +2,24 @@ package org.hswebframework.web.authorization.token.redis;
import lombok.Getter;
import lombok.Setter;
-import org.apache.commons.collections.CollectionUtils;
import org.hswebframework.web.authorization.exception.AccessDenyException;
import org.hswebframework.web.authorization.token.AllopatricLoginMode;
import org.hswebframework.web.authorization.token.TokenState;
import org.hswebframework.web.authorization.token.UserToken;
import org.hswebframework.web.authorization.token.UserTokenManager;
-import org.springframework.data.redis.core.ReactiveHashOperations;
-import org.springframework.data.redis.core.ReactiveRedisOperations;
-import org.springframework.data.redis.core.ReactiveSetOperations;
-import org.springframework.data.redis.core.ScanOptions;
+import org.hswebframework.web.authorization.token.event.UserTokenChangedEvent;
+import org.hswebframework.web.authorization.token.event.UserTokenCreatedEvent;
+import org.hswebframework.web.authorization.token.event.UserTokenRemovedEvent;
+import org.hswebframework.web.bean.FastBeanCopier;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.RedisSerializer;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
@@ -35,6 +38,18 @@ public class RedisUserTokenManager implements UserTokenManager {
this.userTokenMapping = operations.opsForSet();
}
+ @SuppressWarnings("all")
+ public RedisUserTokenManager(ReactiveRedisConnectionFactory connectionFactory) {
+ this(new ReactiveRedisTemplate<>(connectionFactory,
+ RedisSerializationContext.newSerializationContext()
+ .key((RedisSerializer) RedisSerializer.string())
+ .value(RedisSerializer.java())
+ .hashKey(RedisSerializer.string())
+ .hashValue(RedisSerializer.java())
+ .build()
+ ));
+ }
+
@Getter
@Setter
private Map allopatricLoginModes = new HashMap<>();
@@ -44,6 +59,9 @@ public class RedisUserTokenManager implements UserTokenManager {
//异地登录模式,默认允许异地登录
private AllopatricLoginMode allopatricLoginMode = AllopatricLoginMode.allow;
+ @Setter
+ private ApplicationEventPublisher eventPublisher;
+
private String getTokenRedisKey(String key) {
return "user-token:".concat(key);
}
@@ -114,20 +132,24 @@ public class RedisUserTokenManager implements UserTokenManager {
public Mono signOutByUserId(String userId) {
String key = getUserRedisKey(userId);
return getByUserId(key)
- .map(UserToken::getToken)
- .map(this::getTokenRedisKey)
- .concatWithValues(key)
- .as(operations::delete)
+ .flatMap(userToken -> operations
+ .delete(getTokenRedisKey(userToken.getToken()))
+ .then(onTokenRemoved(userToken)))
+ .then(operations.delete(key))
.then();
}
@Override
public Mono signOutByToken(String token) {
//delete token
- // srem user token
+ //srem user token
return getByToken(token)
- .flatMap(t -> operations.delete(getTokenRedisKey(t.getToken()))
- .then(userTokenMapping.remove(getUserRedisKey(t.getToken()),token))).then();
+ .flatMap(t -> operations
+ .delete(getTokenRedisKey(t.getToken()))
+ .then(userTokenMapping.remove(getUserRedisKey(t.getToken()), token))
+ .then(onTokenRemoved(t))
+ )
+ .then();
}
@Override
@@ -140,60 +162,68 @@ public class RedisUserTokenManager implements UserTokenManager {
@Override
public Mono changeTokenState(String token, TokenState state) {
- return userTokenStore
- .put(getTokenRedisKey(token), "state", state.getValue())
- .then();
+
+ return getByToken(token)
+ .flatMap(old -> {
+ SimpleUserToken newToken = FastBeanCopier.copy(old, new SimpleUserToken());
+ newToken.setState(state);
+ return userTokenStore
+ .put(getTokenRedisKey(token), "state", state.getValue())
+ .then(onTokenChanged(old, newToken));
+ });
}
@Override
public Mono signIn(String token, String type, String userId, long maxInactiveInterval) {
- return Mono.defer(() -> {
- Mono doSign = Mono.defer(() -> {
- Map map = new HashMap<>();
- map.put("token", token);
- map.put("type", type);
- map.put("userId", userId);
- map.put("maxInactiveInterval", maxInactiveInterval);
- map.put("state", TokenState.normal.getValue());
- map.put("signInTime", System.currentTimeMillis());
- map.put("lastRequestTime", System.currentTimeMillis());
+ return Mono
+ .defer(() -> {
+ Mono doSign = Mono.defer(() -> {
+ Map map = new HashMap<>();
+ map.put("token", token);
+ map.put("type", type);
+ map.put("userId", userId);
+ map.put("maxInactiveInterval", maxInactiveInterval);
+ map.put("state", TokenState.normal.getValue());
+ map.put("signInTime", System.currentTimeMillis());
+ map.put("lastRequestTime", System.currentTimeMillis());
- String key = getTokenRedisKey(token);
- return userTokenStore
- .putAll(key, map)
- .then(Mono.defer(() -> {
- if (maxInactiveInterval > 0) {
- return operations.expire(key, Duration.ofMillis(maxInactiveInterval));
- }
- return Mono.empty();
- }))
- .then(userTokenMapping.add(getUserRedisKey(userId), token))
- .thenReturn(SimpleUserToken.of(map));
- });
+ String key = getTokenRedisKey(token);
+ return userTokenStore
+ .putAll(key, map)
+ .then(Mono.defer(() -> {
+ if (maxInactiveInterval > 0) {
+ return operations.expire(key, Duration.ofMillis(maxInactiveInterval));
+ }
+ return Mono.empty();
+ }))
+ .then(userTokenMapping.add(getUserRedisKey(userId), token))
+ .thenReturn(SimpleUserToken.of(map));
+ });
- AllopatricLoginMode mode = allopatricLoginModes.getOrDefault(type, allopatricLoginMode);
- if (mode == AllopatricLoginMode.deny) {
- return userIsLoggedIn(userId)
- .flatMap(r -> {
- if (r) {
- return Mono.error(new AccessDenyException("已在其他地方登录", TokenState.deny.getValue(), null));
- }
- return doSign;
- });
+ AllopatricLoginMode mode = allopatricLoginModes.getOrDefault(type, allopatricLoginMode);
+ if (mode == AllopatricLoginMode.deny) {
+ return userIsLoggedIn(userId)
+ .flatMap(r -> {
+ if (r) {
+ return Mono.error(new AccessDenyException("已在其他地方登录", TokenState.deny.getValue(), null));
+ }
+ return doSign;
+ });
- } else if (mode == AllopatricLoginMode.offlineOther) {
- return getByUserId(userId)
- .flatMap(userToken -> {
- if (type.equals(userToken.getType())) {
- return this.changeTokenState(userToken.getToken(), TokenState.offline);
- }
- return Mono.empty();
- })
- .then(doSign);
- }
+ } else if (mode == AllopatricLoginMode.offlineOther) {
+ return getByUserId(userId)
+ .flatMap(userToken -> {
+ if (type.equals(userToken.getType())) {
+ return this.changeTokenState(userToken.getToken(), TokenState.offline);
+ }
+ return Mono.empty();
+ })
+ .then(doSign);
+ }
- return doSign;
- });
+ return doSign;
+ })
+ .flatMap(this::onUserTokenCreated);
}
@@ -213,9 +243,8 @@ public class RedisUserTokenManager implements UserTokenManager {
@Override
public Mono checkExpiredToken() {
- return operations.scan(ScanOptions
- .scanOptions()
- .match("user-token-user:*").build())
+ return operations
+ .scan(ScanOptions.scanOptions().match("user-token-user:*").build())
.map(String::valueOf)
.flatMap(key -> userTokenMapping.members(key)
.map(String::valueOf)
@@ -228,4 +257,28 @@ public class RedisUserTokenManager implements UserTokenManager {
})))
.then();
}
+
+ private Mono onTokenRemoved(UserToken token) {
+ if (eventPublisher == null) {
+ return Mono.empty();
+ }
+ return Mono.fromRunnable(() -> eventPublisher.publishEvent(new UserTokenRemovedEvent(token)));
+ }
+
+ private Mono onTokenChanged(UserToken old, UserToken newToken) {
+ if (eventPublisher == null) {
+ return Mono.empty();
+ }
+ return Mono.fromRunnable(() -> eventPublisher.publishEvent(new UserTokenChangedEvent(old, newToken)));
+ }
+
+ private Mono onUserTokenCreated(UserToken token) {
+ if (eventPublisher == null) {
+ return Mono.just(token);
+ }
+ return Mono
+ .fromRunnable(() -> eventPublisher.publishEvent(new UserTokenCreatedEvent(token)))
+ .thenReturn(token);
+ }
+
}
diff --git a/hsweb-authorization/hsweb-authorization-basic/pom.xml b/hsweb-authorization/hsweb-authorization-basic/pom.xml
index 169cd742b..59a230c6b 100644
--- a/hsweb-authorization/hsweb-authorization-basic/pom.xml
+++ b/hsweb-authorization/hsweb-authorization-basic/pom.xml
@@ -5,7 +5,7 @@
hsweb-authorization
org.hswebframework.web
- 4.0.7
+ 4.0.8-SNAPSHOT
4.0.0
diff --git a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java
index 23a3bd2d4..472b074be 100644
--- a/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java
+++ b/hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java
@@ -17,11 +17,8 @@
package org.hswebframework.web.authorization.basic.web;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.SneakyThrows;
import org.hswebframework.web.authorization.Authentication;
@@ -33,12 +30,10 @@ import org.hswebframework.web.authorization.events.AuthorizationFailedEvent;
import org.hswebframework.web.authorization.events.AuthorizationSuccessEvent;
import org.hswebframework.web.authorization.exception.AuthenticationException;
import org.hswebframework.web.authorization.exception.UnAuthorizedException;
-import org.hswebframework.web.authorization.simple.CompositeReactiveAuthenticationManager;
import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
import org.hswebframework.web.logging.AccessLogger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
-import org.springframework.data.repository.query.Param;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;
@@ -71,7 +66,6 @@ public class AuthorizationController {
}
@PostMapping(value = "/login", consumes = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation("用户名密码登录,json方式")
@Authorize(ignore = true)
@AccessLogger(ignore = true)
@Operation(summary = "登录",description = "必要参数:username,password.根据配置不同,其他参数也不同,如:验证码等.")
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/pom.xml b/hsweb-authorization/hsweb-authorization-oauth2/pom.xml
new file mode 100644
index 000000000..657a94897
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/pom.xml
@@ -0,0 +1,52 @@
+
+
+
+ hsweb-authorization
+ org.hswebframework.web
+ 4.0.8-SNAPSHOT
+
+ 4.0.0
+
+ hsweb-authorization-oauth2
+
+
+
+ org.hswebframework.web
+ hsweb-authorization-api
+ ${project.version}
+
+
+
+ io.projectreactor
+ reactor-core
+
+
+
+ org.springframework
+ spring-webflux
+ true
+
+
+
+ org.springframework.data
+ spring-data-redis
+ true
+
+
+
+ io.lettuce
+ lettuce-core
+ test
+
+
+
+ org.hswebframework.web
+ hsweb-authorization-basic
+ ${project.version}
+ true
+
+
+
+
\ No newline at end of file
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/ErrorType.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/ErrorType.java
new file mode 100644
index 000000000..92bba479d
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/ErrorType.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2020 http://www.hswebframework.org
+ *
+ * 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 org.hswebframework.web.oauth2;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public enum ErrorType {
+ ILLEGAL_CODE(1001), //错误的授权码
+ ILLEGAL_ACCESS_TOKEN(1002), //错误的access_token
+ ILLEGAL_CLIENT_ID(1003),//客户端信息错误
+ ILLEGAL_CLIENT_SECRET(1004),//客户端密钥错误
+ ILLEGAL_GRANT_TYPE(1005), //错误的授权方式
+ ILLEGAL_RESPONSE_TYPE(1006),//response_type 错误
+ ILLEGAL_AUTHORIZATION(1007),//Authorization 错误
+ ILLEGAL_REFRESH_TOKEN(1008),//refresh_token 错误
+ ILLEGAL_REDIRECT_URI(1009), //redirect_url 错误
+ ILLEGAL_SCOPE(1010), //scope 错误
+ ILLEGAL_USERNAME(1011), //username 错误
+ ILLEGAL_PASSWORD(1012), //password 错误
+
+ SCOPE_OUT_OF_RANGE(2010), //scope超出范围
+
+ UNAUTHORIZED_CLIENT(4010), //无权限
+ EXPIRED_TOKEN(4011), //TOKEN过期
+ INVALID_TOKEN(4012), //TOKEN已失效
+ UNSUPPORTED_GRANT_TYPE(4013), //不支持的认证类型
+ UNSUPPORTED_RESPONSE_TYPE(4014), //不支持的响应类型
+
+ EXPIRED_CODE(4015), //AUTHORIZATION_CODE过期
+ EXPIRED_REFRESH_TOKEN(4020), //REFRESH_TOKEN过期
+
+ CLIENT_DISABLED(4016),//客户端已被禁用
+
+ CLIENT_NOT_EXIST(4040),//客户端不存在
+
+ USER_NOT_EXIST(4041),//客户端不存在
+
+ STATE_ERROR(4042), //stat错误
+
+ ACCESS_DENIED(503), //访问被拒绝
+
+ OTHER(5001), //其他错误 ;
+
+ PARSE_RESPONSE_ERROR(5002),//解析返回结果错误
+
+ SERVICE_ERROR(5003); //服务器返回错误信息
+
+
+ private final String message;
+ private final int code;
+ static final Map codeMapping = Arrays.stream(ErrorType.values())
+ .collect(Collectors.toMap(ErrorType::code, type -> type));
+
+ ErrorType(int code) {
+ this.code = code;
+ message = this.name().toLowerCase();
+ }
+
+ ErrorType(int code, String message) {
+ this.message = message;
+ this.code = code;
+ }
+
+ public String message() {
+ if (message == null) {
+ return this.name();
+ }
+ return message;
+ }
+
+ public int code() {
+ return code;
+ }
+
+ public T throwThis(Function errorTypeFunction) {
+ throw errorTypeFunction.apply(this);
+ }
+
+ public T throwThis(BiFunction errorTypeFunction, String message) {
+ throw errorTypeFunction.apply(this, message);
+ }
+
+ public static Optional fromCode(int code) {
+ return Optional.ofNullable(codeMapping.get(code));
+ }
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/GrantType.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/GrantType.java
new file mode 100644
index 000000000..d8d25ac1b
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/GrantType.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020 http://www.hswebframework.org
+ *
+ * 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 org.hswebframework.web.oauth2;
+
+/**
+ *
+ * @author zhouhao
+ */
+public interface GrantType {
+ String authorization_code = "authorization_code";
+ String implicit = "implicit";
+ @SuppressWarnings("all")
+ String password = "password";
+ String client_credentials = "client_credentials";
+ String refresh_token = "refresh_token";
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/OAuth2Constants.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/OAuth2Constants.java
new file mode 100644
index 000000000..86c986de5
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/OAuth2Constants.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2020 http://www.hswebframework.org
+ *
+ * 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 org.hswebframework.web.oauth2;
+
+/**
+ * @author zhouhao
+ */
+public interface OAuth2Constants {
+ String access_token = "access_token";
+ String refresh_token = "refresh_token";
+ String grant_type = "grant_type";
+ String scope = "scope";
+ String client_id = "client_id";
+ String client_secret = "client_secret";
+ String authorization = "Authorization";
+ String redirect_uri = "redirect_uri";
+ String response_type = "response_type";
+ String state = "state";
+ String code = "code";
+ String username = "username";
+
+ @SuppressWarnings("all")
+ String password = "password";
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/OAuth2Exception.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/OAuth2Exception.java
new file mode 100644
index 000000000..fc48ad3e1
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/OAuth2Exception.java
@@ -0,0 +1,19 @@
+package org.hswebframework.web.oauth2;
+
+import lombok.Getter;
+import org.hswebframework.web.exception.BusinessException;
+
+@Getter
+public class OAuth2Exception extends BusinessException {
+ private final ErrorType type;
+
+ public OAuth2Exception(ErrorType type) {
+ super(type.message(), type.name(), type.code());
+ this.type = type;
+ }
+
+ public OAuth2Exception(String message, Throwable cause, ErrorType type) {
+ super(message, cause);
+ this.type = type;
+ }
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/ResponseType.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/ResponseType.java
new file mode 100644
index 000000000..72fb5918c
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/ResponseType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 http://www.hswebframework.org
+ *
+ * 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 org.hswebframework.web.oauth2;
+
+/**
+ * TODO 完成注释
+ *
+ * @author zhouhao
+ */
+public interface ResponseType {
+ String code = "code";
+ String token = "token";
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessToken.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessToken.java
new file mode 100644
index 000000000..a4c1956c8
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessToken.java
@@ -0,0 +1,28 @@
+package org.hswebframework.web.oauth2.server;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.*;
+
+@Getter
+@Setter
+@AllArgsConstructor
+@NoArgsConstructor
+@ToString
+public class AccessToken extends OAuth2Response {
+
+ private static final long serialVersionUID = -6849794470754667710L;
+
+ @Schema(name="access_token")
+ @JsonProperty("access_token")
+ private String accessToken;
+
+ @Schema(name="refresh_token")
+ @JsonProperty("refresh_token")
+ private String refreshToken;
+
+ @Schema(name="expires_in")
+ @JsonProperty("expires_in")
+ private int expiresIn;
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java
new file mode 100644
index 000000000..1645357d3
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/AccessTokenManager.java
@@ -0,0 +1,16 @@
+package org.hswebframework.web.oauth2.server;
+
+import org.hswebframework.web.authorization.Authentication;
+import reactor.core.publisher.Mono;
+
+public interface AccessTokenManager {
+
+ Mono getAuthenticationByToken(String accessToken);
+
+ Mono createAccessToken(String clientId,
+ Authentication authentication,
+ boolean singleton);
+
+ Mono refreshAccessToken(String clientId, String refreshToken);
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java
new file mode 100644
index 000000000..cee9c7ca6
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/ClientCredentialGranter.java
@@ -0,0 +1,7 @@
+package org.hswebframework.web.oauth2.server;
+
+public interface ClientCredentialGranter extends OAuth2Granter {
+
+
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java
new file mode 100644
index 000000000..6421546de
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Client.java
@@ -0,0 +1,38 @@
+package org.hswebframework.web.oauth2.server;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.web.oauth2.ErrorType;
+import org.hswebframework.web.oauth2.OAuth2Exception;
+import org.springframework.util.StringUtils;
+
+import javax.validation.constraints.NotBlank;
+
+@Getter
+@Setter
+public class OAuth2Client {
+
+ @NotBlank
+ private String clientId;
+
+ @NotBlank
+ private String clientSecret;
+
+ @NotBlank
+ private String name;
+
+ private String description;
+
+ @NotBlank
+ private String redirectUrl;
+
+ //client 所属用户
+ private String userId;
+
+ public void validateRedirectUri(String redirectUri) {
+ if (StringUtils.isEmpty(redirectUri) || (!redirectUri.startsWith(this.redirectUrl))) {
+ throw new OAuth2Exception(ErrorType.ILLEGAL_REDIRECT_URI);
+ }
+ }
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ClientManager.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ClientManager.java
new file mode 100644
index 000000000..eb6d5b9df
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ClientManager.java
@@ -0,0 +1,9 @@
+package org.hswebframework.web.oauth2.server;
+
+import reactor.core.publisher.Mono;
+
+public interface OAuth2ClientManager {
+
+ Mono getClient(String clientId);
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java
new file mode 100644
index 000000000..0b1feebf1
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2GrantService.java
@@ -0,0 +1,12 @@
+package org.hswebframework.web.oauth2.server;
+
+
+import org.hswebframework.web.oauth2.server.code.AuthorizationCodeGranter;
+
+public interface OAuth2GrantService {
+
+ AuthorizationCodeGranter authorizationCode();
+
+ ClientCredentialGranter clientCredential();
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Granter.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Granter.java
new file mode 100644
index 000000000..e9ee66b1b
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Granter.java
@@ -0,0 +1,7 @@
+package org.hswebframework.web.oauth2.server;
+
+public interface OAuth2Granter {
+
+
+
+}
diff --git a/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Request.java b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Request.java
new file mode 100644
index 000000000..ab21e5753
--- /dev/null
+++ b/hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2Request.java
@@ -0,0 +1,31 @@
+package org.hswebframework.web.oauth2.server;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+@Getter
+@Setter
+@AllArgsConstructor
+public class OAuth2Request {
+
+ private Map parameters;
+
+
+ public Optional