MyBatis组件的DAO层拦截器

最近老大想对储存到数据库的数据进行加密,不让某表的某几列以明文的形式储存,巧的是这些数据是公司框架写好的,不能直接上代码改。
而业务层对于这张表的数据,应用的地方非常多,从业务侧进行拦截不现实。
所以,决定指使本“怨种”编写 DAO 层的拦截器,对特定输入、输出的几个数据进行加解密。
…经过两天的 coding 拦截器完成度 80% 。
此时,提了一句“加密后的参数无法使用“模糊查询”,而且拦截的方法特别多”。老大大意回了句,先这样吧,弄别的先,看看别的有没有解决方案…
我…就知道~呵,DAO 层拦截器不被普遍使用是有原因的,一旦使用这项技术大概率是某个乌龙事件发生了~

查询拦截器

  1. 获取 id
  2. 比对 DAO 的全量方法名 com.xxx.xx.methedName 判断,是否是需要处理的方法
  3. 获取参数,判断、强转 参数为 bean
  4. 处理参数,参数替换(原对象的参数是不可变的需要赋值替换)
  5. 执行“业务逻辑”
  6. 处理响应
  7. 结束
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * 用户查询拦截器
 *
 */
@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}
        )
})
@Slf4j
public class UserQueryInterceptor implements Interceptor {

    /**
     * 需要拦截的 DAO 层查询方法
     * 相同 set 代表相同响应 DTO
     */
    private static final Set<String> USER_SET = new HashSet<>();

    private static final Set<String> USER_LIST_SET = new HashSet<>();

    static {
        final String basePkg = "com.xxx.xx.manage.user.core.dao.UserDao.";
        // user
        final String getDetail = basePkg + "getDetail";
        final String get = basePkg + "get";
        USER_SET.add(getDetail);
        USER_SET.add(get);
        // userList
        final String getByUserName = basePkg + "getByUserName";
        final String listUserByResourceId = basePkg + "listUserByResourceId";
        USER_LIST_SET.add(getByUserName);
        USER_LIST_SET.add(listUserByResourceId);
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.info("UserQueryInterceptor in");
        Object result = invocation.proceed();
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取执行id
        String id = mappedStatement.getId();
        log.info("id : {}", id);
        // 处理查询参数
        processQueryParams(invocation, id);
        // 处理响应
        processResult(id, result);
        log.info("UserQueryInterceptor end");
        return result;
    }

    /**
     * 处理查询参数
     *
     * @param invocation invocation
     * @param id         id
     */
    private void processQueryParams(Invocation invocation, String id) {
        //todo:加密后的参数无法使用模糊查询
    }

    /**
     * 处理响应
     *
     * @param id
     * @param result
     */
    private void processResult(String id, Object result) {
        if (ObjectUtils.isNull(result)) {
            return;
        }
        if (USER_SET.contains(id)) {
            User user = (User) result;
            decode(user);
        }
        if (USER_LIST_SET.contains(id)) {
            List<User> list = (List<User>) result;
            for (User user : list) {
                decode(user);
            }
        }
    }

    /**
     * 解密
     *
     * @param user user
     */
    private void decode(User user) {
        if (null == user) {
            return;
        }
        // todo 解密
        user.setName(decode(user.getName());
    }

    /**
     * 解密
     *
     * @param data 密文
     * @return 原文
     */
    private String decode(String data) {
        // todo 解密
        return null;
    }

}

更新拦截器

  1. 获取 id
  2. 比对 DAO 的全量方法名 com.xxx.xx.methedName 判断,是否是需要处理的方法
  3. 获取参数,判断、强转 参数
  4. 处理参数,参数替换(原对象的参数是不可变的需要赋值替换)
  5. 执行“业务逻辑”
  6. 结束
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * 用户插入、更新查询拦截器
 *
 */
@Component
@Intercepts({
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Slf4j
public class UserUpdateInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.info("UserUpdateInterceptor in");
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取执行id
        String id = mappedStatement.getId();
        log.info("id : {}", id);
        // 获取参数
        Object params;
        if (invocation.getArgs().length > 1) {
            params = invocation.getArgs()[1];
        } else {
            return invocation.proceed();
        }
        log.info("params {}", params);
        // 处理目标参数
        processTargetParams(invocation, id, params);
        log.info("UserUpdateInterceptor end");
        return invocation.proceed();
    }

    /**
     * 处理目标参数
     *
     * @param invocation invocation
     * @param id         id id参见UserDao
     * @param params     参数
     * @see UserDao
     */
    private void processTargetParams(Invocation invocation, String id, Object params) {
        final String insert = "com.xxx.xx.manage.user.core.dao.UserDao.insert";
        final String update = "com.xxx.xx.manage.user.core.dao.UserDao.update";
        final String insertBatch = "com.xxx.xx.manage.user.core.dao.UserDao.insertBatch";
        switch (id) {
            // 插入
            case insert -> {
                User insertUser = (User) params;
                if (null != insertUser) {
                    encodeUserInfo(insertUser);
                    invocation.getArgs()[1] = insertUser;
                }
                log.info("insertUser encode : {}", insertUser);
            }
            // 更新
            case update -> {
                User updateUser = (User) params;
                if (null != updateUser) {
                    encodeUserInfo(updateUser);
                    invocation.getArgs()[1] = updateUser;
                }
                log.info("updateUser encode : {}", updateUser);
            }
            // 批量插入
            case insertBatch -> {
                List<User> userList = (List<User>) params;
                log.info("insertBatch encode : {}", userList);
                if (!CollectionUtils.isEmpty(userList)) {
                    for (User user : userList) {
                        encodeUserInfo(user);
                    }
                    invocation.getArgs()[1] = userList;
                }
                log.info("updateUser encode : {}", userList);
            }
            default -> {
            }
        }
    }

    /**
     * 用户信息加密
     *
     * @param user 用户信息
     */
    private void encodeUserInfo(User user) {
        if (StringUtils.isNotEmpty(user.getName())) {
            user.setName(encode(user.getName()));
        }
    }

    /**
     * 加密
     * 注意长度不能超过数据库允许的长度
     *
     * @param data 数据
     * @return 加密后的数据
     */
    private String encode(String data) {
        // todo 加密 
        return null;
    }

}
  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*crzep

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值