SoFunction
Updated on 2025-03-06

Mybatis interceptor realizes database data permission isolation method

principle

Use an interceptor before executing sql in mybatis,

Add the specified query conditions after SQL

For example, your table uses user_id as the distinction

Then you need to add the logic of user_id = #{userId} to the sql interceptor

accomplish

The relevant knowledge of mybatis interceptor will not be described in detail. You can intercept in the four stages of mybatis

Execute , MappedStatment , ParamHanlder , and ResultHandler

You can do something in detail at each stage by Baidu.

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

The specific effect is that we hope that the above sql will be automatically spliced ​​with and user_id = 1 when executing to filter the data of the specified user.

Configuration File

@Configuration
@AutoConfigureAfter()
public class MybatisConfig {

    @Autowired
    private List<SqlSessionFactory> sqlSessionFactoryList;

    @PostConstruct
    void mybatisConfigurationCustomizer() {

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

Custom annotations

@Retention()
@Target({ , })
@Documented
public @interface AuthFilter {

    String userFiled() default "userId";

    String orgFiled() default "orgId";

    boolean ignoreUserFiled() default false;

    boolean ignoreOrgFiled() default false;
}

Specific interceptor logic

Among them, GlobalHolder is the container in each system that stores user login information.

@Slf4j
@Component
@Intercepts({@Signature(
        type = ,
        method = "query",
        args = {, , , }
), @Signature(
        type = ,
        method = "query",
        args = {, , , , , }
)})
public class AuthInterceptor implements Interceptor {

   private static final Map&lt;Class&lt;?&gt;, Map&lt;String, List&lt;List&lt;Class&gt;&gt;&gt;&gt; mapperCache = new ConcurrentHashMap();


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

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

        Object[] paramArr = getParamArr(args[1]);
        Class&lt;?&gt; clazz = (clazzName);

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


        // If the method does not add annotation, execute normally, otherwise start parsing        if (authFilter != null) {

            Map params = new HashMap();
            // Get each filed            String orgFiled = ();
            String userFiled = ();
            // Get the user login id and organization ID            String orgId = ();
            String loginId = ();

            boolean ignoreOrgFiled = ();
            boolean ignoreUserFiled = ();

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

            String sql = ();

            // Add organization number            if (!ignoreOrgFiled) {

                if((orgId)){
                    (orgFiled , orgId);
                }else {
                    throw new IllegalStateException("The user is not logged in!");
                }

            }

            if (!ignoreUserFiled) {

                if((loginId)){
                    (userFiled , loginId);
                }else {
                    throw new IllegalStateException("The user is not logged in!");
                }
            }

            if(() &gt; 0){
               String concatSql = contactConditions(wrapSql(sql) , params);
                ("Addedsqlfor: {}" , concatSql);
                (boundSql, "sql", concatSql);
            }
        }
        return ();
    }


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

    }

    @Override
    public void setProperties(Properties properties) {
    }

    private String wrapSql(String sql){

        if((sql)){

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

            return ();
        }
        return sql;
    }

    /** Get the corresponding Method reflection class of mapper */
    private Method getMethod(Class&lt;?&gt; clazz, String mapperMethod, Object[] paramArr) throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
        // 1. Check the mapper interface cache        if (!(clazz)) // Mapper does not have cache, so it is cached        {
            cacheMapper(clazz);
        }
        // 2. Return to the corresponding method        A:
        for (List&lt;Class&gt; paramList : (clazz).get(mapperMethod)) {
            if (!()) {
                for (int i = 0; i &lt; ; i++) { // Compare parameter list class                    if (paramArr[i] != null)
                        if (!compareClass((i), paramArr[i].getClass())) continue A;
                }
                return (mapperMethod, (new Class[()]));
            }
        }
        return (mapperMethod); // Return to the method without parameters    }

        /** Cache mapper method fields */
        private void cacheMapper(Class&lt;?&gt; clazz) {
            Map&lt;String, List&lt;List&lt;Class&gt;&gt;&gt; methodMap = new HashMap();
            for(Method method : ()) {
                List&lt;List&lt;Class&gt;&gt; paramLists = (()) ?
                        (()) : new ArrayList&lt;List&lt;Class&gt;&gt;();
                List&lt;Class&gt; paramClass = new ArrayList&lt;Class&gt;();
                for (Type type : ())
                {
                    ((Class) type);
                }
                (paramClass);
                ((), paramLists);
            }
            (clazz, methodMap);
        }

        /** class comparison */
        private boolean compareClass(Class&lt;?&gt; returnType, Class&lt;?&gt; paramType) throws NoSuchFieldException, IllegalAccessException {
            if(returnType == paramType) {
                return true;
            }
            else if((paramType)) { // Determine whether paramType is a returnType subclass or implementation class                return true;
            }
            // Basic data type judgment            else if(()) { // paramType is the packaging class                return returnType == ("TYPE").get(null);
            }
            else if(()) { // returnType is the wrapper class                return paramType == ("TYPE").get(null);
            }
            return false;
        }

    /**
      * Get the parameter value of the parameter list of the mapper interface in mybatis
      * @param parameter
      * @return
      */
    private Object[] getParamArr(Object parameter) {
        Object[] paramArr = null;
        // The mapper interface uses paramMap, passing multiple parameters        if(parameter instanceof )
        {
            Map map = ((Map) parameter);
            if(!()) {
                StringBuilder builder = new StringBuilder();
                // Initialize param_arr                int size = () &gt;&gt; 1;
                paramArr = new Object[size];
                for(int i = 1;i &lt;= size;i ++)
                {
                    // Use param0 ~ paramN to name parameters in the mapper interface                    paramArr[i - 1] = (("param").append(i).toString());
                    (0);
                }
            }
        }
        else if(parameter != null)
        {
            paramArr = new Object[1];
            paramArr[0] = parameter;
        }
        return paramArr;
    }


    private static String contactConditions(String sql, Map&lt;String, Object&gt; columnMap) {
        SQLStatementParser parser = (sql, );
        List&lt;SQLStatement&gt; stmtList = ();
        SQLStatement stmt = (0);
        if (stmt instanceof SQLSelectStatement) {
            StringBuffer constraintsBuffer = new StringBuffer();
            Set&lt;String&gt; keys = ();
            Iterator&lt;String&gt; keyIter = ();
            if (()) {
                String key = ();
                (key).append(" = " + getSqlByClass((key)));
            }
            while (()) {
                String key = ();
                (" AND ").append(key).append(" = " + getSqlByClass((key)));
            }
            SQLExprParser constraintsParser = ((), );
            SQLExpr constraintsExpr = ();

            SQLSelectStatement selectStmt = (SQLSelectStatement) stmt;
            // Get SQLSelect            SQLSelect sqlselect = ();
            SQLSelectQueryBlock query = (SQLSelectQueryBlock) ();
            SQLExpr whereExpr = ();
            // Modify where expression            if (whereExpr == null) {
                (constraintsExpr);
            } else {
                SQLBinaryOpExpr newWhereExpr = new SQLBinaryOpExpr(whereExpr, , constraintsExpr);
                (newWhereExpr);
            }
            (query);
            return ();

        }

        return sql;
    }

    private static String getSqlByClass(Object value){

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

        return "'" + () + "'";
    }

}

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.