Browse Source

增加webflux权限控制

zhou-hao 5 năm trước cách đây
mục cha
commit
e2449c48d9
23 tập tin đã thay đổi với 611 bổ sung70 xóa
  1. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ParsedToken.java
  2. 18 0
      hsweb-authorization/hsweb-authorization-basic/pom.xml
  3. 149 57
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java
  4. 23 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java
  5. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/BasicAuthorizationTokenParser.java
  6. 2 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java
  7. 2 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java
  8. 75 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/DefaultUserTokenGenPar.java
  9. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/GeneratedToken.java
  10. 10 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ReactiveUserTokenGenerator.java
  11. 9 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ReactiveUserTokenParser.java
  12. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/SessionIdUserTokenGenerator.java
  13. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/SessionIdUserTokenParser.java
  14. 3 2
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java
  15. 2 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenParser.java
  16. 76 0
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenWebFilter.java
  17. 1 1
      hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/WebUserTokenInterceptor.java
  18. 72 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java
  19. 20 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/FluxTestController.java
  20. 43 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestApplication.java
  21. 27 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java
  22. 65 0
      hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/WebFluxTests.java
  23. 9 0
      hsweb-authorization/hsweb-authorization-basic/src/test/resources/application.yml

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

@@ -1,4 +1,4 @@
-package org.hswebframework.web.authorization.basic.web;
+package org.hswebframework.web.authorization.token;
 
 /**
  * 令牌解析结果

+ 18 - 0
hsweb-authorization/hsweb-authorization-basic/pom.xml

@@ -64,6 +64,24 @@
         <dependency>
             <groupId>javax.servlet</groupId>
             <artifactId>javax.servlet-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aspects</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+            <scope>test</scope>
         </dependency>
 
     </dependencies>

+ 149 - 57
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingController.java

@@ -2,15 +2,13 @@ package org.hswebframework.web.authorization.basic.aop;
 
 import lombok.extern.slf4j.Slf4j;
 import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
 import org.hswebframework.web.aop.MethodInterceptorContext;
 import org.hswebframework.web.aop.MethodInterceptorHolder;
 import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.authorization.basic.handler.AuthorizingHandler;
-import org.hswebframework.web.authorization.define.AuthorizeDefinition;
-import org.hswebframework.web.authorization.define.AuthorizeDefinitionInitializedEvent;
-import org.hswebframework.web.authorization.define.AuthorizingContext;
-import org.hswebframework.web.authorization.define.Phased;
+import org.hswebframework.web.authorization.define.*;
 import org.hswebframework.web.authorization.exception.UnAuthorizedException;
 import org.hswebframework.web.utils.AnnotationUtils;
 import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
@@ -19,6 +17,8 @@ import org.springframework.boot.CommandLineRunner;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 import java.lang.reflect.Method;
 import java.util.List;
@@ -29,13 +29,20 @@ import java.util.stream.Collectors;
  * @see AuthorizeDefinitionInitializedEvent
  */
 @Slf4j
