Browse Source

数据权限初期版本

NorthLan 6 years ago
parent
commit
9233351f03

+ 0 - 35
zen-api/src/main/kotlin/com/gxzc/zen/constants/DAConstants.kt

@@ -1,35 +0,0 @@
-package com.gxzc.zen.constants
-
-/**
- * 数据权限相关常量
- * @author NorthLan
- * @date 2018/7/11
- * @url https://noahlan.com
- */
-object DAConstants {
-
-    /**
-     * 数权总开关
-     */
-    const val DA_ENABLED = "da_enabled"
-
-    /**
-     * 按人员分配的数权开关
-     */
-    const val DA_MEMBER_ENABLED = "da_member_enabled"
-
-    /**
-     * 按角色分配的数权开关
-     */
-    const val DA_ROLE_ENABLED = "da_role_enabled"
-
-    /**
-     * 按部门分配的数权开关
-     */
-    const val DA_DEPT_ENABLED = "da_dept_enabled"
-
-    /**
-     * 全宗业务数权开关
-     */
-    const val DA_BIZ_FONDS_ENABLED = "da_biz_fonds_enabled"
-}

+ 36 - 3
zen-core/src/main/kotlin/com/gxzc/zen/common/contants/ZenConstants.kt

@@ -7,12 +7,45 @@ package com.gxzc.zen.common.contants
  * @url https://noahlan.com
  */
 object ZenConstants {
+    /*-------------------------------------------
+    |                树 参 数                    |
+    ============================================*/
     const val TREE_ROOT_PID = 0L
     const val TREE_PATH_SEPARATOR = "-"
-    // 系统缓存key
-    const val CACHE_KEY_SYS = "sys"
 
-    // true / false
+    /*-------------------------------------------
+    |                框架所需参数                 |
+    ============================================*/
+    const val CACHE_KEY_SYS = "sys"     // 系统缓存key
     const val TRUE = "1"
     const val FALSE = "0"
+
+    /*-------------------------------------------
+    |              系 统 参 数 KEY               |
+    ============================================*/
+    final val PARAMKEY_SUPER_ACCOUNT = "superAccount"
+    /**
+     * 系统数据权限开关
+     */
+    final val PARAMKEY_SYS_DA_ENABLED = "sysDAEnabled"
+
+    /**
+     * 用户数据权限开关
+     */
+    final val PARAMKEY_USER_DA_ENABLED = "userDAEnabled"
+
+    /**
+     * 角色数据权限开关
+     */
+    final val PARAMKEY_ROLE_DA_ENABLED = "roleDAEnabled"
+
+    /**
+     * 部门数据权限开关
+     */
+    final val PARAMKEY_DEPT_DA_ENABLED = "deptDAEnabled"
+
+    /**
+     * 岗位数据权限开关
+     */
+    final val PARAMKEY_POST_DA_ENABLED = "postDAEnabled"
 }

+ 30 - 0
zen-web/src/main/kotlin/com/gxzc/zen/orm/data/authority/DAHelper.kt

@@ -0,0 +1,30 @@
+package com.gxzc.zen.orm.data.authority
+
+import org.slf4j.LoggerFactory
+
+/**
+ * 数据权限辅助类(适用于全局)
+ * @author NorthLan
+ * @date 2018/7/16
+ * @url https://noahlan.com
+ */
+object DAHelper {
+    private val log = LoggerFactory.getLogger(DAHelper::class.java)
+
+    /**
+     * 数据权限 线程局部变量
+     */
+    private val LOCAL_DA = ThreadLocal<DataAuthority>()
+
+    fun getDA(): DataAuthority? {
+        return LOCAL_DA.get()
+    }
+
+    fun startDA(dataAuthority: DataAuthority) {
+        LOCAL_DA.set(dataAuthority)
+    }
+
+    fun remove() {
+        LOCAL_DA.remove()
+    }
+}

+ 24 - 0
zen-web/src/main/kotlin/com/gxzc/zen/orm/data/authority/DataAuthority.kt

@@ -0,0 +1,24 @@
+package com.gxzc.zen.orm.data.authority
+
+/**
+ * 数据权限 包装类
+ * @author NorthLan
+ * @date 2018/7/16
+ * @url https://noahlan.com
+ */
+open class DataAuthority {
+    /**
+     * 是否启用系统 数权
+     */
+    var sysDAEnabled: Boolean = false
+
+    /**
+     * 是否启用业务 数权
+     */
+    var bizDAEnabled: Boolean = false
+
+    /**
+     * 业务数权 需要过滤的createBy
+     */
+    var createByList: String? = null
+}

