时间:2023-02-12 10:53:36 | 栏目:JAVA代码 | 点击:次
springboot实现敏感字段加密存储,解密显示,通过mybatis,自定义注解+AOP切面,Base64加解密方式实现功能。
创建springboot项目
添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <!--mysql数据库驱动--> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <!--mybatis--> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.0</version> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.7</version> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.6</version>
yml配置
server: port: 8081 spring: #数据库连接配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false username: root password: 123456 #mybatis的相关配置 mybatis: #mapper配置文件 mapper-locations: classpath:mapper/*.xml type-aliases-package: com.cxh.mybatis.entity #开启驼峰命名 configuration: map-underscore-to-camel-case: true
自定义注解
//表示要加密的字段 @Target({ElementType.FIELD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) public @interface EncryptField { String[] value() default ""; }
//表示需解密 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NeedDecrypt { }
//表示需加密 @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface NeedEncrypt { }
AOP切面
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.Objects; //加密AOP @Slf4j @Aspect @Component public class EncryptAspect { //拦截需加密注解 @Pointcut("@annotation(com.cxh.mybatis.test.NeedEncrypt)") public void pointCut() { } @Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { //加密 encrypt(joinPoint); return joinPoint.proceed(); public void encrypt(ProceedingJoinPoint joinPoint) { Object[] objects=null; try { objects = joinPoint.getArgs(); if (objects.length != 0) { for (int i = 0; i < objects.length; i++) { //抛砖引玉 ,可自行扩展其他类型字段的判断 if (objects[i] instanceof String) { objects[i] = encryptValue(objects[i]); } else { encryptObject(objects[i]); } } } } catch (Exception e) { e.printStackTrace(); } /** * 加密对象 * @param obj * @throws IllegalAccessException */ private void encryptObject(Object obj) throws IllegalAccessException { if (Objects.isNull(obj)) { log.info("当前需要加密的object为null"); return; Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { boolean containEncryptField = field.isAnnotationPresent(EncryptField.class); if (containEncryptField) { //获取访问权 field.setAccessible(true); if(field.get(obj) != null){ String value = Base64Util.getBase64(String.valueOf(field.get(obj))); field.set(obj, value); * 加密单个值 * @param realValue * @return public String encryptValue(Object realValue) { realValue = Base64Util.getBase64(String.valueOf(realValue)); log.info("加密异常={}",e.getMessage()); return String.valueOf(realValue); }
import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Objects; //解密AOP @Slf4j @Aspect @Component public class DecryptAspect { //拦截需解密注解 @Pointcut("@annotation(com.cxh.mybatis.test.NeedDecrypt)") public void pointCut() { } @Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { //解密 Object result = decrypt(joinPoint); return result; public Object decrypt(ProceedingJoinPoint joinPoint) { Object result = null; try { Object obj = joinPoint.proceed(); if (obj != null) { //抛砖引玉 ,可自行扩展其他类型字段的判断 if (obj instanceof String) { decryptValue(obj); } else { result = decryptData(obj); } } } catch (Throwable e) { e.printStackTrace(); } private Object decryptData(Object obj) throws IllegalAccessException { if (Objects.isNull(obj)) { return null; if (obj instanceof ArrayList) { decryptList(obj); } else { decryptObj(obj); return obj; /** * 针对单个实体类进行 解密 * @param obj * @throws IllegalAccessException */ private void decryptObj(Object obj) throws IllegalAccessException { Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { boolean hasSecureField = field.isAnnotationPresent(EncryptField.class); if (hasSecureField) { field.setAccessible(true); if(field.get(obj) != null) { String realValue = (String) field.get(obj); String value = Base64Util.getFromBase64(realValue); field.set(obj, value); * 针对list<实体来> 进行反射、解密 private void decryptList(Object obj) throws IllegalAccessException { List<Object> result = new ArrayList<>(); for (Object o : (List<?>) obj) { result.add(o); for (Object object : result) { decryptObj(object); public String decryptValue(Object realValue) { realValue = Base64Util.getFromBase64(String.valueOf(realValue)); } catch (Exception e) { log.info("解密异常={}", e.getMessage()); return String.valueOf(realValue); }
BASE64加解密工具类
import java.io.UnsupportedEncodingException; import org.springframework.stereotype.Component; import sun.misc.*; public class Base64Util { // 加密 public static String getBase64(String str) { byte[] b = null; String s = null; try { b = str.getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } if (b != null) { s = new BASE64Encoder().encode(b); return s; } // 解密 public static String getFromBase64(String s) { String result = null; if (s != null) { BASE64Decoder decoder = new BASE64Decoder(); try { b = decoder.decodeBuffer(s); result = new String(b, "utf-8"); } catch (Exception e) { e.printStackTrace(); } return result; public static void main(String[] args) { String a = "123456"; String b = getBase64(a); System.out.println(b); System.out.println(getBase64(a)); System.out.println(getFromBase64(b)); }
控制层
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping("/findAll") public List<User> findAll(){ return userService.findAll(); } @RequestMapping("/add") @NeedEncrypt public int add(User user){ return userService.add(user); @RequestMapping("/get") @NeedDecrypt public List<User> get(User user){ return userService.get(user); @RequestMapping("/getByName") public List<User> getByName(String username){ User user = new User(); user.setUsername(Base64Util.getBase64(username)); }
service实现类
@Service("userService") public class UserServiceimpl implements UserService { @Autowired private UserMapper userMapper; @Override public List<User> findAll() { return userMapper.findAll(); } public Integer add(User user) { return userMapper.add(user); public List<User> get(User user) { return userMapper.get(user); }
userMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.cxh.mybatis.mapper.UserMapper"> <select id="findAll" resultType="User"> SELECT * FROM tb_user </select> <insert id="add" parameterType="User"> INSERT INTO tb_user(`id`, `username`, `password`) VALUES (#{id}, #{username}, #{password}); </insert> <select id="get" resultType="User" parameterType="User"> <where> <if test="id != null and id != ''"> and id = #{id} </if> <if test="username != null and username != ''"> and username = #{username} <if test="password != null and password != ''"> and password = #{password} </where> </mapper>
运行项目,打开postman,发起插入请求:localhost:8081/user/add
查看数据库,显示数据已加密
发起查询请求localhost:8081/user/get,显示数据已解密
发起查询所有请求localhost:8081/user/findAll,由于该方法没有添加解密注解,所以数据还是加密的。