-public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor implements CommandLineRunner {
+@SuppressWarnings("all")
+public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor implements CommandLineRunner, MethodInterceptor {
 
     private static final long serialVersionUID = 1154190623020670672L;
 
     @Autowired
     private ApplicationEventPublisher eventPublisher;
 
+    @Autowired
+    private AuthorizingHandler authorizingHandler;
+
+    @Autowired
+    private AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser;
+
     private DefaultAopMethodAuthorizeDefinitionParser defaultParser = new DefaultAopMethodAuthorizeDefinitionParser();
 
     private boolean autoParse = false;
@@ -44,70 +51,155 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
         this.autoParse = autoParse;
     }
 
-    public AopAuthorizingController(AuthorizingHandler authorizingHandler, AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser) {
-        super((MethodInterceptor) methodInvocation -> {
+    protected Mono<?> handleReactive(AuthorizeDefinition definition, MethodInterceptorHolder holder, AuthorizingContext context, Mono<?> mono) {
+
+        return Authentication.currentReactive()
+                .switchIfEmpty(Mono.error(new UnAuthorizedException()))
+                .flatMap(auth -> {
+                    DataAccessDefinition dataAccessDefinition = definition.getDataAccessDefinition();
+
+                    context.setAuthentication(auth);
+                    if (definition.getPhased() == Phased.before) {
+                        authorizingHandler.handRBAC(context);
+                        if (dataAccessDefinition != null && dataAccessDefinition.getPhased() == Phased.before) {
+                            authorizingHandler.handleDataAccess(context);
+                        } else {
+                            return mono.doOnNext(res -> {
+                                context.setParamContext(holder.createParamContext(res));
+                                authorizingHandler.handleDataAccess(context);
+                            });
+                        }
+                    } else {
+                        if (dataAccessDefinition != null && dataAccessDefinition.getPhased() == Phased.before) {
+                            authorizingHandler.handleDataAccess(context);
+                            return mono.doOnNext(res -> {
+                                context.setParamContext(holder.createParamContext(res));
+                                authorizingHandler.handRBAC(context);
+                            });
+                        } else {
+                            return mono.doOnNext(res -> {
+                                context.setParamContext(holder.createParamContext(res));
+                                authorizingHandler.handle(context);
+                            });
+                        }
 
-            MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
+                    }
+                    return mono;
+                });
+    }
 
-            MethodInterceptorContext paramContext = holder.createParamContext();
+    protected Flux<?> handleReactive(AuthorizeDefinition definition, MethodInterceptorHolder holder, AuthorizingContext context, Flux<?> flux) {
+
+        return Authentication.currentReactive()
+                .switchIfEmpty(Mono.error(new UnAuthorizedException()))
+                .flatMapMany(auth -> {
+                    context.setAuthentication(auth);
+                    if (definition.getPhased() == Phased.before) {
+                        authorizingHandler.handRBAC(context);
+                        if (definition.getDataAccessDefinition().getPhased() == Phased.before) {
+                            authorizingHandler.handleDataAccess(context);
+                        } else {
+                            return flux.doOnNext(res -> {
+                                context.setParamContext(holder.createParamContext(res));
+                                authorizingHandler.handleDataAccess(context);
+                            });
+                        }
+                    } else {
+
+                        if (definition.getDataAccessDefinition().getPhased() == Phased.before) {
+                            authorizingHandler.handleDataAccess(context);
+                            return flux.doOnNext(res -> {
+                                context.setParamContext(holder.createParamContext(res));
+                                authorizingHandler.handRBAC(context);
+                            });
+                        } else {
+                            return flux.doOnNext(res -> {
+                                context.setParamContext(holder.createParamContext(res));
+                                authorizingHandler.handle(context);
+                            });
+                        }
 
-            AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser.parse(methodInvocation.getThis().getClass(), methodInvocation.getMethod(), paramContext);
-            Object result = null;
-            boolean isControl = false;
-            if (null != definition) {
-                Authentication authentication = Authentication.current().orElseThrow(UnAuthorizedException::new);
-                //空配置也进行权限控制
-//                if (!definition.isEmpty()) {
+                    }
+                    return flux;
+                });
+    }
 
-                AuthorizingContext context = new AuthorizingContext();
-                context.setAuthentication(authentication);
-                context.setDefinition(definition);
-                context.setParamContext(paramContext);
-                isControl = true;
 
-                Phased dataAccessPhased = null;
-                if (definition.getDataAccessDefinition() != null) {
-                    dataAccessPhased = definition.getDataAccessDefinition().getPhased();
-                }
-                if (definition.getPhased() == Phased.before) {
-                    //RDAC before
-                    authorizingHandler.handRBAC(context);
+    @Override
+    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
+        MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
+
+        MethodInterceptorContext paramContext = holder.createParamContext();
+
+        AuthorizeDefinition definition = aopMethodAuthorizeDefinitionParser.parse(methodInvocation.getThis().getClass(), methodInvocation.getMethod(), paramContext);
+        Object result = null;
+        boolean isControl = false;
+        if (null != definition) {
+            AuthorizingContext context = new AuthorizingContext();
+            context.setDefinition(definition);
+            context.setParamContext(paramContext);
+
+            Class<?> returnType = methodInvocation.getMethod().getReturnType();
+            //handle reactive method
+            if (Mono.class.isAssignableFrom(returnType)) {
+                return handleReactive(definition, holder, context, ((Mono<?>) methodInvocation.proceed()));
+            } else if (Flux.class.isAssignableFrom(returnType)) {
+                return handleReactive(definition, holder, context, ((Flux<?>) methodInvocation.proceed()));
+            }
 
-                    //方法调用前验证数据权限
-                    if (dataAccessPhased == Phased.before) {
-                        authorizingHandler.handleDataAccess(context);
-                    }
+            Authentication authentication = Authentication.current().orElseThrow(UnAuthorizedException::new);
 
-                    result = methodInvocation.proceed();
+            context.setAuthentication(authentication);
+            isControl = true;
 
-                    //方法调用后验证数据权限
-                    if (dataAccessPhased == Phased.after) {
-                        context.setParamContext(holder.createParamContext(result));
-                        authorizingHandler.handleDataAccess(context);
-                    }
-                } else {
-                    //方法调用前验证数据权限
-                    if (dataAccessPhased == Phased.before) {
-                        authorizingHandler.handleDataAccess(context);
-                    }
+            Phased dataAccessPhased = null;
+            if (definition.getDataAccessDefinition() != null) {
+                dataAccessPhased = definition.getDataAccessDefinition().getPhased();
+            }
+            if (definition.getPhased() == Phased.before) {
+                //RDAC before
+                authorizingHandler.handRBAC(context);
 
-                    result = methodInvocation.proceed();
-                    context.setParamContext(holder.createParamContext(result));
+                //方法调用前验证数据权限
+                if (dataAccessPhased == Phased.before) {
+                    authorizingHandler.handleDataAccess(context);
+                }
 
-                    authorizingHandler.handRBAC(context);
+                result = methodInvocation.proceed();
 
-                    //方法调用后验证数据权限
-                    if (dataAccessPhased == Phased.after) {
-                        authorizingHandler.handleDataAccess(context);
-                    }
+                //方法调用后验证数据权限
+                if (dataAccessPhased == Phased.after) {
+                    context.setParamContext(holder.createParamContext(result));
+                    authorizingHandler.handleDataAccess(context);
                 }
-//                }
-            }
-            if (!isControl) {
+            } else {
+                //方法调用前验证数据权限
+                if (dataAccessPhased == Phased.before) {
+                    authorizingHandler.handleDataAccess(context);
+                }
+
                 result = methodInvocation.proceed();
+                context.setParamContext(holder.createParamContext(result));
+
+                authorizingHandler.handRBAC(context);
+
+                //方法调用后验证数据权限
+                if (dataAccessPhased == Phased.after) {
+                    authorizingHandler.handleDataAccess(context);
+                }
             }
-            return result;
-        });
+        }
+        if (!isControl) {
+            result = methodInvocation.proceed();
+        }
+        return result;
+
+    }
+
+    public AopAuthorizingController(AuthorizingHandler authorizingHandler, AopMethodAuthorizeDefinitionParser aopMethodAuthorizeDefinitionParser) {
+        this.authorizingHandler = authorizingHandler;
+        this.aopMethodAuthorizeDefinitionParser = aopMethodAuthorizeDefinitionParser;
+        setAdvice(this);
     }
 
     @Override
@@ -127,12 +219,12 @@ public class AopAuthorizingController extends StaticMethodMatcherPointcutAdvisor
         if (autoParse) {
             List<AuthorizeDefinition> definitions = defaultParser.getAllParsed()
                     .stream().filter(def -> !def.isEmpty()).collect(Collectors.toList());
-
-
             log.info("publish AuthorizeDefinitionInitializedEvent,definition size:{}", definitions.size());
             eventPublisher.publishEvent(new AuthorizeDefinitionInitializedEvent(definitions));
 
             defaultParser.destroy();
         }
     }
+
+
 }

+ 23 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/AuthorizingHandlerAutoConfiguration.java

@@ -15,6 +15,7 @@ import org.hswebframework.web.authorization.twofactor.TwoFactorValidatorManager;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.BeanPostProcessor;
 import org.springframework.boot.autoconfigure.condition.*;
+import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.Ordered;
@@ -44,22 +45,23 @@ public class AuthorizingHandlerAutoConfiguration {
         return new DefaultAuthorizingHandler(dataAccessController);
     }
 
-
     @Bean
     @ConditionalOnMissingBean(UserTokenParser.class)
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public UserTokenParser userTokenParser() {
         return new SessionIdUserTokenParser();
     }
 
     @Bean
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public SessionIdUserTokenGenerator sessionIdUserTokenGenerator() {
         return new SessionIdUserTokenGenerator();
     }
 
-
     @Bean
     @ConditionalOnProperty(prefix = "hsweb.authorize.two-factor", name = "enable", havingValue = "true")
     @Order(100)
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public WebMvcConfigurer twoFactorHandlerConfigurer(TwoFactorValidatorManager manager) {
         return new WebMvcConfigurerAdapter() {
             @Override
@@ -70,8 +72,15 @@ public class AuthorizingHandlerAutoConfiguration {
         };
     }
 
+    @Bean
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
+    public UserTokenWebFilter userTokenWebFilter(){
+        return new UserTokenWebFilter();
+    }
+
     @Bean
     @Order(Ordered.HIGHEST_PRECEDENCE)
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public WebMvcConfigurer webUserTokenInterceptorConfigurer(UserTokenManager userTokenManager,
                                                               AopMethodAuthorizeDefinitionParser parser,
                                                               List<UserTokenParser> userTokenParser) {
@@ -97,15 +106,24 @@ public class AuthorizingHandlerAutoConfiguration {
     }
 
     @Bean
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public UserOnSignIn userOnSignIn(UserTokenManager userTokenManager) {
         return new UserOnSignIn(userTokenManager);
     }
 
     @Bean
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public UserOnSignOut userOnSignOut(UserTokenManager userTokenManager) {
         return new UserOnSignOut(userTokenManager);
     }
 
+    @Bean
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
+    @ConfigurationProperties(prefix = "hsweb.authorize.token.default")
+    public DefaultUserTokenGenPar defaultUserTokenGenPar(){
+        return new DefaultUserTokenGenPar();
+    }
+
     @Bean
     public AuthorizationController authorizationController() {
         return new AuthorizationController();
@@ -136,9 +154,12 @@ public class AuthorizingHandlerAutoConfiguration {
         }
     }
 
+
+
     @Configuration
     @ConditionalOnProperty(prefix = "hsweb.authorize", name = "basic-authorization", havingValue = "true")
     @ConditionalOnClass(UserTokenForTypeParser.class)
+    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
     public static class BasicAuthorizationConfiguration {
         @Bean
         public BasicAuthorizationTokenParser basicAuthorizationTokenParser(AuthenticationManager authenticationManager,

+ 1 - 1
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/configuration/BasicAuthorizationTokenParser.java

@@ -4,7 +4,7 @@ import org.apache.commons.codec.binary.Base64;
 import org.hswebframework.web.authorization.Authentication;
 import org.hswebframework.web.authorization.AuthenticationManager;
 import org.hswebframework.web.authorization.basic.web.AuthorizedToken;
-import org.hswebframework.web.authorization.basic.web.ParsedToken;
+import org.hswebframework.web.authorization.token.ParsedToken;
 import org.hswebframework.web.authorization.basic.web.UserTokenForTypeParser;
 import org.hswebframework.web.authorization.simple.PlainTextUsernamePasswordAuthenticationRequest;
 import org.hswebframework.web.authorization.token.UserToken;

+ 2 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizationController.java

@@ -63,8 +63,8 @@ public class AuthorizationController {
     @PostMapping(value = "/login", consumes = MediaType.APPLICATION_JSON_VALUE)
     @ApiOperation("用户名密码登录,json方式")
     public Mono<Map<String, Object>> authorizeByJson(@ApiParam(example = "{\"username\":\"admin\",\"password\":\"admin\"}")
-                                                     @RequestBody Mono<Map<String, Object>> parameter) {
-        return doLogin(parameter);
+                                                     @RequestBody Map<String, Object> parameter) {
+        return doLogin(Mono.just(parameter));
     }
 
     @PostMapping(value = "/login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)

+ 2 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/AuthorizedToken.java

@@ -1,5 +1,7 @@
 package org.hswebframework.web.authorization.basic.web;
 
+import org.hswebframework.web.authorization.token.ParsedToken;
+
 /**
  * 已完成认证的令牌,如果返回此令牌,将直接使用{@link this#getUserId()}来绑定用户信息
  *

+ 75 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/DefaultUserTokenGenPar.java

@@ -0,0 +1,75 @@
+package org.hswebframework.web.authorization.basic.web;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.hswebframework.web.id.IDGenerator;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Getter
+@Setter
+public class DefaultUserTokenGenPar implements ReactiveUserTokenGenerator, ReactiveUserTokenParser {
+
+    private long timeout = TimeUnit.MINUTES.toMillis(30);
+
+    private String headerName = "X-Access-Token";
+
+    @Override
+    public String getTokenType() {
+        return "default";
+    }
+
+    @Override
+    public GeneratedToken generate(Authentication authentication) {
+        String token = IDGenerator.MD5.generate();
+
+        return new GeneratedToken() {
+            @Override
+            public Map<String, Object> getResponse() {
+                return Collections.singletonMap("expires", timeout);
+            }
+
+            @Override
+            public String getToken() {
+                return token;
+            }
+
+            @Override
+            public String getType() {
+                return getTokenType();
+            }
+
+            @Override
+            public long getTimeout() {
+                return timeout;
+            }
+        };
+    }
+
+    @Override
+    public Mono<ParsedToken> parseToken(ServerWebExchange exchange) {
+        String token =exchange.getRequest()
+                .getHeaders()
+                .getFirst(headerName);
+        if(token==null){
+            return Mono.empty();
+        }
+        return Mono.just(new ParsedToken() {
+            @Override
+            public String getToken() {
+                return token;
+            }
+
+            @Override
+            public String getType() {
+                return getTokenType();
+            }
+        });
+    }
+}

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

@@ -29,5 +29,5 @@ public interface GeneratedToken extends Serializable {
     /**
      * @return 令牌有效期(单位毫秒)
      */
-    int getTimeout();
+    long getTimeout();
 }

+ 10 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ReactiveUserTokenGenerator.java

@@ -0,0 +1,10 @@
+package org.hswebframework.web.authorization.basic.web;
+
+import org.hswebframework.web.authorization.Authentication;
+
+public interface ReactiveUserTokenGenerator {
+
+    String getTokenType();
+
+    GeneratedToken generate(Authentication authentication);
+}

+ 9 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/ReactiveUserTokenParser.java

@@ -0,0 +1,9 @@
+package org.hswebframework.web.authorization.basic.web;
+
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+public interface ReactiveUserTokenParser {
+    Mono<ParsedToken> parseToken(ServerWebExchange exchange);
+}

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

@@ -50,7 +50,7 @@ public class SessionIdUserTokenGenerator implements UserTokenGenerator, Serializ
             }
 
             @Override
-            public int getTimeout() {
+            public long getTimeout() {
                 return timeout;
             }
         };

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

@@ -1,12 +1,12 @@
 package org.hswebframework.web.authorization.basic.web;
 
+import org.hswebframework.web.authorization.token.ParsedToken;
 import org.hswebframework.web.authorization.token.UserToken;
 import org.hswebframework.web.authorization.token.UserTokenManager;
 import org.springframework.beans.factory.annotation.Autowired;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
-import java.util.function.Predicate;
 
 import static org.hswebframework.web.authorization.basic.web.UserTokenGenerator.TOKEN_TYPE_SESSION_ID;
 

+ 3 - 2
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserOnSignIn.java

@@ -57,7 +57,7 @@ public class UserOnSignIn implements ApplicationListener<AuthorizationSuccessEve
 
         if (token != null) {
             //先退出已登陆的用户
-            userTokenManager.signOutByToken(token.getToken());
+            userTokenManager.signOutByToken(token.getToken()).block();
         }
         //创建token
         GeneratedToken newToken = userTokenGenerators.stream()
@@ -66,7 +66,8 @@ public class UserOnSignIn implements ApplicationListener<AuthorizationSuccessEve
                 .orElseThrow(() -> new UnsupportedOperationException(tokenType))
                 .generate(event.getAuthentication());
         //登入
-        userTokenManager.signIn(newToken.getToken(), newToken.getType(), event.getAuthentication().getUser().getId(), newToken.getTimeout());
+        userTokenManager.signIn(newToken.getToken(), newToken.getType(), event.getAuthentication().getUser().getId(), newToken.getTimeout())
+        .block();
 
         //响应结果
         event.getResult().putAll(newToken.getResponse());

+ 2 - 1
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenParser.java

@@ -1,7 +1,8 @@
 package org.hswebframework.web.authorization.basic.web;
 
+import org.hswebframework.web.authorization.token.ParsedToken;
+
 import javax.servlet.http.HttpServletRequest;
-import java.util.function.Predicate;
 
 /**
  * 令牌解析器,用于在接受到请求到时候,从请求中获取令牌

+ 76 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/web/UserTokenWebFilter.java

@@ -0,0 +1,76 @@
+package org.hswebframework.web.authorization.basic.web;
+
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.authorization.events.AuthorizationSuccessEvent;
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.context.ContextUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.event.EventListener;
+import org.springframework.lang.NonNull;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+@Slf4j
+public class UserTokenWebFilter implements WebFilter, BeanPostProcessor {
+
+    private List<ReactiveUserTokenParser> parsers = new ArrayList<>();
+
+    private Map<String, ReactiveUserTokenGenerator> tokenGeneratorMap = new HashMap<>();
+
+    @Autowired
+    private UserTokenManager userTokenManager;
+
+    @Override
+    @NonNull
+    public Mono<Void> filter(@NonNull ServerWebExchange exchange, WebFilterChain chain) {
+
+        return chain.filter(exchange)
+                .subscriberContext(ContextUtils.acceptContext(ctx ->
+                        Flux.fromIterable(parsers)
+                                .flatMap(parser -> parser.parseToken(exchange))
+                                .subscribe(token -> ctx.put(ParsedToken.class, token))));
+    }
+
+    @EventListener
+    public void handleUserSign(AuthorizationSuccessEvent event) {
+        ReactiveUserTokenGenerator generator = event.<String>getParameter("tokenType")
+                .map(tokenGeneratorMap::get)
+                .orElseGet(() -> tokenGeneratorMap.get("default"));
+        if (generator != null) {
+            GeneratedToken token = generator.generate(event.getAuthentication());
+            event.getResult().put("token", token.getToken());
+            event.getResult().putAll(token.getResponse());
+            userTokenManager.signIn(token.getToken(), token.getType(), event.getAuthentication().getUser().getId(), token.getTimeout())
+                    .subscribe(t -> {
+                        log.debug("user [{}] sign in", t.getUserId());
+                    });
+        }
+
+    }
+
+    @Override
+    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+        if (bean instanceof ReactiveUserTokenGenerator) {
+            ReactiveUserTokenGenerator generator = ((ReactiveUserTokenGenerator) bean);
+            tokenGeneratorMap.put(generator.getTokenType(), generator);
+        }
+        if (bean instanceof ReactiveUserTokenParser) {
+            parsers.add(((ReactiveUserTokenParser) bean));
+        }
+        return bean;
+    }
+}

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

@@ -2,10 +2,10 @@ package org.hswebframework.web.authorization.basic.web;
 
 import org.hswebframework.web.authorization.basic.aop.AopMethodAuthorizeDefinitionParser;
 import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+import org.hswebframework.web.authorization.token.ParsedToken;
 import org.hswebframework.web.authorization.token.UserToken;
 import org.hswebframework.web.authorization.token.UserTokenHolder;
 import org.hswebframework.web.authorization.token.UserTokenManager;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 

+ 72 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/AopAuthorizingControllerTest.java

@@ -0,0 +1,72 @@
+package org.hswebframework.web.authorization.basic.aop;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.ReactiveAuthenticationHolder;
+import org.hswebframework.web.authorization.ReactiveAuthenticationSupplier;
+import org.hswebframework.web.authorization.User;
+import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
+import org.hswebframework.web.authorization.exception.AccessDenyException;
+import org.hswebframework.web.authorization.exception.UnAuthorizedException;
+import org.hswebframework.web.authorization.simple.SimpleAuthentication;
+import org.hswebframework.web.authorization.simple.SimplePermission;
+import org.hswebframework.web.authorization.simple.SimpleUser;
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+import reactor.test.StepVerifier;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.Assert.*;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = TestApplication.class)
+public class AopAuthorizingControllerTest {
+
+    @Autowired
+    public TestController testController;
+
+    @Test
+    public void testAccessDeny() {
+
+        SimpleAuthentication authentication = new SimpleAuthentication();
+
+        authentication.setUser(SimpleUser.builder().id("test").username("test").build());
+//        authentication.setPermissions(Arrays.asList(SimplePermission.builder().id("test").build()));
+        authentication.setPermissions(Collections.emptyList());
+        ReactiveAuthenticationHolder.addSupplier(new ReactiveAuthenticationSupplier() {
+            @Override
+            public Mono<Authentication> get(String userId) {
+                return Mono.empty();
+            }
+
+            @Override
+            public Mono<Authentication> get() {
+                return Mono.just(authentication);
+            }
+        });
+
+        testController.getUser()
+                .map(User::getId)
+                .onErrorReturn(AccessDenyException.class, "403")
+                .as(StepVerifier::create)
+                .expectNext("403")
+                .verifyComplete();
+
+        testController.getUserAfter()
+                .map(User::getId)
+                .onErrorReturn(AccessDenyException.class, "403")
+                .as(StepVerifier::create)
+                .expectNext("403")
+                .verifyComplete();
+    }
+}

+ 20 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/FluxTestController.java

@@ -0,0 +1,20 @@
+package org.hswebframework.web.authorization.basic.aop;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.exception.UnAuthorizedException;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+@RestController
+@RequestMapping("/test")
+public class FluxTestController {
+
+    @GetMapping
+    public Mono<Authentication> getUser() {
+        return Authentication
+                .currentReactive()
+                .switchIfEmpty(Mono.error(UnAuthorizedException::new));
+    }
+}

+ 43 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestApplication.java

@@ -0,0 +1,43 @@
+package org.hswebframework.web.authorization.basic.aop;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.basic.configuration.EnableAopAuthorize;
+import org.hswebframework.web.authorization.basic.web.GeneratedToken;
+import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenGenerator;
+import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.hswebframework.web.id.IDGenerator;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
+import org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.reactive.config.EnableWebFlux;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import reactor.core.publisher.Mono;
+
+import java.util.Collections;
+import java.util.Map;
+
+@SpringBootApplication(exclude = {
+        WebMvcAutoConfiguration.class,
+        ServletWebServerFactoryAutoConfiguration.class,
+        DispatcherServletAutoConfiguration.class})
+@EnableAopAuthorize
+public class TestApplication {
+
+    public static void main(String[] args) {
+        SpringApplication application=new SpringApplication(TestApplication.class);
+        application.setApplicationContextClass(ReactiveWebServerApplicationContext.class);
+        application.run(args);
+    }
+
+}

+ 27 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/TestController.java

@@ -0,0 +1,27 @@
+package org.hswebframework.web.authorization.basic.aop;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.User;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.define.Phased;
+import org.hswebframework.web.authorization.exception.UnAuthorizedException;
+import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
+
+@RestController
+public class TestController {
+
+    @Authorize(permission = "test")
+    public Mono<User> getUser(){
+        return Authentication.currentReactive()
+                .switchIfEmpty(Mono.error(new UnAuthorizedException()))
+                .map(Authentication::getUser);
+    }
+
+    @Authorize(permission = "test",phased = Phased.after)
+    public Mono<User> getUserAfter(){
+        return Authentication.currentReactive()
+                .switchIfEmpty(Mono.error(new UnAuthorizedException()))
+                .map(Authentication::getUser);
+    }
+}

+ 65 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/java/org/hswebframework/web/authorization/basic/aop/WebFluxTests.java

@@ -0,0 +1,65 @@
+package org.hswebframework.web.authorization.basic.aop;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.AuthenticationManager;
+import org.hswebframework.web.authorization.User;
+import org.hswebframework.web.authorization.basic.web.GeneratedToken;
+import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenGenerator;
+import org.hswebframework.web.authorization.basic.web.ReactiveUserTokenParser;
+import org.hswebframework.web.authorization.simple.DefaultAuthorizationAutoConfiguration;
+import org.hswebframework.web.authorization.token.ParsedToken;
+import org.hswebframework.web.authorization.token.UserTokenManager;
+import org.hswebframework.web.id.IDGenerator;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.reactive.server.WebTestClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.util.Collections;
+import java.util.Map;
+
+@WebFluxTest(FluxTestController.class)
+@RunWith(SpringRunner.class)
+@Import(DefaultAuthorizationAutoConfiguration.class)
+public class WebFluxTests {
+
+    @Autowired
+    private WebTestClient client;
+
+    @Autowired
+    private UserTokenManager tokenManager;
+
+
+
+    @Test
+    public void test(){
+
+        tokenManager.signIn("test","test-token","admin",10000).block();
+
+        client.get().uri("/test")
+                .header("token","test")
+                .exchange()
+                .expectStatus()
+                .isOk();
+
+    }
+
+
+
+
+
+}

+ 9 - 0
hsweb-authorization/hsweb-authorization-basic/src/test/resources/application.yml

@@ -0,0 +1,9 @@
+hsweb:
+  users:
+    admin:
+      username: admin
+      password: admin
+      permissions-simple:
+          user-token:
+            - get
+            - update