保护用户隐私:Spring Boot 数据脱敏实战
在开发过程中,我们要防止内部数据、账号等泄漏,如果导致核心数据外漏,这种事的后果可大可小,所以实际开发中需要配置文件内容都加密了,数据安全问题真的不容小觑,敏感数据一定要做脱敏处理。
今日内容介绍,大约花费12分钟

#代码地址
https://github.com/bangbangzhou/springboot-learn/tree/main/spring-boot-jasypt-demo
1. 配置脱敏
在Spring Boot开发中,使用Jasypt(Java Simplified Encryption)对配置进行脱敏相对简单的 jasypt存在两种加密方式:
-
单密钥对称加密:一个密钥加盐,可以同时用作内容的加密和解密依据;
-
非对称加密:使用公钥和私钥两个密钥,才可以对内容加密和解密;
以上两种加密方式使用都比较简单,我们以springboot单密钥对称加密方式做示例
1.1. 添加jasypt依赖
在pom.xml中添加jasypt-spring-boot-starter依赖:
<!--配置文件加密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
1.2. 配置应用属性
在application.yml文件中配置Jasypt的对称加密密码,并将需要脱敏的value值替换成预先经过加密的内容ENC(daV7F8cFzZBTzwSQ4gVGAw==)
spring:
application:
name: backend #指定服务名
datasource:
username: root
password: ENC(daV7F8cFzZBTzwSQ4gVGAw==)
url: jdbc:mysql://localhost:3306/backend_db?useUnicode=true&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
# password: zbb[daV7F8cFzZBTzwSQ4gVGAw==]
# 秘钥
jasypt:
encryptor:
password: zbbmeta
# property:
# prefix: "zbb["
# suffix: "]"
注意点:
-
-
加密的内容格式可以随意定义,比如我们需要zbb[daV7F8cFzZBTzwSQ4gVGAw==],只需要配置前后缀即可
jasypt:
encryptor:
property:
prefix: "zbb["
suffix: "]"
-
-
一般密钥不建议存放在项目中,特别是生产环境,我们可以通过启动环境参数进行配置,或者存放在配置中心
java -jar -Djasypt.encryptor.password=zbbmeta spring-boot-jasypt-demo-1.0-SNAPSHOT.jar
-
-
Jasypt加解密值,可以通过代码内调用API生成
@SpringBootTest
public class JasyptTest {
@Autowired
private StringEncryptor stringEncryptor;
@Test
public void test1(){
String encryptStr = stringEncryptor.encrypt("root");
System.out.println("加密后的内容:" + encryptStr);
}
@Test
public void test2(){
String decrypt = stringEncryptor.decrypt("23WuA8R+tUK7pIfurUH38Q==");
System.out.println("解密后的内容:" + decrypt);
}


2. 敏感字段脱敏
项目中用户的隐私数据,比如手机号、身份证或者一些账号配置等信息,入库时都要进行不落地脱敏,也就是在进入我们系统时就要实时脱敏处理。
比如登录,用户在前端就会对密码进行加密,传入到后台后就需要进行脱敏,以供后端使用使用或者持久化到数据库;当用户查询数据时,我们从数据库查询出数据是正常数据,但是为了防止泄密,我们需要响应到前端前进行加密
思考:我们该如何去实现?AOP实现敏感字段的加密和解密是再好不过
2.1. 创建自定义注解
首先自定义两个注解@SensitiveField、@SensitiveMethod分别用在字段属性和方法上,实现思路很简单,只要方法上应用到@SensitiveMethod注解,则检查入参对象是字段是否存在注解@SensitiveField注解,则将对应字段内容解密,在返回数据到前端阶段判断返回对象字段中是否存在@SensitiveField注解,有则将对应字段内容加密,防止数据泄露。
-
SensitiveMethod 注解添加在方法上
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveMethod {
}
-
SensitiveField注解添加到字段上
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveField {
}
2.2. 创建切面 SensitiveAspect
SensitiveAspect实现对前端传入后端数据进行解密,以及对后端返回前端数据数据加密
@Slf4j
@Aspect
@Component
public class SensitiveAspect {
@Autowired
private StringEncryptor stringEncryptor;
@Pointcut("@annotation(com.zbbmeta.annotation.SensitiveMethod)")
public void pointCut() {
}
@Around("pointCut()")
public Object doAround(ProceedingJoinPoint joinPoint ) throws Throwable {
// 获取参数,对参数中加密字段,进行解密
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
Field[] fields = arg.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(SensitiveField.class)) {
field.setAccessible(true);
Object value = field.get(arg);
if (value instanceof String) {
// 调用脱敏服务进行处理
String maskedValue = stringEncryptor.decrypt((String) value);
// 设置脱敏后的值
field.set(arg, maskedValue);
}
}
}
}
Object returnValue = joinPoint.proceed(args);//执行方法获取返回值
// 获取对象的字段及其值进行加密
if (returnValue != null) {
Class<?> clazz = returnValue.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(SensitiveField.class)) {
field.setAccessible(true);
Object value = field.get(returnValue);
// 在这里可以对字段值进行处理,例如脱敏
if (value instanceof String) {
String maskedValue = stringEncryptor.encrypt((String) value);
field.set(returnValue, maskedValue);
}
}
}
}
return returnValue;
}
}
2.3. 使用自定义注解进行标记
-
在你的实体类中使用@SensitiveField注解标记需要脱敏的字段:
@TableName(value ="tb_tutorial")
@Data
public class Tutorial implements Serializable {
private Long id;
private String title;
@SensitiveField
private String description;
private Integer published;
private static final long serialVersionUID = 1L;
}
-
在Controller层方法上添加注解
@RestController
@RequestMapping("tutorial")
public class TutorialController {
@SensitiveMethod
@PutMapping
public Tutorial update(@RequestBody Tutorial tutorial){
System.out.println("tutorial = " + tutorial);
return tutorial;
}
@SensitiveMethod
@GetMapping
public Tutorial getTutorial(){
Tutorial tutorial = new Tutorial();
tutorial.setTitle("springboot葵花宝典");
tutorial.setDescription("微信公众号");
return tutorial;
}
}
3.测试
配置好上述内容后,启动项目进行测试
-
获取GET 请求发送 http://localhost:8877/tutorial

-
前端传入加密数据,后端解密PUT请求 http://localhost:8877/tutorial
{
"id": null,
"title": "springboot葵花宝典",
"description": "D7rG2JHxhwbTd/sdoNJZ4EuKyvOo2DFo",
"published": null
}
控制台输出如下:发现以及将前端加密数据进行解密
tutorial = Tutorial [Hash = 1228016670, id=null, title=springboot葵花宝典, description=微信公众号, published=null, serialVersionUID=1]
#代码地址
https://github.com/bangbangzhou/springboot-learn/tree/main/spring-boot-jasypt-demo
如果您觉得本文不错,欢迎关注,点赞,收藏支持,您的关注是我坚持的动力!
原创不易,转载请注明出处,感谢支持!如果本文对您有用,欢迎转发分享!
原文始发于微信公众号(springboot葵花宝典):保护用户隐私:Spring Boot 数据脱敏实战
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/197671.html