Mybatis统一加密方案TypeHandler

846 阅读2分钟

有的时候我们需要对一些数据库的字段进行加密,例如身份证号码,手机号码之类的。如果我们每次插入都调一次encode方法,在查询出来之后再调一次decode方法太过于麻烦,那么我们使用mybatis提供的TypeHandler来解决这个问题。

TypeHandler 提供了4个方法,第一个是处理方法是处理sql参数的,后面三个是处理返回值的

public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}

Mybatis 提供了一个抽象类 BaseTypeHandler 实现了 TypeHandler 接口,我们可以直接继承这个抽象类。我使用的是mybatis-plus 所以我直接使用了 mybatis-plus 的实现 AbstractJsonTypeHandler。 如果不是mybatis-plus可以直接使用 BaseTypeHandler

public abstract class AbstractJsonTypeHandler<T> extends BaseTypeHandler<T> {
    public AbstractJsonTypeHandler() {
    }

    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, this.toJson(parameter));
    }

    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String json = rs.getString(columnName);
        return StringUtils.isBlank(json) ? null : this.parse(json);
    }

    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String json = rs.getString(columnIndex);
        return StringUtils.isBlank(json) ? null : this.parse(json);
    }

    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        String json = cs.getString(columnIndex);
        return StringUtils.isBlank(json) ? null : this.parse(json);
    }

    protected abstract T parse(String json);

    protected abstract String toJson(T obj);
}

public class EncryptTypeHandler extends AbstractJsonTypeHandler<String> {

    /**
     * 处理结果集
     * @param json
     * @return
     */
    @Override
    protected String parse(String json) {
        try {
            return EncryUtil.decrypt(json);
        } catch (Exception e) {
            return json;
        }
    }

    /**
     * 处理参数
     * @param obj
     * @return
     */
    @Override
    protected String toJson(String obj) {
        try {
            return EncryUtil.encrypt(obj);
        } catch (Exception e) {
            return obj;
        }
    }
}

我们需要在对应的数据库实例类加上 autoResultMap = true,然后在需要加密的字段上 加上 typeHandler = EncryptTypeHandler.class 即可实现统一的加密和解密了

@Getter  
@Setter  
@Accessors(chain = true)  
@TableName(value = "user",autoResultMap = true)  
public class User extends BaseEntity {  
  
private static final long serialVersionUID = 1L;  
  
@TableId(value = "id", type = IdType.AUTO)  
private Long id;  
  
@TableField("user_id")  
private Long userId;  
  
@TableField(value = "real_name",typeHandler = EncryptTypeHandler.class)  
private String realName;  
  
@TableField(value = "id_card_no",typeHandler = EncryptTypeHandler.class)  
private String idCardNo;  
  
}

有的时候我们需要通过xml 写sql 时,可能时导致Handler,我们需要加上对应的Handler

<update id="updateUser" parameterType="com.wa.user.entity.UserEntity">
        UPDATE user
        <set>
            <if test="realName != null and realName != ''">
                real_name= #{realName,typeHandler=com.wa.user.mybatis.EncryptTypeHandler},
            </if>
            <if test="idCardNo != null and idCardNo != ''">
                id_card_no= #{idCardNo,typeHandler=com.wa.user.mybatis.EncryptTypeHandler},
            </if>
            update_time= now()
        </set>
        WHERE id= #{id}
    </update>