数据权限实现
權限框架可以根據用戶所屬角色決定有權限看到的菜單資源權限。
同一個資源下的同一個菜單的數據權限需要單獨處理。
案例:一部門的張三和二部門的李四都是普通用戶角色,普通用戶都有用戶管理的查詢權限,但是,一部門的張三只能看到一部門以及一部門下面的數據的權限。
效果圖:
測試部門的test001用戶之后能看到自己部門的數據,需求部門的數據看不到
文章目錄
- 一、數據權限模型
- 1. 實現原理
- 2. 實現流程
- 3. 權限標識對象
- 4. 查詢SQL攔截器
- 5. 查詢SQL特殊處理
- 二、數據權限使用
- 2.1. 控制層
- 2.2. service
- 2.3. mapper
- 2.4. 獲取部門集合
- 2.5.
- 三、實戰演練
- 3.1. 獲取當前用戶的所屬部門
- 3.2. 獲取當前用戶的所屬部門以及子部門集合
- 3.3. 調用邏輯service層執行查詢邏輯
- 3.4. 調用mapper執行查詢邏輯
- 3.5. 在查詢數據庫之前攔截處理
- 3.6. 未處理的原sql
- 3.7. 處理后的sql
一、數據權限模型
1. 實現原理
1.創建一個需要執行數據權限的標志 2.根據數據權限標識對原查詢SQL,在查詢數據庫之前進行特殊處理 3.利用攔截器在使用mapper在查詢數據庫時進行攔截處理1>獲取所有的查詢SQL2>獲取查詢sql中的的數據權限標識無:放行有:動態拼接數據權限sql2. 實現流程
1.創建一個需要執行數據權限的標志對象DataScope 2.除超級管理員外,進行數據權限處理 3.獲取當前用戶的所處部門ID,根據部門ID獲取當前部門以及子部門的ID集合 4.創建數據范圍的攔截器DataScopeInterceptor 5.再判斷根據數據權限標識對原查詢SQL,利用攔截器在使用mapper在查詢數據庫時進行攔截處理1>獲取所有的查詢SQL2>獲取查詢sql中的的數據權限標識無:放行有:動態拼接數據權限sql簡言之:將源sql看做一個查詢整體,最后將結果集按照部門ids集合進行模糊區配篩選出(當前部門以及子部門的權限范圍)3. 權限標識對象
package cn.stylefeng.roses.core.datascope;import java.util.List;/*** 數據范圍** @author fengshuonan* @date 2017-07-23 22:19*/ public class DataScope {/*** 限制范圍的字段名稱*/private String scopeName = "deptid";/*** 具體的數據范圍*/private List<Long> deptIds;public DataScope() {}public DataScope(List<Long> deptIds) {this.deptIds = deptIds;}public DataScope(String scopeName, List<Long> deptIds) {this.scopeName = scopeName;this.deptIds = deptIds;}public List<Long> getDeptIds() {return deptIds;}public void setDeptIds(List<Long> deptIds) {this.deptIds = deptIds;}public String getScopeName() {return scopeName;}public void setScopeName(String scopeName) {this.scopeName = scopeName;} }4. 查詢SQL攔截器
5. 查詢SQL特殊處理
package cn.stylefeng.roses.core.datascope;import cn.hutool.core.collection.CollectionUtil; import com.baomidou.mybatisplus.core.toolkit.PluginUtils; 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.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection; import java.util.List; import java.util.Map; import java.util.Properties;/*** 數據范圍的攔截器** @author fengshuonan* @date 2017-07-23 21:26*/ @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class DataScopeInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {return invocation.proceed();}BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");String originalSql = boundSql.getSql();Object parameterObject = boundSql.getParameterObject();//查找參數中包含DataScope類型的參數DataScope dataScope = findDataScopeObject(parameterObject);if (dataScope == null) {return invocation.proceed();} else {String scopeName = dataScope.getScopeName();List<Long> deptIds = dataScope.getDeptIds();String join = CollectionUtil.join(deptIds, ",");originalSql = "select * from (" + originalSql + ") temp_data_scope where temp_data_scope." + scopeName + " in (" + join + ")";metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);return invocation.proceed();}}/*** 查找參數是否包括DataScope對象*/public DataScope findDataScopeObject(Object parameterObj) {if (parameterObj instanceof DataScope) {return (DataScope) parameterObj;} else if (parameterObj instanceof Map) {for (Object val : ((Map<?, ?>) parameterObj).values()) {if (val instanceof DataScope) {return (DataScope) val;}}}return null;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {} }二、數據權限使用
2.1. 控制層
/*** 查詢管理員列表** @author gblfy* @Date 2020/12/24 22:43*/@RequestMapping("/list")@Permission@ResponseBodypublic Object list(@RequestParam(required = false) String name,@RequestParam(required = false) String timeLimit,@RequestParam(required = false) Long deptId) {//拼接查詢條件String beginTime = "";String endTime = "";if (ToolUtil.isNotEmpty(timeLimit)) {String[] split = timeLimit.split(" - ");beginTime = split[0];endTime = split[1];}if (ShiroKit.isAdmin()) {Page<Map<String, Object>> users = userService.selectUsers(null, name, beginTime, endTime, deptId);Page wrapped = new UserWrapper(users).wrap();return LayuiPageFactory.createPageInfo(wrapped);} else {DataScope dataScope = new DataScope(ShiroKit.getDeptDataScope());Page<Map<String, Object>> users = userService.selectUsers(dataScope, name, beginTime, endTime, deptId);Page wrapped = new UserWrapper(users).wrap();return LayuiPageFactory.createPageInfo(wrapped);}}2.2. service
/*** 根據條件查詢用戶列表** @author gblfy* @Date 2020/12/24 22:45*/public Page<Map<String, Object>> selectUsers(DataScope dataScope, String name, String beginTime, String endTime, Long deptId) {Page page = LayuiPageFactory.defaultPage();return this.baseMapper.selectUsers(page, dataScope, name, beginTime, endTime, deptId);}2.3. mapper
/*** 根據條件查詢用戶列表*/Page<Map<String, Object>> selectUsers(@Param("page") Page page, @Param("dataScope") DataScope dataScope, @Param("name") String name, @Param("beginTime") String beginTime, @Param("endTime") String endTime, @Param("deptId") Long deptId); <select id="selectUsers" resultType="map">select<include refid="Base_Column_List"/>from sys_userwhere status != 'DELETED'<if test="name != null and name != ''">and (phone like CONCAT('%',#{name},'%')or account like CONCAT('%',#{name},'%')or name like CONCAT('%',#{name},'%'))</if><if test="deptId != null and deptId != 0">and (dept_id = #{deptId} or dept_id in ( select dept_id from sys_dept where pids like CONCAT('%$[', #{deptId}, '$]%') escape '$' ))</if><if test="beginTime != null and beginTime != '' and endTime != null and endTime != ''">and (create_time between CONCAT(#{beginTime},' 00:00:00') and CONCAT(#{endTime},' 23:59:59'))</if></select>2.4. 獲取部門集合
/*** 獲取當前用戶的部門數據范圍的集合*/public static List<Long> getDeptDataScope() {Long deptId = getUser().getDeptId();List<Long> subDeptIds = ConstantFactory.me().getSubDeptId(deptId);subDeptIds.add(deptId);return subDeptIds;}/*** 獲取子部門id*/@Overridepublic List<Long> getSubDeptId(Long deptId) {ArrayList<Long> deptIds = new ArrayList<>();if (deptId == null) {return deptIds;} else {List<Dept> depts = this.deptMapper.likePids(deptId);if (depts != null && depts.size() > 0) {for (Dept dept : depts) {deptIds.add(dept.getDeptId());}}return deptIds;}}2.5.
三、實戰演練
3.1. 獲取當前用戶的所屬部門
3.2. 獲取當前用戶的所屬部門以及子部門集合
3.3. 調用邏輯service層執行查詢邏輯
3.4. 調用mapper執行查詢邏輯
3.5. 在查詢數據庫之前攔截處理
3.6. 未處理的原sql
selectuser_id AS "userId", avatar AS "avatar", account AS "account", salt AS "salt", name AS "name", birthday AS "birthday", sex AS "sex", email AS "email", phone AS "phone", role_id AS "roleId", dept_id AS "deptId", status AS "status", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser", version AS "version"from sys_userwhere status != 'DELETED'3.7. 處理后的sql
select * from (selectuser_id AS "userId", avatar AS "avatar", account AS "account", salt AS "salt", name AS "name", birthday AS "birthday", sex AS "sex", email AS "email", phone AS "phone", role_id AS "roleId", dept_id AS "deptId", status AS "status", create_time AS "createTime", create_user AS "createUser", update_time AS "updateTime", update_user AS "updateUser", version AS "version"from sys_userwhere status != 'DELETED') temp_data_scope where temp_data_scope.deptid in (26)總結
- 上一篇: linux 统计当前目录下文件或者文件夹
- 下一篇: @Transactional和@Asyn