最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

mybatis攔截器實現(xiàn)數(shù)據(jù)庫數(shù)據(jù)權(quán)限隔離方式

 更新時間:2024年11月06日 09:39:37   作者:不要停下腳步  
通過Mybatis攔截器,在執(zhí)行SQL前添加條件實現(xiàn)數(shù)據(jù)權(quán)限隔離,特別是對于存在用戶ID區(qū)分的表,攔截器會自動添加如user_id=#{userId}的條件,確保SQL在執(zhí)行時只能操作指定用戶的數(shù)據(jù),此方法主要應(yīng)用于Mybatis的四個階段

原理

使用攔截器在mybatis 執(zhí)行sql 之前 ,

將sql 后面加上指定的查詢條件 

比如,你的表以user_id 作為區(qū)分 

那么你就需要在sql 攔截器中加上 user_id = #{userId} 的邏輯

實現(xiàn)

mybatis 攔截器的相關(guān)知識不再贅述 , 可以在mybatis 的四個階段進行攔截

分別是 Execute , MappedStatment , ParamHanlder ,以及 ResultHandler

詳細的每個階段做什么事情 ,可以自行百度。

 @AuthFilter(userFiled = "user_id" , ignoreOrgFiled = true)
    Page getUserMsgPage(@Param("page")Page page , @Param("param") MsgUserRefDto param , @Param("loginId") String loginId , @Param("orderBy")String orderBy);

具體效果就是 , 我們希望上面的sql 在執(zhí)行的時候 ,自動拼接上 and user_id = 1 ,去過濾指定用戶的數(shù)據(jù)。

配置文件

@Configuration
@AutoConfigureAfter(PageHelperAutoConfiguration.class)
public class MybatisConfig {

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @PostConstruct
    void mybatisConfigurationCustomizer() {

        AuthInterceptor authInterceptor = new AuthInterceptor();
        sqlSessionFactoryList.forEach(o->{
            o.getConfiguration().addInterceptor(authInterceptor);
        });
    }
}

自定義注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD , ElementType.TYPE})
@Documented
public @interface AuthFilter {

    String userFiled() default "userId";

    String orgFiled() default "orgId";

    boolean ignoreUserFiled() default false;

    boolean ignoreOrgFiled() default false;
}

具體攔截器邏輯

其中,GlobalHolder 就是每個系統(tǒng)中自己存儲用戶登錄信息的容器 。

@Slf4j
@Component
@Intercepts({@Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
), @Signature(
        type = Executor.class,
        method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
)})
public class AuthInterceptor implements Interceptor {

   private static final Map<Class<?>, Map<String, List<List<Class>>>> mapperCache = new ConcurrentHashMap();


    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        Object[] args = invocation.getArgs();
        String id = ((MappedStatement)args[0]).getId();
        String clazzName = id.substring(0, id.lastIndexOf('.'));
        String mapperMethod = id.substring(id.lastIndexOf('.') + 1);

        Object[] paramArr = getParamArr(args[1]);
        Class<?> clazz = Class.forName(clazzName);

        Method method = getMethod(clazz, mapperMethod, paramArr);
        AuthFilter authFilter = method.getAnnotation(AuthFilter.class);


