Bläddra i källkod

增加基本权限控制实现

zhouhao 7 år sedan
förälder
incheckning
0dd2540580

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

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>hsweb-authorization</artifactId>
+        <groupId>org.hswebframework.web</groupId>
+        <version>3.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>hsweb-authorization-basic</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-authorization-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hswebframework</groupId>
+            <artifactId>hsweb-expands-script</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 65 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/AnnotationAuthorizeDefinitionParser.java

@@ -0,0 +1,65 @@
+package org.hswebframework.web.authorization.basic.define;
+
+import org.hswebframework.web.AopUtils;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
+import org.hswebframework.web.authorization.annotation.RequiresExpression;
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 注解权限控制定义解析器,通过判断方法上的注解来获取权限控制的方式
+ * @author zhouhao
+ * @see AopMethodAuthorizeDefinitionParser
+ * @see AuthorizeDefinition
+ */
+
+public class AnnotationAuthorizeDefinitionParser implements AopMethodAuthorizeDefinitionParser {
+
+    private Map<Method,AuthorizeDefinition> cache=new ConcurrentHashMap<>();
+
+    @Override
+    public AuthorizeDefinition parse(MethodInterceptorParamContext paramContext) {
+
+        AuthorizeDefinition definition = cache.get(paramContext.getMethod());
+        if(definition!=null)return definition instanceof EmptyAuthorizeDefinition ?null:definition;
+
+
+        Authorize classAuth= AopUtils.findAnnotation(paramContext.getTarget().getClass(),Authorize.class);
+        Authorize methodAuth=AopUtils.findMethodAnnotation(paramContext.getTarget().getClass(),paramContext.getMethod(),Authorize.class);
+        RequiresDataAccess classDataAccess=AopUtils.findAnnotation(paramContext.getTarget().getClass(),RequiresDataAccess.class);
+        RequiresDataAccess methodDataAccess=AopUtils.findMethodAnnotation(paramContext.getTarget().getClass(),paramContext.getMethod(),RequiresDataAccess.class);
+
+        RequiresExpression expression=AopUtils.findAnnotation(paramContext.getTarget().getClass(),RequiresExpression.class);
+
+        if(classAuth==null&&methodAuth==null&&classDataAccess==null&&methodDataAccess==null&&expression==null){
+            cache.put(paramContext.getMethod(), EmptyAuthorizeDefinition.instance);
+            return null;
+        }
+
+        if(methodAuth!=null&&methodAuth.ignore()){
+            cache.put(paramContext.getMethod(), EmptyAuthorizeDefinition.instance);
+            return null;
+        }
+
+
+        DefaultBasicAuthorizeDefinition authorizeDefinition=new DefaultBasicAuthorizeDefinition();
+
+        authorizeDefinition.put(classAuth);
+        authorizeDefinition.put(methodAuth);
+
+        authorizeDefinition.put(expression);
+
+        authorizeDefinition.put(classDataAccess);
+        authorizeDefinition.put(methodDataAccess);
+
+        cache.put(paramContext.getMethod(),authorizeDefinition);
+
+        return authorizeDefinition;
+    }
+
+}

+ 3 - 2
hsweb-authorization/hsweb-authorization-api/src/main/java/org/hswebframework/web/authorization/define/AuthorizeDefinitionParser.java

@@ -1,5 +1,6 @@
-package org.hswebframework.web.authorization.define;
+package org.hswebframework.web.authorization.basic.define;
 
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
 import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
 
 /**
@@ -8,7 +9,7 @@ import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
  * @author zhouhao
  * @see AuthorizeDefinition
  */