+ 43 - 0
zen-web/src/main/kotlin/com/gxzc/zen/orm/data/authority/DataAuthorityBuilder.kt

@@ -0,0 +1,43 @@
+package com.gxzc.zen.orm.data.authority
+
+/**
+ * 数据权限 构建器
+ * @author NorthLan
+ * @date 2018/7/25
+ * @url https://noahlan.com
+ */
+open class DataAuthorityBuilder {
+    private val da: DataAuthority = DataAuthority()
+
+    fun createByList(createByList: String): DataAuthorityBuilder {
+        da.createByList = createByList
+        configBizDA()
+        return this
+    }
+
+    fun createByList(createByList: Collection<String>): DataAuthorityBuilder {
+        da.createByList = createByList.joinToString(",")
+        configBizDA()
+        return this
+    }
+
+    fun systemDA(enabled: Boolean): DataAuthorityBuilder {
+        da.sysDAEnabled = enabled
+        return this
+    }
+
+    fun bizDA(enabled: Boolean): DataAuthorityBuilder {
+        da.bizDAEnabled = enabled
+        return this
+    }
+
+    fun build(): DataAuthority {
+        return da
+    }
+
+    private fun configBizDA() {
+        if (!da.createByList.isNullOrEmpty()) {
+            da.bizDAEnabled = true
+        }
+    }
+}

+ 148 - 0
zen-web/src/main/kotlin/com/gxzc/zen/orm/data/authority/interceptor/ZenDataAuthorityInterceptor.kt

@@ -0,0 +1,148 @@
+package com.gxzc.zen.orm.data.authority.interceptor
+
+import com.baomidou.mybatisplus.plugins.SqlParserHandler
+import com.baomidou.mybatisplus.toolkit.PluginUtils
+import com.gxzc.zen.api.util.SysParamUtil
+import com.gxzc.zen.common.contants.ZenConstants
+import com.gxzc.zen.orm.data.authority.DAHelper
+import com.gxzc.zen.orm.data.authority.visitor.DataAuthoritySelectVisitor
+import com.gxzc.zen.umps.util.SSOUtil
+import net.sf.jsqlparser.parser.CCJSqlParserManager
+import net.sf.jsqlparser.statement.select.Select
+import org.apache.ibatis.executor.statement.StatementHandler
+import org.apache.ibatis.mapping.BoundSql
+import org.apache.ibatis.mapping.MappedStatement
+import org.apache.ibatis.mapping.SqlCommandType
+import org.apache.ibatis.plugin.*
+import org.apache.ibatis.reflection.SystemMetaObject
+import org.slf4j.LoggerFactory
+import java.io.StringReader
+import java.sql.Connection
+import java.util.*
+
+/**
+ * 数据权限 <br>
+ * Sql拦截器 <br>
+ *     拦截 SELECT <br>
+ *     注入查询条件
+ * @author NorthLan
+ * @date 2018/7/6
+ * @url https://noahlan.com
+ */
+@Intercepts(Signature(type = StatementHandler::class, method = "prepare", args = [Connection::class, java.lang.Integer::class]))
+open class ZenDataAuthorityInterceptor : SqlParserHandler(), Interceptor {
+    companion object {
+        private val log = LoggerFactory.getLogger(ZenDataAuthorityInterceptor::class.java)
+    }
+
+    private val parserManager = CCJSqlParserManager()
+
+    override fun intercept(invocation: Invocation): Any {
+        if (!validate()) {
+            return invocation.proceed()
+        }
+
+
+        // 未登录,不执行数权拦截
+        if (!SSOUtil.isLogin()) {
+            return invocation.proceed()
+        }
+
+        // 当前账户名
+        val currentAccount = SSOUtil.getCurAccount()
+        if (currentAccount.isNullOrEmpty()) {
+            return invocation.proceed()
+        }
+
+        // 权限总开关
+        if (ZenConstants.FALSE == SysParamUtil.getByKey(ZenConstants.PARAMKEY_SYS_DA_ENABLED)?.value) {
+            return invocation.proceed()
+        }
+
+        // 超级管理员
+        val superAccountList = SysParamUtil.getByKey(ZenConstants.PARAMKEY_SUPER_ACCOUNT)?.value
+                ?: return invocation.proceed()
+        if (currentAccount!! in superAccountList) {
+            return invocation.proceed()
+        }
+
+        // ThreadLocal 先序判定是否启用 系统 数据权限
+        val da = DAHelper.getDA()
+        if (da != null) {
+            if (!da.sysDAEnabled) {
+
+            }
+        }
+
+
+        // 当前操作员的数据权限(人员+部门+角色)(业务数权交由后续链处理)
+
+        val statementHandler = PluginUtils.realTarget(invocation.target) as StatementHandler
+        val metaObject = SystemMetaObject.forObject(statementHandler)
+        this.sqlParser(metaObject)
+        // 获取SQL操作类型
+        val mappedStatement = metaObject.getValue("delegate.mappedStatement") as MappedStatement
+
+        val boundSql = metaObject.getValue("delegate.boundSql") as BoundSql
+        val originalSql = boundSql.sql
+
+        when (mappedStatement.sqlCommandType) {
+            SqlCommandType.SELECT -> {
+                // 仅查询 本部门+子部门 + 特殊 人员创建的数据
+                val select = parserManager.parse(StringReader(originalSql)) as Select
+                select.selectBody.accept(DataAuthoritySelectVisitor())
+                metaObject.setValue("delegate.boundSql.sql", select.toString())
+            }
+            SqlCommandType.DELETE,
+            SqlCommandType.INSERT,
+            SqlCommandType.UPDATE -> {
+                // 判断权限是否足够
+            }
+            else -> {
+                return invocation.proceed()
+            }
+        }
+        return invocation.proceed()
+    }
+
+    /**
+     * 数据权限 断言验证
+     */
+    private fun validate(): Boolean {
+        // 未登录,不执行数权拦截
+        if (!SSOUtil.isLogin()) {
+            return false
+        }
+
+        // 当前账户名
+        val currentAccount = SSOUtil.getCurAccount()
+        if (currentAccount.isNullOrEmpty()) {
+            return false
+        }
+
+        // 权限总开关
+        if (ZenConstants.FALSE == SysParamUtil.getByKey(ZenConstants.PARAMKEY_SYS_DA_ENABLED)?.value) {
+            return false
+        }
+
+        // 超级管理员
+        val superAccountList = SysParamUtil.getByKey(ZenConstants.PARAMKEY_SUPER_ACCOUNT)?.value
+                ?: return false
+        if (currentAccount!! in superAccountList) {
+            return false
+        }
+
+        return true
+    }
+
+    override fun plugin(target: Any): Any {
+        return if (target is StatementHandler) {
+            Plugin.wrap(target, this)
+        } else {
+            target
+        }
+    }
+
+    override fun setProperties(properties: Properties) {
+    }
+}

