소스 검색

优化权限认证

zhouhao 2 년 전
부모
커밋
03c47f1228
13개의 변경된 파일147개의 추가작업 그리고 52개의 파일을 삭제
  1. 6 2
      hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java
  2. 6 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java
  3. 22 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/BearerTokenParser.java
  4. 1 11
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/DefaultUserTokenGenPar.java
  5. 14 9
      hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ServerAutoConfiguration.java
  6. 4 1
      hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/code/DefaultAuthorizationCodeGranter.java
  7. 56 18
      hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/impl/RedisAccessTokenManager.java
  8. 9 2
      hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/utils/OAuth2ScopeUtils.java
  9. 10 2
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/UserEntity.java
  10. 2 0
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/service/reactive/ReactiveUserService.java
  11. 4 4
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationServiceAutoConfiguration.java
  12. 2 2
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveAuthenticationInitializeService.java
  13. 11 1
      hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveUserService.java

+ 6 - 2
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/simple/SimpleAuthentication.java

@@ -78,7 +78,6 @@ public class SimpleAuthentication implements Authentication {
             me.getDataAccesses().addAll(permission.getDataAccesses());
         }
 
-
         for (Dimension dimension : authentication.getDimensions()) {
             if (!getDimension(dimension.getType(), dimension.getId()).isPresent()) {
                 dimensions.add(dimension);
@@ -91,7 +90,6 @@ public class SimpleAuthentication implements Authentication {
     public Authentication copy(BiPredicate<Permission, String> permissionFilter,
                                Predicate<Dimension> dimension) {
         SimpleAuthentication authentication = new SimpleAuthentication();
-        authentication.setUser(user);
         authentication.setDimensions(dimensions.stream().filter(dimension).collect(Collectors.toList()));
         authentication.setPermissions(permissions
                                               .stream()
@@ -99,6 +97,12 @@ public class SimpleAuthentication implements Authentication {
                                               .filter(per -> !per.getActions().isEmpty())
                                               .collect(Collectors.toList())
         );
+        authentication.setUser(user);
         return authentication;
     }
+
+    public void setUser(User user) {
+        this.user = user;
+        dimensions.add(user);
+    }
 }

+ 6 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java

@@ -145,6 +145,12 @@ public class AuthorizingHandlerAutoConfiguration {
         return new ReactiveUserTokenController();
     }
 
+    @Bean
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
+    public BearerTokenParser bearerTokenParser() {
+        return new BearerTokenParser();
+    }
+
     @Configuration
     public static class DataAccessHandlerProcessor implements BeanPostProcessor {
 

+ 22 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/BearerTokenParser.java

@@ -0,0 +1,22 @@
+package org.hswebframework.web.authorization.basic.web;
+
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+public class BearerTokenParser implements ReactiveUserTokenParser {
+    @Override
+    public Mono<ParsedToken> parseToken(ServerWebExchange exchange) {
+
+        String token = exchange
+                .getRequest()
+                .getHeaders()
+                .getFirst(HttpHeaders.AUTHORIZATION);
+
+        if (token != null && token.startsWith("Bearer ")) {
+            return Mono.just(ParsedToken.of("bearer", token.substring(7)));
+        }
+        return Mono.empty();
+    }
+}

+ 1 - 11
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/DefaultUserTokenGenPar.java

@@ -62,16 +62,6 @@ public class DefaultUserTokenGenPar implements ReactiveUserTokenGenerator, React
         if (token == null) {
             return Mono.empty();
         }
-        return Mono.just(new ParsedToken() {
-            @Override
-            public String getToken() {
-                return token;
-            }
-
-            @Override
-            public String getType() {
-                return getTokenType();
-            }
-        });
+        return Mono.just(ParsedToken.of(getTokenType(),token));
     }
 }

+ 14 - 9
hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/OAuth2ServerAutoConfiguration.java

@@ -3,6 +3,8 @@ package org.hswebframework.web.oauth2.server;
 import org.hswebframework.web.authorization.ReactiveAuthenticationHolder;
 import org.hswebframework.web.authorization.ReactiveAuthenticationManager;
 import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
+import org.hswebframework.web.authorization.token.UserToken;
+import org.hswebframework.web.authorization.token.UserTokenManager;
 import org.hswebframework.web.oauth2.server.auth.ReactiveOAuth2AccessTokenParser;
 import org.hswebframework.web.oauth2.server.code.AuthorizationCodeGranter;
 import org.hswebframework.web.oauth2.server.code.DefaultAuthorizationCodeGranter;
@@ -22,6 +24,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
+import org.springframework.data.redis.core.ReactiveRedisOperations;
 
 @Configuration(proxyBeanMethods = false)
 @EnableConfigurationProperties(OAuth2Properties.class)
@@ -32,13 +35,13 @@ public class OAuth2ServerAutoConfiguration {
     @ConditionalOnClass(ReactiveUserTokenParser.class)
     static class ReactiveOAuth2AccessTokenParserConfiguration {
 
-        @Bean
-        @ConditionalOnBean(AccessTokenManager.class)
-        public ReactiveOAuth2AccessTokenParser reactiveOAuth2AccessTokenParser(AccessTokenManager accessTokenManager) {
-            ReactiveOAuth2AccessTokenParser parser = new ReactiveOAuth2AccessTokenParser(accessTokenManager);
-            ReactiveAuthenticationHolder.addSupplier(parser);
-            return parser;
-        }
+//        @Bean
+//        @ConditionalOnBean(AccessTokenManager.class)
+//        public ReactiveOAuth2AccessTokenParser reactiveOAuth2AccessTokenParser(AccessTokenManager accessTokenManager) {
+//            ReactiveOAuth2AccessTokenParser parser = new ReactiveOAuth2AccessTokenParser(accessTokenManager);
+//            ReactiveAuthenticationHolder.addSupplier(parser);
+//            return parser;
+//        }
     }
 
     @Configuration(proxyBeanMethods = false)
@@ -48,9 +51,11 @@ public class OAuth2ServerAutoConfiguration {
 
         @Bean
         @ConditionalOnMissingBean
-        public AccessTokenManager accessTokenManager(ReactiveRedisConnectionFactory redisConnectionFactory,
+        public AccessTokenManager accessTokenManager(ReactiveRedisOperations<Object, Object> redis,
+                                                     UserTokenManager tokenManager,
                                                      OAuth2Properties properties) {
-            RedisAccessTokenManager manager = new RedisAccessTokenManager(redisConnectionFactory);
+            @SuppressWarnings("all")
+            RedisAccessTokenManager manager = new RedisAccessTokenManager((ReactiveRedisOperations) redis, tokenManager);
             manager.setTokenExpireIn((int) properties.getTokenExpireIn().getSeconds());
             manager.setRefreshExpireIn((int) properties.getRefreshTokenIn().getSeconds());
             return manager;

+ 4 - 1
hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/code/DefaultAuthorizationCodeGranter.java

@@ -48,9 +48,12 @@ public class DefaultAuthorizationCodeGranter implements AuthorizationCodeGranter
         request.getParameter(OAuth2Constants.scope).map(String::valueOf).ifPresent(codeCache::setScope);
         codeCache.setCode(code);
         codeCache.setClientId(client.getClientId());
+
         ScopePredicate permissionPredicate = OAuth2ScopeUtils.createScopePredicate(codeCache.getScope());
 
-        codeCache.setAuthentication(authentication.copy((permission, action) -> permissionPredicate.test(permission.getId(), action), dimension -> true));
+        codeCache.setAuthentication(authentication.copy(
+                (permission, action) -> permissionPredicate.test(permission.getId(), action),
+                dimension -> permissionPredicate.test(dimension.getType().getId(), dimension.getId())));
 
 
         return redis

+ 56 - 18
hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/impl/RedisAccessTokenManager.java

@@ -4,6 +4,9 @@ import lombok.Getter;
 import lombok.Setter;
 import org.apache.commons.codec.digest.DigestUtils;
 import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.token.AuthenticationUserToken;
+import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.authorization.token.redis.RedisUserTokenManager;
 import org.hswebframework.web.oauth2.ErrorType;
 import org.hswebframework.web.oauth2.OAuth2Exception;
 import org.hswebframework.web.oauth2.server.AccessToken;
@@ -13,6 +16,7 @@ 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.Flux;
 import reactor.core.publisher.Mono;
 
 import java.time.Duration;
@@ -22,6 +26,8 @@ public class RedisAccessTokenManager implements AccessTokenManager {
 
     private final ReactiveRedisOperations<String, RedisAccessToken> tokenRedis;
 
+    private final UserTokenManager userTokenManager;
+
     @Getter
     @Setter
     private int tokenExpireIn = 7200;//2小时
@@ -30,29 +36,32 @@ public class RedisAccessTokenManager implements AccessTokenManager {
     @Setter
     private int refreshExpireIn = 2592000; //30天
 
-    public RedisAccessTokenManager(ReactiveRedisOperations<String, RedisAccessToken> tokenRedis) {
+    public RedisAccessTokenManager(ReactiveRedisOperations<String, RedisAccessToken> tokenRedis,
+                                   UserTokenManager userTokenManager) {
         this.tokenRedis = tokenRedis;
+        this.userTokenManager = userTokenManager;
     }
 
     @SuppressWarnings("all")
     public RedisAccessTokenManager(ReactiveRedisConnectionFactory connectionFactory) {
-        this(new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext
+        ReactiveRedisTemplate redis = new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext
                 .newSerializationContext()
                 .key((RedisSerializer) RedisSerializer.string())
                 .value(RedisSerializer.java())
                 .hashKey(RedisSerializer.string())
                 .hashValue(RedisSerializer.java())
-                .build()
-        ));
+                .build());
+        this.tokenRedis = redis;
+        this.userTokenManager = new RedisUserTokenManager(redis);
     }
 
+
     @Override
     public Mono<Authentication> getAuthenticationByToken(String accessToken) {
-
-        return tokenRedis
-                .opsForValue()
-                .get(createTokenRedisKey(accessToken))
-                .map(RedisAccessToken::getAuthentication);
+        return userTokenManager
+                .getByToken(accessToken)
+                .filter(token -> token instanceof AuthenticationUserToken)
+                .map(t -> ((AuthenticationUserToken) t).getAuthentication());
     }
 
     private String createTokenRedisKey(String token) {
@@ -75,12 +84,36 @@ public class RedisAccessTokenManager implements AccessTokenManager {
         return storeToken(accessToken).thenReturn(accessToken);
     }
 
+    private Mono<Void> storeAuthToken(RedisAccessToken token) {
+        if (token.isSingleton()) {
+            return userTokenManager
+                    .signIn(token.getAccessToken(),
+                            "oauth2",
+                            token.getAuthentication().getUser().getId(),
+                            tokenExpireIn * 1000L)
+                    .then();
+        } else {
+            return userTokenManager
+                    .signIn(token.getAccessToken(),
+                            "oauth2",
+                            token.getAuthentication().getUser().getId(),
+                            tokenExpireIn * 1000L,
+                            token.getAuthentication())
+                    .then();
+        }
+    }
+
     private Mono<Void> storeToken(RedisAccessToken token) {
-        return Mono
-                .zip(
-                        tokenRedis.opsForValue().set(createTokenRedisKey(token.getAccessToken()), token, Duration.ofSeconds(tokenExpireIn)),
-                        tokenRedis.opsForValue().set(createRefreshTokenRedisKey(token.getRefreshToken()), token, Duration.ofSeconds(refreshExpireIn))
-                ).then();
+
+        return Flux
+                .merge(storeAuthToken(token),
+                       tokenRedis
+                               .opsForValue()
+                               .set(createTokenRedisKey(token.getAccessToken()), token, Duration.ofSeconds(tokenExpireIn)),
+                       tokenRedis
+                               .opsForValue()
+                               .set(createRefreshTokenRedisKey(token.getRefreshToken()), token, Duration.ofSeconds(refreshExpireIn)))
+                .then();
     }
 
     private Mono<AccessToken> doCreateSingletonAccessToken(String clientId, Authentication authentication) {
@@ -129,10 +162,15 @@ public class RedisAccessTokenManager implements AccessTokenManager {
                             .as(result -> {
                                 // 单例token
                                 if (token.isSingleton()) {
-                                    return tokenRedis
-                                            .opsForValue()
-                                            .set(createSingletonTokenRedisKey(clientId), token, Duration.ofSeconds(tokenExpireIn))
-                                            .then(result);
+                                    return userTokenManager
+                                            .signOutByToken(token.getAccessToken())
+                                            .then(
+                                                    tokenRedis
+                                                            .opsForValue()
+                                                            .set(createSingletonTokenRedisKey(clientId), token, Duration.ofSeconds(tokenExpireIn))
+                                                            .then(result)
+                                            )
+                                            ;
                                 }
                                 return result;
                             })

+ 9 - 2
hsweb-authorization/hsweb-authorization-oauth2/src/main/java/org/hswebframework/web/oauth2/server/utils/OAuth2ScopeUtils.java

@@ -6,6 +6,10 @@ import org.springframework.util.StringUtils;
 import java.util.*;
 
 /**
+ * <pre>{@code
+ *   role:* user:* device-manager:*
+ * }</pre>
+ *
  * @author zhouhao
  * @since 4.0.8
  */
@@ -23,10 +27,13 @@ public class OAuth2ScopeUtils {
             Set<String> acts = actions.computeIfAbsent(per, k -> new HashSet<>());
             acts.addAll(Arrays.asList(permissions).subList(1, permissions.length));
         }
-
+        //全部授权
+        if (actions.containsKey("*")) {
+            return ((permission, action) -> true);
+        }
         return ((permission, action) -> Optional
                 .ofNullable(actions.get(permission))
-                .map(acts -> action.length == 0 || acts.containsAll(Arrays.asList(action)))
+                .map(acts -> action.length == 0 || acts.contains("*") || acts.containsAll(Arrays.asList(action)))
                 .orElse(false));
     }
 }

+ 10 - 2
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/entity/UserEntity.java

@@ -1,5 +1,7 @@
 package org.hswebframework.web.system.authorization.api.entity;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.Hidden;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -9,15 +11,18 @@ import org.apache.commons.codec.digest.DigestUtils;
 import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
 import org.hswebframework.ezorm.rdb.mapping.annotation.DefaultValue;
 import org.hswebframework.web.api.crud.entity.GenericEntity;
+import org.hswebframework.web.api.crud.entity.QueryParamEntity;
 import org.hswebframework.web.api.crud.entity.RecordCreationEntity;
 import org.hswebframework.web.bean.ToString;
 import org.hswebframework.web.validator.CreateGroup;
+import org.springframework.util.StringUtils;
 
 import javax.persistence.Column;
 import javax.persistence.GeneratedValue;
 import javax.persistence.Index;
 import javax.persistence.Table;
 import javax.validation.constraints.NotBlank;
+import java.util.Objects;
 
 /**
  * 系统用户实体
@@ -85,7 +90,10 @@ public class UserEntity extends GenericEntity<String> implements RecordCreationE
         return super.getId();
     }
 
-    public void generateId(){
-       setId(DigestUtils.md5Hex(username));
+    public void generateId() {
+        if (StringUtils.hasText(getId())) {
+            return;
+        }
+        setId(DigestUtils.md5Hex(username));
     }
 }

+ 2 - 0
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-api/src/main/java/org/hswebframework/web/system/authorization/api/service/reactive/ReactiveUserService.java

@@ -29,6 +29,8 @@ public interface ReactiveUserService {
      */
     Mono<Boolean> saveUser(Mono<UserEntity> userEntity);
 
+    Mono<UserEntity> addUser(UserEntity userEntity);
+
     /**
      * 根据用户名查询用户实体,如果用户不存在则返回{@link Mono#empty()}
      *

+ 4 - 4
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/configuration/AuthorizationServiceAutoConfiguration.java

@@ -55,10 +55,10 @@ public class AuthorizationServiceAutoConfiguration {
             return new DefaultDimensionService();
         }
 
-        @Bean
-        public UserDimensionProvider userPermissionDimensionProvider() {
-            return new UserDimensionProvider();
-        }
+//        @Bean
+//        public UserDimensionProvider userPermissionDimensionProvider() {
+//            return new UserDimensionProvider();
+//        }
 
         @Bean
         public DefaultDimensionUserService defaultDimensionUserService() {

+ 2 - 2
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveAuthenticationInitializeService.java

@@ -69,8 +69,9 @@ public class DefaultReactiveAuthenticationInitializeService
                                            .username(user.getUsername())
                                            .userType(user.getType())
                                            .build());
+
             return initPermission(authentication)
-                    .switchIfEmpty(Mono.just(authentication))
+                    .defaultIfEmpty(authentication)
                     .onErrorResume(err -> {
                         log.warn(err.getMessage(), err);
                         return Mono.just(authentication);
@@ -82,7 +83,6 @@ public class DefaultReactiveAuthenticationInitializeService
                                 .then(Mono.fromSupplier(event::getAuthentication));
                     });
         });
-
     }
 
     protected Flux<AuthorizationSettingEntity> getSettings(List<Dimension> dimensions) {

+ 11 - 1
hsweb-system/hsweb-system-authorization/hsweb-system-authorization-default/src/main/java/org/hswebframework/web/system/authorization/defaults/service/DefaultReactiveUserService.java

@@ -24,6 +24,7 @@ import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 import javax.validation.ValidationException;
+import java.util.Objects;
 
 
 public class DefaultReactiveUserService extends GenericReactiveCrudService<UserEntity, String> implements ReactiveUserService {
@@ -61,10 +62,19 @@ public class DefaultReactiveUserService extends GenericReactiveCrudService<UserE
                     }
                     return findById(userEntity.getId())
                             .flatMap(ignore -> doUpdate(userEntity))
-                            .switchIfEmpty(Mono.error(NotFoundException::new));
+                            .switchIfEmpty(
+                                    Objects.equals(userEntity.getId(),userEntity.getUsername()) ?
+                                            doAdd(userEntity) :
+                                            Mono.error(NotFoundException::new)
+                            );
                 }).thenReturn(true);
     }
 
+    @Override
+    public Mono<UserEntity> addUser(UserEntity userEntity) {
+        return doAdd(userEntity);
+    }
+
     protected Mono<UserEntity> doAdd(UserEntity userEntity) {
 
         return Mono