        // 如果方法沒有加上注解正常執(zhí)行 ,否則開始解析
        if (authFilter != null) {

            Map params = new HashMap();
            // 獲取各個filed
            String orgFiled = authFilter.orgFiled();
            String userFiled = authFilter.userFiled();
            // 獲取用戶登錄id 和 組織Id
            String orgId = GlobalHolder.getOrgId();
            String loginId = GlobalHolder.getLoginId();

            boolean ignoreOrgFiled = authFilter.ignoreOrgFiled();
            boolean ignoreUserFiled = authFilter.ignoreUserFiled();

            MappedStatement ms = (MappedStatement)args[0];
            Object parameter = args[1];
            BoundSql boundSql;
            if (args.length == 4) {
                boundSql = ms.getBoundSql(parameter);
            } else {
                boundSql = (BoundSql)args[5];
            }

            String sql = boundSql.getSql();

            // 添加組織編號
            if (!ignoreOrgFiled) {

                if(StringUtils.isNotEmpty(orgId)){
                    params.put(orgFiled , orgId);
                }else {
                    throw new IllegalStateException("用戶未登錄!");
                }

            }

            if (!ignoreUserFiled) {

                if(StringUtils.isNotEmpty(loginId)){
                    params.put(userFiled , loginId);
                }else {
                    throw new IllegalStateException("用戶未登錄!");
                }
            }

            if(params.size() > 0){
               String concatSql = contactConditions(wrapSql(sql) , params);
                log.info("添加后的sql為: {}" , concatSql);
                ReflectUtil.setFieldValue(boundSql, "sql", concatSql);
            }
        }
        return invocation.proceed();
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);

    }

    @Override
    public void setProperties(Properties properties) {
    }

    private String wrapSql(String sql){

        if(StringUtils.isNotEmpty(sql)){

            StringBuilder realSql = new StringBuilder();
            realSql.append("select * from ( ");
            realSql.append(sql);
            realSql.append(") a");

            return realSql.toString();
        }
        return sql;
    }

    /** 獲取 mapper 相應(yīng) Method 反射類 */
    private Method getMethod(Class<?> clazz, String mapperMethod, Object[] paramArr) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
        // 1、查 mapper 接口緩存
        if (!mapperCache.containsKey(clazz)) // mapper 沒有緩存, 就進行緩存
        {
            cacheMapper(clazz);
        }
        // 2、返回相應(yīng) method
        A:
        for (List<Class> paramList : mapperCache.get(clazz).get(mapperMethod)) {
            if (!paramList.isEmpty()) {
                for (int i = 0; i < paramArr.length; i++) { // 比較參數(shù)列表class
                    if (paramArr[i] != null)
                        if (!compareClass(paramList.get(i), paramArr[i].getClass())) continue A;
                }
                return clazz.getMethod(mapperMethod, paramList.toArray(new Class[paramList.size()]));
            }
        }
        return clazz.getMethod(mapperMethod); // 返回?zé)o參方法
    }

        /** 對 mapper 方法字段進行緩存 */
        private void cacheMapper(Class<?> clazz) {
            Map<String, List<List<Class>>> methodMap = new HashMap();
            for(Method method : clazz.getMethods()) {
                List<List<Class>> paramLists = methodMap.containsKey(method.getName()) ?
                        methodMap.get(method.getName()) : new ArrayList<List<Class>>();
                List<Class> paramClass = new ArrayList<Class>();
                for (Type type : method.getParameterTypes())
                {
                    paramClass.add((Class) type);
                }
                paramLists.add(paramClass);
                methodMap.put(method.getName(), paramLists);
            }
            mapperCache.put(clazz, methodMap);
        }

        /** class 比較 */
        private boolean compareClass(Class<?> returnType, Class<?> paramType) throws NoSuchFieldException, IllegalAccessException {
            if(returnType == paramType) {
                return true;
            }
            else if(returnType.isAssignableFrom(paramType)) { // 判斷 paramType 是否為 returnType 子類或者實現(xiàn)類
                return true;
            }
            // 基本數(shù)據(jù)類型判斷
            else if(returnType.isPrimitive()) { // paramType為包裝類
                return returnType == paramType.getField("TYPE").get(null);
            }
            else if(paramType.isPrimitive()) { // returnType為包裝類
                return paramType == returnType.getField("TYPE").get(null);
            }
            return false;
        }

    /**
     * 獲取 mybatis 中 mapper 接口的參數(shù)列表的參數(shù)值
     * @param parameter
     * @return
     */
    private Object[] getParamArr(Object parameter) {
        Object[] paramArr = null;
        // mapper 接口中使用的是 paramMap, 傳多個參數(shù)
        if(parameter instanceof MapperMethod.ParamMap)
        {
            Map map = ((Map) parameter);
            if(!map.isEmpty()) {
                StringBuilder builder = new StringBuilder();
                // 初始化 param_arr
                int size = map.size() >> 1;
                paramArr = new Object[size];
                for(int i = 1;i <= size;i ++)
                {
                    // mapper 接口中使用 param0 ~ paramN 命名參數(shù)
                    paramArr[i - 1] = map.get(builder.append("param").append(i).toString());
                    builder.setLength(0);
                }
            }
        }
        else if(parameter != null)
        {
            paramArr = new Object[1];
            paramArr[0] = parameter;
        }
        return paramArr;
    }


    private static String contactConditions(String sql, Map<String, Object> columnMap) {
        SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, JdbcUtils.MYSQL);
        List<SQLStatement> stmtList = parser.parseStatementList();
        SQLStatement stmt = stmtList.get(0);
        if (stmt instanceof SQLSelectStatement) {
            StringBuffer constraintsBuffer = new StringBuffer();
            Set<String> keys = columnMap.keySet();
            Iterator<String> keyIter = keys.iterator();
            if (keyIter.hasNext()) {
                String key = keyIter.next();
                constraintsBuffer.append(key).append(" = " + getSqlByClass(columnMap.get(key)));
            }
            while (keyIter.hasNext()) {
                String key = keyIter.next();
                constraintsBuffer.append(" AND ").append(key).append(" = " + getSqlByClass(columnMap.get(key)));
            }
            SQLExprParser constraintsParser = SQLParserUtils.createExprParser(constraintsBuffer.toString(), JdbcUtils.MYSQL);
            SQLExpr constraintsExpr = constraintsParser.expr();

            SQLSelectStatement selectStmt = (SQLSelectStatement) stmt;
            // 拿到SQLSelect
            SQLSelect sqlselect = selectStmt.getSelect();
            SQLSelectQueryBlock query = (SQLSelectQueryBlock) sqlselect.getQuery();
            SQLExpr whereExpr = query.getWhere();
            // 修改where表達式
            if (whereExpr == null) {
                query.setWhere(constraintsExpr);
            } else {
                SQLBinaryOpExpr newWhereExpr = new SQLBinaryOpExpr(whereExpr, SQLBinaryOperator.BooleanAnd, constraintsExpr);
                query.setWhere(newWhereExpr);
            }
            sqlselect.setQuery(query);
            return sqlselect.toString();

        }

        return sql;
    }

    private static String getSqlByClass(Object value){

        if(value instanceof Number){
            return value + "";
        }else if(value instanceof String){
            return "'" + value + "'";
        }

        return "'" + value.toString() + "'";
    }

}

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Maven插件構(gòu)建Docker鏡像的實現(xiàn)步驟

    Maven插件構(gòu)建Docker鏡像的實現(xiàn)步驟

    這篇文章主要介紹了Maven插件構(gòu)建Docker鏡像的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-10-10
  • JDK的下載、安裝與部署圖文教程

    JDK的下載、安裝與部署圖文教程

    這篇文章主要為大家詳細介紹了JDK的下載、安裝與部署圖文教程,感興趣的小伙伴們可以參考一下
    2016-06-06
  • JMeter配置元件詳解

    JMeter配置元件詳解

    JMeter提供了豐富的配置元件,常用的包括參數(shù)化配置元件、HTTP請求默認值、HTTP信息頭管理器、計數(shù)器等,本文就詳細的介紹一下這些元件的使用,感興趣的可以了解一下
    2021-12-12
  • Java CharacterEncodingFilter過濾器的理解和配置案例詳解

    Java CharacterEncodingFilter過濾器的理解和配置案例詳解

    這篇文章主要介紹了Java CharacterEncodingFilter過濾器的理解和配置案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 詳解Spring Security中權(quán)限注解的使用

    詳解Spring Security中權(quán)限注解的使用

    這篇文章主要為大家詳細介紹一下Spring Security中權(quán)限注解的使用方法,文中的示例代碼講解詳細,對我們學(xué)習(xí)或工作有一定參考價值,需要的可以參考一下
    2022-05-05
  • Java Swing實現(xiàn)自定義日期時間選擇器組件

    Java Swing實現(xiàn)自定義日期時間選擇器組件

    Swing 原生沒有好用的日期時間選擇器組件,如果想讓用戶選擇日期,通常只能用手寫文本框加格式校驗,代碼繁瑣且體驗一般,下面小編就和大家分享一下Java Swing實現(xiàn)自定義日期時間選擇器的相關(guān)方法吧
    2026-05-05
  • Java高效實現(xiàn)將CSV文件轉(zhuǎn)換為PDF文檔的完整解決方案

    Java高效實現(xiàn)將CSV文件轉(zhuǎn)換為PDF文檔的完整解決方案

    在企業(yè)和開發(fā)場景中,CSV 文件是最常見的數(shù)據(jù)存儲格式之一,但純文本的表格數(shù)據(jù)在展示和共享時往往缺乏美觀性和可讀性,下面我們就來看看如何使用Java實現(xiàn)將CSV文件吧轉(zhuǎn)換為PDF文檔
    2026-01-01
  • 關(guān)于apollo和Spring集成@Value注解通用解析

    關(guān)于apollo和Spring集成@Value注解通用解析

    這篇文章主要介紹了關(guān)于apollo和Spring集成@Value注解通用解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • JavaSE的三大接口:Comparator,Comparable和Cloneable詳解

    JavaSE的三大接口:Comparator,Comparable和Cloneable詳解

    這篇文章主要介紹了詳解JavaSE中Comparator,Comparable和Cloneable接口的區(qū)別的相關(guān)資料,希望通過本文大家能徹底掌握這部分內(nèi)容,需要的朋友可以參考下
    2021-10-10
  • Java實現(xiàn)汽車租賃系統(tǒng)

    Java實現(xiàn)汽車租賃系統(tǒng)

    這篇文章介紹了Java實現(xiàn)汽車租賃系統(tǒng)的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03

最新評論

泰宁县| 梧州市| 涟水县| 扬州市| 河津市| 凤凰县| 肇州县| 馆陶县| 湘潭县| 屏东市| 兰考县| 剑阁县| 甘泉县| 阜阳市| 弥渡县| 离岛区| 白城市| 任丘市| 莱阳市| 肇庆市| 昆明市| 崇阳县| 南陵县| 林州市| 奉贤区| 普陀区| 杂多县| 三门峡市| 惠州市| 班戈县| 宣武区| 昌吉市| 宁远县| 吐鲁番市| 正宁县| 新安县| 玉溪市| 灵石县| 孟州市| 盐源县| 长阳|