-
不被泄露说明用户信息应被加密储存; -
不能直接展示说明用户信息应脱敏展示;
@EncryptField),返回用户信息实体类(这里我们用UserDO来表示,给UserDO里面的name,phone字段添加@DecryptField);然后利用@EncryptField,@DecryptField做为切入点,以切面的形式实现加密,解密脱敏;
@Target(ElementType.METHOD) //METHOD 说明该注解只能用在方法上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public@interface Encryption {
}
@Target(ElementType.FIELD) //FIELD 说明该注解只能用在字段上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 说明该注解在运行时生效
public@interface EncryptField {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface DecryptField {
// 注解是可以有值的,这里可以为数组,String,枚举等类型
// DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value(); 这里的field是指当前标记的字段
DesensitizationEnum value();
}
publicenum DesensitizationEnum {
name, // 用户信息姓名脱敏
address, // 用户信息地址脱敏
phone; // 用户信息手机号脱敏
}
// 用户信息返回实体类
publicclassUserDO{
@DecryptField(DesensitizationEnum.name)
private String name;
@DecryptField(DesensitizationEnum.address)
private String address;
public String getName(){
return name;
}
publicvoidsetName(String name){
this.name = name;
}
public String getAddress(){
return address;
}
publicvoidsetAddress(String address){
this.address = address;
}
publicUserDO(String name, String address){
this.name = name;
this.address = address;
}
publicstaticvoidmain(String[] args)throws IllegalAccessException {
// 生成并初始化对象
UserDO userDO = new UserDO("梦想是什么","湖北省武汉市");
// 反射获取当前对象的所有字段
Field[] fields = userDO.getClass().getDeclaredFields();
// 遍历字段
for (Field field : fields) {
// 判断字段上是否存在@DecryptField注解
boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);
// 存在
if (hasSecureField) {
// 暴力破解 不然操作不了权限为private的字段
field.setAccessible(true);
// 如果当前字段在userDo中不为空 即name,address字段有值
if (field.get(userDO) != null) {
// 获取字段上注解的value值
DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value();
// 控制台输出
System.out.println(desensitizationEnum);
// 根据不同的value值 我们可以对字段进行不同逻辑的脱敏 比如姓名脱敏-魏*,手机号脱敏-187****2275
}
}
}
}
}
// 用户信息新增实体类
publicclassUserBO{
@EncryptField
private String name;
@EncryptField
private String address;
public String getName(){
return name;
}
publicvoidsetName(String name){
this.name = name;
}
public String getAddress(){
return address;
}
publicvoidsetAddress(String address){
this.address = address;
}
publicUserBO(String name, String address){
this.name = name;
this.address = address;
}
@Override
public String toString(){
return"UserBO{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
publicstaticvoidmain(String[] args)throws IllegalAccessException {
UserBO userBO = new UserBO("周传雄","湖北省武汉市");
Field[] fields = userBO.getClass().getDeclaredFields();
for (Field field : fields) {
boolean annotationPresent = field.isAnnotationPresent(EncryptField.class);
if(annotationPresent){
// 当前字段内容不为空
if(field.get(userBO) != null){
// 这里对字段内容进行加密
Object obj = encrypt(field.get(userBO));
// 字段内容加密过后 通过反射重新赋给该字段
field.set(userBO, obj);
}
}
}
System.out.println(userBO);
}
publicstatic Object encrypt(Object obj){
return"加密: " + obj;
}
}
@RestController
@RequestMapping("/encrypt")
@Slf4j
publicclassEncryptController{
@PostMapping("/v1")
@Encryption// 切入点
public UserBO insert(@RequestBody UserBO user){
log.info("加密后对象:{}", user);
return user;
}
}
@Slf4j
@Aspect
@Component
publicclassEncryptAspect{
//拦截需加密注解 切入点
@Pointcut("@annotation(com.weige.javaskillpoint.annotation.Encryption)")
publicvoidpoint(){
}
@Around("point()") //环绕通知
public Object around(ProceedingJoinPoint joinPoint)throws Throwable {
//加密逻辑处理
encrypt(joinPoint);
return joinPoint.proceed();
}
}
ResponseBodyAdvice 和 RequestBodyAdvice两个接口,这两个接口可以对请求和响应进行预处理,就可以不需要使用AOP;
<dependencies>
<!--Springboot项目自带 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Springboot Web项目 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.20</version>
</dependency>
<!-- 切面 aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
// 实体类
publicclassUserBO{
@EncryptField
private String name;
@EncryptField
private String address;
public String getName(){
return name;
}
publicvoidsetName(String name){
this.name = name;
}
public String getAddress(){
return address;
}
publicvoidsetAddress(String address){
this.address = address;
}
publicUserBO(String name, String address){
this.name = name;
this.address = address;
}
@Override
public String toString(){
return"UserBO{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
// 实体类
publicclassUserDO{
@DecryptField(DesensitizationEnum.name)
private String name;
@DecryptField(DesensitizationEnum.address)
private String address;
public String getName(){
return name;
}
publicvoidsetName(String name){
this.name = name;
}
public String getAddress(){
return address;
}
publicvoidsetAddress(String address){
this.address = address;
}
publicUserDO(String name, String address){
this.name = name;
this.address = address;
}
}
publicenum DesensitizationEnum {
name,
address,
phone;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface DecryptField {
DesensitizationEnum value();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interface Decryption {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public@interface EncryptField {
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public@interface Encryption {
}
@RestController
@RequestMapping("/decrypt")
publicclassDecryptController{
@GetMapping("/v1")
@Decryption
public UserDO decrypt(){
returnnew UserDO("7c29e296e92893476db5f9477480ba7f", "b5c7ff86ac36c01dda45d9ffb0bf73194b083937349c3901f571d42acdaa7bae");
}
}
@RestController
@RequestMapping("/encrypt")
@Slf4j
publicclassEncryptController{
@PostMapping("/v1")
@Encryption
public UserBO insert(@RequestBody UserBO user){
log.info("加密后对象:{}", user);
return user;
}
}
@Slf4j
@Aspect
@Component
publicclassDecryptAspect{
//拦截需解密注解
@Pointcut("@annotation(com.weige.javaskillpoint.annotation.Decryption)")
publicvoidpoint(){
}
@Around("point()")
public Object around(ProceedingJoinPoint joinPoint)throws Throwable {
//解密
return decrypt(joinPoint);
}
public Object decrypt(ProceedingJoinPoint joinPoint){
Object result = null;
try {
Object obj = joinPoint.proceed();
if (obj != null) {
//抛砖引玉 ,可自行扩展其他类型字段的判断
if (obj instanceof String) {
decryptValue();
} else {
result = decryptData(obj);
}
}
} catch (Throwable e) {
e.printStackTrace();
}
return result;
}
private Object decryptData(Object obj)throws IllegalAccessException {
if (Objects.isNull(obj)) {
returnnull;
}
if (obj instanceof ArrayList) {
decryptList(obj);
} else {
decryptObj(obj);
}
return obj;
}
privatevoiddecryptObj(Object obj)throws IllegalAccessException {
Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
boolean hasSecureField = field.isAnnotationPresent(DecryptField.class);
if (hasSecureField) {
field.setAccessible(true);
if (field.get(obj) != null) {
String realValue = (String) field.get(obj);
DesensitizationEnum desensitizationEnum = field.getAnnotation(DecryptField.class).value();
String value = (String) AesUtil.decrypt(realValue,desensitizationEnum);
field.set(obj, value);
}
}
}
}
privatevoiddecryptList(Object obj)throws IllegalAccessException {
List<Object> result = new ArrayList<>();
if (obj instanceof ArrayList) {
result.addAll((Collection<?>) obj);
}
for (Object object : result) {
decryptObj(object);
}
}
privatevoiddecryptValue(){
log.info("根据对象进行解密脱敏,单个字段不做处理!");
}
}
@Slf4j
@Aspect
@Component
publicclassEncryptAspect{
//拦截需加密注解
@Pointcut("@annotation(com.weige.javaskillpoint.annotation.Encryption)")
publicvoidpoint(){
}
@Around("point()")
public Object around(ProceedingJoinPoint joinPoint)throws Throwable {
//加密
encrypt(joinPoint);
return joinPoint.proceed();
}
publicvoidencrypt(ProceedingJoinPoint joinPoint){
Object[] objects;
try {
objects = joinPoint.getArgs();
if (objects.length != 0) {
for (Object object : objects) {
if (object instanceof UserBO) {
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(EncryptField.class)) {
field.setAccessible(true);
if (field.get(object) != null) {
// 进行加密
Object o = field.get(object);
Object encrypt = AesUtil.encrypt(field.get(object));
field.set(object, encrypt);
}
}
}
}
}
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
publicclassAesUtil{
// 默认16位 或 128 256位
publicstatic String AES_KEY = "Wk#qerdfdshbd910";
publicstatic AES aes = SecureUtil.aes(AES_KEY.getBytes());
publicstatic Object encrypt(Object obj){
return aes.encryptHex((String) obj);
}
publicstatic Object decrypt(Object obj, DesensitizationEnum desensitizationEnum){
// 解密
Object decrypt = decrypt(obj);
// 脱敏
return DesensitizationUtil.desensitization(decrypt, desensitizationEnum);
}
publicstatic Object decrypt(Object obj){
return aes.decryptStr((String) obj, CharsetUtil.CHARSET_UTF_8);
}
}
publicclassDesensitizationUtil{
publicstatic Object desensitization(Object obj, DesensitizationEnum desensitizationEnum){
Object result;
switch (desensitizationEnum) {
case name:
result = strUtilHide(obj, 1);
break;
case address:
result = strUtilHide(obj, 3);
break;
default:
result = "";
}
return result;
}
/**
* start从0开始
*/
publicstatic Object strUtilHide(String obj, int start, int end){
return StrUtil.hide(obj, start, end);
}
publicstatic Object strUtilHide(Object obj, int start){
return strUtilHide(((String) obj), start, ((String) obj).length());
}
}
往期推荐
蚂蚁又开源了一个顶级 Java 项目!
网易二面:阿里为何建议MVC+Manager层混合架构?
从一个程序员的角度告诉你:“12306”有多牛逼!
Arthas全面使用指南:离线安装+Docker/K8s集成+集中管理
如何优雅实现多账号统一登录?so easy!
HTTPS 行为大赏:三分钟了解加密过程