+ 43 - 0
zen-web/src/main/kotlin/com/gxzc/zen/orm/data/authority/visitor/DataAuthoritySelectVisitor.kt

@@ -0,0 +1,43 @@
+package com.gxzc.zen.orm.data.authority.visitor
+
+import net.sf.jsqlparser.expression.StringValue
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList
+import net.sf.jsqlparser.expression.operators.relational.InExpression
+import net.sf.jsqlparser.schema.Column
+import net.sf.jsqlparser.statement.select.PlainSelect
+import net.sf.jsqlparser.statement.select.SelectVisitor
+import net.sf.jsqlparser.statement.select.SetOperationList
+import net.sf.jsqlparser.statement.select.WithItem
+
+/**
+ * 数据权限 Select Visitor (处理sql)
+ * @author NorthLan
+ * @date 2018/7/9
+ * @url https://noahlan.com
+ */
+class DataAuthoritySelectVisitor : SelectVisitor {
+
+    override fun visit(plainSelect: PlainSelect) {
+        val inExp = InExpression().apply {
+            this.leftExpression = Column("create_by")
+            this.rightItemsList = ExpressionList(StringValue("admin"), StringValue("haha"))
+        }
+
+        if (plainSelect.where == null) {
+            plainSelect.where = inExp
+        } else {
+            plainSelect.where = AndExpression(plainSelect.where, inExp)
+        }
+    }
+
+    override fun visit(setOpList: SetOperationList) {
+        setOpList.selects.forEach {
+            it.accept(DataAuthoritySelectVisitor())
+        }
+    }
+
+    override fun visit(withItem: WithItem) {
+        withItem.selectBody.accept(DataAuthoritySelectVisitor())
+    }
+}