-public interface AuthorizeDefinitionParser {
+public interface AopMethodAuthorizeDefinitionParser {
 
     /**
      * 解析权限控制定义

+ 155 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultBasicAuthorizeDefinition.java

@@ -0,0 +1,155 @@
+package org.hswebframework.web.authorization.basic.define;
+
+import org.hswebframework.web.authorization.access.DataAccessController;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.Logical;
+import org.hswebframework.web.authorization.annotation.RequiresDataAccess;
+import org.hswebframework.web.authorization.annotation.RequiresExpression;
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+import org.hswebframework.web.authorization.define.DataAccessDefinition;
+import org.hswebframework.web.authorization.define.Script;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Created by zhouhao on 2017/8/13.
+ */
+public class DefaultBasicAuthorizeDefinition implements AuthorizeDefinition {
+    private boolean dataAccessControl;
+
+    private Set<String> permissions = new HashSet<>();
+
+    private Set<String> actions = new HashSet<>();
+
+    private Set<String> roles = new HashSet<>();
+
+    private Set<String> user = new HashSet<>();
+
+    private Script script;
+
+    private String message = "{un_authorized}";
+
+    private Logical logical = Logical.DEFAULT;
+
+    private DataAccessDefinition dataAccessDefinition;
+
+    @Override
+    public int getPriority() {
+        return Integer.MIN_VALUE;
+    }
+
+    @Override
+    public boolean isDataAccessControl() {
+        return dataAccessControl;
+    }
+
+    @Override
+    public Set<String> getPermissions() {
+        return new HashSet<>(permissions);
+    }
+
+    @Override
+    public Set<String> getActions() {
+        return new HashSet<>(actions);
+    }
+
+    @Override
+    public Set<String> getRoles() {
+        return new HashSet<>(roles);
+    }
+
+    @Override
+    public Set<String> getUser() {
+        return new HashSet<>(user);
+    }
+
+    @Override
+    public Script getScript() {
+        return script;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    @Override
+    public Logical getLogical() {
+        return logical;
+    }
+
+    @Override
+    public DataAccessDefinition getDataAccessDefinition() {
+        return dataAccessDefinition;
+    }
+
+    public void setDataAccessDefinition(DataAccessDefinition dataAccessDefinition) {
+        this.dataAccessDefinition = dataAccessDefinition;
+    }
+
+    public void setActions(Set<String> actions) {
+        this.actions = actions;
+    }
+
+    public void setDataAccessControl(boolean dataAccessControl) {
+        this.dataAccessControl = dataAccessControl;
+    }
+
+    public void setLogical(Logical logical) {
+        this.logical = logical;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    public void setPermissions(Set<String> permissions) {
+        this.permissions = permissions;
+    }
+
+    public void setRoles(Set<String> roles) {
+        this.roles = roles;
+    }
+
+    public void setScript(Script script) {
+        this.script = script;
+    }
+
+    public void setUser(Set<String> user) {
+        this.user = user;
+    }
+
+    void put(Authorize authorize) {
+        permissions.addAll(Arrays.asList(authorize.permission()));
+        actions.addAll(Arrays.asList(authorize.action()));
+        roles.addAll(Arrays.asList(authorize.role()));
+        user.addAll(Arrays.asList(authorize.user()));
+        if (authorize.logical() != Logical.DEFAULT) {
+            logical = authorize.logical();
+        }
+        message = authorize.message();
+    }
+
+    void put(RequiresExpression expression) {
+        script = new DefaultScript(expression.language(), expression.value());
+    }
+
+    void put(RequiresDataAccess dataAccess) {
+        if (!dataAccess.permission().equals("")) {
+            permissions.add(dataAccess.permission());
+        }
+        actions.addAll(Arrays.asList(dataAccess.action()));
+        DefaultDataAccessDefinition definition = new DefaultDataAccessDefinition();
+
+        if (!"".equals(dataAccess.controllerBeanName())) {
+            definition.setController(dataAccess.controllerBeanName());
+        } else if (DataAccessController.class != dataAccess.controllerClass()) {
+            definition.setController(dataAccess.getClass().getName());
+        }
+        dataAccessDefinition=definition;
+    }
+
+
+}

+ 30 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultDataAccessDefinition.java

@@ -0,0 +1,30 @@
+package org.hswebframework.web.authorization.basic.define;
+
+import org.hswebframework.web.authorization.define.DataAccessDefinition;
+
+/**
+ * @author zhouhao
+ */
+public class DefaultDataAccessDefinition implements DataAccessDefinition {
+
+    private String controller;
+
+    private String idParameterName="id";
+    @Override
+    public String getController() {
+        return controller;
+    }
+
+    @Override
+    public String getIdParameterName() {
+        return idParameterName;
+    }
+
+    public void setController(String controller) {
+        this.controller = controller;
+    }
+
+    public void setIdParameterName(String idParameterName) {
+        this.idParameterName = idParameterName;
+    }
+}

+ 38 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/DefaultScript.java

@@ -0,0 +1,38 @@
+package org.hswebframework.web.authorization.basic.define;
+
+import org.hswebframework.web.authorization.define.Script;
+
+/**
+ * @author zhouhao
+ */
+public class DefaultScript implements Script {
+    private String language;
+
+    private String script;
+
+    public DefaultScript() {
+    }
+
+    public DefaultScript(String language, String script) {
+        this.language = language;
+        this.script = script;
+    }
+
+    @Override
+    public String getLanguage() {
+        return language;
+    }
+
+    @Override
+    public String getScript() {
+        return script;
+    }
+
+    public void setScript(String script) {
+        this.script = script;
+    }
+
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+}

+ 66 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/define/EmptyAuthorizeDefinition.java

@@ -0,0 +1,66 @@
+package org.hswebframework.web.authorization.basic.define;
+
+import org.hswebframework.web.authorization.annotation.Logical;
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+import org.hswebframework.web.authorization.define.DataAccessDefinition;
+import org.hswebframework.web.authorization.define.Script;
+
+import java.util.Set;
+
+/**
+ * @author zhouhao
+ */
+public class EmptyAuthorizeDefinition implements AuthorizeDefinition {
+
+    public static final EmptyAuthorizeDefinition instance=new EmptyAuthorizeDefinition();
+    private EmptyAuthorizeDefinition(){}
+    @Override
+    public int getPriority() {
+       throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isDataAccessControl() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> getPermissions() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> getActions() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> getRoles() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Set<String> getUser() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Script getScript() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public String getMessage() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Logical getLogical() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public DataAccessDefinition getDataAccessDefinition() {
+        throw new UnsupportedOperationException();
+    }
+}

+ 41 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/AuthorizingContext.java

@@ -0,0 +1,41 @@
+package org.hswebframework.web.authorization.basic.handler;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
+
+/**
+ * Created by zhouhao on 2017/8/15.
+ */
+public class AuthorizingContext {
+    private AuthorizeDefinition definition;
+
+    private Authentication authentication;
+
+    private MethodInterceptorParamContext paramContext;
+
+
+    public AuthorizeDefinition getDefinition() {
+        return definition;
+    }
+
+    public void setDefinition(AuthorizeDefinition definition) {
+        this.definition = definition;
+    }
+
+    public Authentication getAuthentication() {
+        return authentication;
+    }
+
+    public void setAuthentication(Authentication authentication) {
+        this.authentication = authentication;
+    }
+
+    public MethodInterceptorParamContext getParamContext() {
+        return paramContext;
+    }
+
+    public void setParamContext(MethodInterceptorParamContext paramContext) {
+        this.paramContext = paramContext;
+    }
+}

+ 12 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/AuthorizingHandler.java

@@ -0,0 +1,12 @@
+package org.hswebframework.web.authorization.basic.handler;
+
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+
+/**
+ * aop方式权限控制处理器
+ * @author zhouhao
+ */
+public interface AuthorizingHandler {
+    void handle(AuthorizingContext context);
+}

+ 166 - 0
hsweb-authorization/hsweb-authorization-basic/src/main/java/org/hswebframework/web/authorization/basic/handler/DefaultAuthorizingHandler.java

@@ -0,0 +1,166 @@
+package org.hswebframework.web.authorization.basic.handler;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.hswebframework.expands.script.engine.DynamicScriptEngine;
+import org.hswebframework.expands.script.engine.DynamicScriptEngineFactory;
+import org.hswebframework.web.authorization.Authentication;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.Role;
+import org.hswebframework.web.authorization.access.DataAccessConfig;
+import org.hswebframework.web.authorization.access.DataAccessController;
+import org.hswebframework.web.authorization.annotation.Logical;
+import org.hswebframework.web.authorization.define.AuthorizeDefinition;
+import org.hswebframework.web.authorization.exception.AuthorizationException;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorParamContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * @author zhouhao
+ */
+public class DefaultAuthorizingHandler implements AuthorizingHandler {
+
+    private DataAccessController dataAccessController;
+
+    private Logger logger= LoggerFactory.getLogger(this.getClass());
+
+    public void setDataAccessController(DataAccessController dataAccessController) {
+        this.dataAccessController = dataAccessController;
+    }
+
+    @Override
+    public void handle(AuthorizingContext context) {
+
+        handleRdac(context.getAuthentication(),context.getDefinition());
+
+        handleDataAccess(context.getAuthentication(),context.getDefinition(),context.getParamContext());
+
+        handleExpression(context.getAuthentication(),context.getDefinition(),context.getParamContext());
+
+    }
+
+    protected void handleDataAccess(Authentication authentication, AuthorizeDefinition definition, MethodInterceptorParamContext paramContext){
+
+        if(dataAccessController==null){
+            logger.warn("dataAccessController is null,skip data access control!");
+            return;
+        }
+        List<Permission> permission=authentication.getPermissions()
+                .stream()
+                .filter(per->definition.getPermissions().contains(per.getId()))
+                .collect(Collectors.toList());
+
+        DataAccessController finalAccessController=dataAccessController;
+
+        //取得当前登录用户持有的控制规则
+        Set<DataAccessConfig> accesses =permission
+                .stream().map(Permission::getDataAccesses)
+                .flatMap(Collection::stream)
+                .filter(access -> definition.getActions().contains(access.getAction()))
+                .collect(Collectors.toSet());
+        //无规则,则代表不进行控制
+        if (accesses.isEmpty()) return;
+        //单个规则验证函数
+        Function<Predicate<DataAccessConfig>, Boolean> function =
+                definition.getLogical() == Logical.AND ?
+                        accesses.stream()::allMatch : accesses.stream()::anyMatch;
+        //调用控制器进行验证
+        boolean isAccess = function.apply(access -> finalAccessController.doAccess(access, paramContext));
+        if (!isAccess) {
+            throw new AuthorizationException(definition.getMessage());
+        }
+
+    }
+    protected void handleExpression(Authentication authentication,AuthorizeDefinition definition, MethodInterceptorParamContext paramContext){
+        if(definition.getScript()!=null){
+            String scriptId= DigestUtils.md5Hex(definition.getScript().getScript());
+
+            DynamicScriptEngine engine = DynamicScriptEngineFactory.getEngine(definition.getScript().getLanguage());
+            if (null == engine) {
+                throw new AuthorizationException("{unknown_engine}:" + definition.getScript().getLanguage());
+            }
+            if (!engine.compiled(scriptId)) {
+                try {
+                    engine.compile(scriptId,definition.getScript().getScript());
+                } catch (Exception e) {
+                    logger.error("express compile error", e);
+                    throw new AuthorizationException("{expression_error}");
+                }
+            }
+            Map<String, Object> var = new HashMap<>(paramContext.getParams());
+            var.put("auth", authentication);
+            Object success = engine.execute(scriptId, var).get();
+            if (!(success instanceof Boolean) || !((Boolean) success)) {
+                throw new AuthorizationException(definition.getMessage());
+            }
+        }
+    }
+
+    protected void handleRdac(Authentication authentication,AuthorizeDefinition definition){
+        boolean access = true;
+        Logical logical = definition.getLogical() == Logical.DEFAULT ? Logical.OR : definition.getLogical();
+        boolean logicalIsOr = logical == Logical.OR;
+        Set<String> permissionsDef=definition.getPermissions();
+        Set<String> actionsDef=definition.getActions();
+        Set<String> rolesDef=definition.getRoles();
+        Set<String> usersDef=definition.getUser();
+
+
+        // 控制权限
+        if (!definition.getPermissions().isEmpty()) {
+            if(logger.isInfoEnabled()){
+                logger.info("do permission access handle : permissions{},actions{} ",permissionsDef,actionsDef);
+            }
+            List<Permission> permissions = authentication.getPermissions().stream()
+                    .filter(permission -> {
+                        // 未持有任何一个权限
+                        if (!permissionsDef.contains(permission.getId())) return false;
+                        //未配置action
+                        if (actionsDef.isEmpty())
+                            return true;
+                        //判断action
+                        List<String> actions = permission.getActions()
+                                .stream()
+                                .filter(rolesDef::contains)
+                                .collect(Collectors.toList());
+
+                        if (actions.isEmpty()) return false;
+
+                        //如果 控制逻辑是or,则只要过滤结果数量不为0.否则过滤结果数量必须和配置的数量相同
+                        return logicalIsOr ? actions.size() > 0 : permission.getActions().containsAll(actions);
+                    }).collect(Collectors.toList());
+            access = logicalIsOr ?
+                    permissions.size() > 0 :
+                    //权限数量和配置的数量相同
+                    permissions.size() == permissionsDef.size();
+        }
+        //控制角色
+        if (!rolesDef.isEmpty()) {
+            if(logger.isInfoEnabled()){
+                logger.info("do role access handle : roles{} ",rolesDef);
+            }
+            Function<Predicate<Role>, Boolean> func = logicalIsOr
+                    ? authentication.getRoles().stream()::anyMatch
+                    : authentication.getRoles().stream()::allMatch;
+            access = func.apply(role -> rolesDef.contains(role.getId()));
+        }
+        //控制用户
+        if (!usersDef.isEmpty()) {
+            if(logger.isInfoEnabled()){
+                logger.info("do user access handle : users{} ",usersDef);
+            }
+            Function<Predicate<String>, Boolean> func = logicalIsOr
+                    ? usersDef.stream()::anyMatch
+                    : usersDef.stream()::allMatch;
+            access = func.apply(authentication.getUser().getUsername()::equals);
+        }
+        if (!access) {
+            throw new AuthorizationException(definition.getMessage());
+        }
+    }
+}