Skip to content

Commit

Permalink
Renamed groupId from 'red.zyc' to 'io.allurx' and updated package str…
Browse files Browse the repository at this point in the history
…ucture accordingly.
  • Loading branch information
allurx committed Oct 27, 2024
1 parent c36ace9 commit 0721880
Show file tree
Hide file tree
Showing 42 changed files with 1,398 additions and 836 deletions.
95 changes: 57 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
# desensitization-spring-boot
[desensitization](https://github.com/allurx/desensitization) 库集成到spring-boot中实现数据自动脱敏。
实现原理是基于spring-aop对全局方法进行拦截脱敏处理,默认会对当前spring-boot工程启动类所在的包及其子包下所有**需要**脱敏处理的方法进行拦截。
当然你也可以在spring的配置文件中通过desensitization开头的配置参数编写自己的切点表达式或者编写一个名称为**desensitizationAdvisor**的Advisor
添加到spring上下文中以便更好地控制脱敏。
# 用法
## spring-boot版本
3.1.5
## maven依赖
# blur-spring-boot

This project integrates the [blur](https://github.com/allurx/blur) library into Spring Boot to enable automatic data blurring. Using Spring AOP, this library globally intercepts targeted methods to apply blurring (data masking) by default on all methods within the package of the Spring Boot application's main class and its sub-packages.

To further customize the blurring, you can specify a custom pointcut expression using `blur`-prefixed parameters in the Spring configuration file. Alternatively, you can define an `Advisor` named `blurAdvisor` in the Spring context to gain more granular control over the blurring process.

# Usage

## Spring Boot Version

3.3.5

## Maven Dependency

```xml
<dependency>
<groupId>red.zyc.boot</groupId>
<artifactId>desensitization-spring-boot-starter</artifactId>
<version>2.0.0</version>
<groupId>io.allurx</groupId>
<artifactId>blur-spring-boot-starter</artifactId>
<version>${latest version}</version>
</dependency>
```
## 注意
默认情况下只会对基于Spring内置的`ResponseEntity`类型返回值的方法进行必要的脱敏处理。而通常情况下我们系统中都会自定义一个类似的响应实体

## Notes
By default, this library enables blurring for methods returning Spring’s `ResponseEntity` type only. If your application uses a custom response entity, such as:
```java
@Getter
@Setter
public class CustomizedResponse<T> {

private T data;
public T data;

private String code;
public String code;

private String message;
public String message;

public CustomizedResponse() {}

Expand All @@ -37,25 +41,34 @@ public class CustomizedResponse<T> {

}
```
对自定义类型进行脱敏处理时我们需要配置一个类型解析器来解析该类型
then you’ll need to configure a type parser to handle custom types for blurring.
```java
@Configuration
public class DesensitizationConfig {

public class BlurConfig {

/**
* Registers the {@code CustomizedResponseTypeParser} as a bean in the Spring context.
*
* @return {@link CustomizedResponseTypeParser}
*/
@Bean
public TypeParser<CustomizedResponse<Object>, AnnotatedParameterizedType> typeParser() {
return new CustomizedResponseTypeParser();
}

public static class CustomizedResponseTypeParser implements TypeParser<CustomizedResponse<Object>, AnnotatedParameterizedType>, AopInfrastructureBean {

/**
* Custom type parser to handle {@code CustomizedResponse} type data.
*/
public static class CustomizedResponseTypeParser
implements TypeParser<CustomizedResponse<Object>, AnnotatedParameterizedType>, AopInfrastructureBean {

private final int order = AnnotationParser.randomOrder();

@Override
public CustomizedResponse<Object> parse(CustomizedResponse<Object> response, AnnotatedParameterizedType annotatedParameterizedType) {
AnnotatedType typeArgument = annotatedParameterizedType.getAnnotatedActualTypeArguments()[0];
Object erased = AnnotationParser.parse(response.getData(), typeArgument);
return new CustomizedResponse<>(erased, response.getMessage(), response.getCode());
public CustomizedResponse<Object> parse(CustomizedResponse<Object> response, AnnotatedParameterizedType type) {
AnnotatedType typeArgument = type.getAnnotatedActualTypeArguments()[0];
Object blurredData = AnnotationParser.parse(response.getData(), typeArgument);
return new CustomizedResponse<>(blurredData, response.getMessage(), response.getCode());
}

@Override
Expand All @@ -70,14 +83,20 @@ public class DesensitizationConfig {
}
}
```
该配置是用来解析CustomizedResponse类型的对象,通常情况下我们只需要对响应的实际数据(data)进行脱敏即可。
将上面的类型解析器添加到Spring上下文中之后,接下来我们只需将脱敏注解标记到需要脱敏的方法返回对象的泛型参数上就能完成CustomizedResponse类型数据的自动脱敏处理。
# 例子
## 脱敏ResponseEntity类型的数据
1. [需要脱敏的方法](https://github.com/allurx/desensitization-spring-boot/blob/master/desensitization-spring-boot-samples/desensitization-spring-boot-sample-web/src/main/java/red/zyc/desensitization/boot/sample/web/controller/ResponseEntityDesensitizationController.java)
2. [测试用例](https://github.com/allurx/desensitization-spring-boot/blob/master/desensitization-spring-boot-samples/desensitization-spring-boot-sample-web/src/test/java/red/zyc/desensitization/boot/sample/web/ResponseEntityDesensitizationTest.java)
## 脱敏CustomizedResponse类型的数据
1. [需要脱敏的方法](https://github.com/allurx/desensitization-spring-boot/blob/master/desensitization-spring-boot-samples/desensitization-spring-boot-sample-web/src/main/java/red/zyc/desensitization/boot/sample/web/controller/CustomizedResponseDesensitizationController.java)
2. [测试用例](https://github.com/allurx/desensitization-spring-boot/blob/master/desensitization-spring-boot-samples/desensitization-spring-boot-sample-web/src/test/java/red/zyc/desensitization/boot/sample/web/CustomizedResponseDesensitizationTest.java)
This configuration blurs `CustomizedResponse` type objects,
typically applying blurring only to the actual data (`data`) within the response body.
After adding this type parser to the Spring context,
you need only annotate the generic parameter of the return object in the method with the blurring annotation to enable automatic blurring for `CustomizedResponse` type data.

## Examples

### Blurring `ResponseEntity` Type Data
1. [Blurred method example](blur-spring-boot-samples/blur-spring-boot-sample-web/src/main/java/io/allurx/blur/spring/boot/sample/web/controller/ResponseEntityBlurController.java)
2. [Related test cases](blur-spring-boot-samples/blur-spring-boot-sample-web/src/test/java/io/allurx/blur/spring/boot/sample/web/test/ResponseEntityBlurTest.java)

### Blurring `CustomizedResponse` Type Data
1. [Blurred method example](blur-spring-boot-samples/blur-spring-boot-sample-web/src/main/java/io/allurx/blur/spring/boot/sample/web/controller/CustomizedResponseBlurController.java)
2. [Related test cases](blur-spring-boot-samples/blur-spring-boot-sample-web/src/test/java/io/allurx/blur/spring/boot/sample/web/test/CustomizedResponseBlurTest.java)

# License
[Apache License 2.0](https://github.com/allurx/desensitization-spring-boot/blob/master/LICENSE.txt)
[Apache License 2.0](LICENSE.txt)
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>red.zyc.boot</groupId>
<artifactId>desensitization-spring-boot</artifactId>
<groupId>io.allurx</groupId>
<artifactId>blur-spring-boot</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>desensitization-spring-boot-autoconfigure</artifactId>
<name>desensitization-spring-boot-autoconfigure</name>
<description>desensitization-spring-boot-autoconfigure</description>
<artifactId>blur-spring-boot-autoconfigure</artifactId>
<name>blur-spring-boot-autoconfigure</name>
<description>blur-spring-boot-autoconfigure</description>

<dependencies>
<dependency>
<groupId>red.zyc</groupId>
<artifactId>desensitization</artifactId>
<groupId>io.allurx</groupId>
<artifactId>blur</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2024 allurx
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.allurx.blur.spring.boot.autoconfigure;

import io.allurx.annotation.parser.type.TypeParser;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;
import org.springframework.util.Assert;

import java.lang.reflect.AnnotatedParameterizedType;
import java.util.Optional;

/**
* Autoconfiguration class for enabling blur functionality, which includes data masking,
* obfuscating, and anonymizing capabilities. Configures default beans if not already defined.
* <p>
* This configuration class loads properties from {@link BlurProperties} and sets up
* aspect-oriented data handling for specified pointcuts.
* </p>
*
* @author allurx
*/
@AutoConfiguration
@EnableConfigurationProperties(BlurProperties.class)
public class BlurAutoConfiguration {

private static final String BLUR_ADVISOR = "blurAdvisor";
private final BlurProperties blurProperties;
static final ThreadLocal<SpringApplication> SPRING_APPLICATION_HOLDER = new ThreadLocal<>();

/**
* Constructor for {@link BlurAutoConfiguration}.
*
* @param blurProperties properties configuration for blur functionality
*/
public BlurAutoConfiguration(BlurProperties blurProperties) {
this.blurProperties = blurProperties;
}

/**
* Defines a bean for the blur advisor, which applies data masking and obfuscation
* advice to methods matched by the pointcut expression.
*
* @return a configured {@link Advisor} with pointcut and advice set up
*/
@Bean
@ConditionalOnMissingBean(name = BLUR_ADVISOR)
public Advisor blurAdvisor() {
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
advisor.setAdvice(new BlurMethodInterceptor());
advisor.setExpression(pointcutExpression());
return advisor;
}

/**
* Registers a type parser bean for {@link ResponseEntity} to handle annotated type
* resolution during runtime, enabling enhanced blur functionality.
*
* @return a {@link TypeParser} implementation for {@link ResponseEntity} types
*/
@Bean
public TypeParser<ResponseEntity<Object>, AnnotatedParameterizedType> responseEntityResolver() {
return new ResponseEntityTypeParser();
}

/**
* Retrieves the pointcut expression used for data blur application. If a custom
* expression is not configured, defaults to targeting all methods in the main application package.
*
* @return the pointcut expression string for data blur operations
*/
private String pointcutExpression() {
SpringApplication springApplication = SPRING_APPLICATION_HOLDER.get();
Assert.notNull(springApplication, () -> "Failed to retrieve SpringApplication. The current project may not be a Spring Boot application.");
return Optional.ofNullable(blurProperties.getPointcutExpression())
.orElse("execution(* " + springApplication.getMainApplicationClass().getPackage().getName() + "..*.*(..))");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2024 allurx
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.allurx.blur.spring.boot.autoconfigure;

import io.allurx.annotation.parser.handler.Parse;
import io.allurx.annotation.parser.type.Cascade;
import io.allurx.blur.Blur;
import io.allurx.kit.base.reflection.AnnotatedTypeToken;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import java.lang.reflect.AnnotatedArrayType;
import java.lang.reflect.AnnotatedParameterizedType;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.AnnotatedTypeVariable;
import java.lang.reflect.AnnotatedWildcardType;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
* Interceptor for applying data blur functionality on method arguments and return values.
* This interceptor applies masking, obfuscation, and anonymization based on annotation-driven rules.
* <p>
* It dynamically checks for blur-related annotations on method parameters and return types,
* ensuring only annotated data is processed, which optimizes performance by avoiding unnecessary processing.
* </p>
*
* @author allurx
* @see Blur
* @see MethodInterceptor
* @see AnnotatedTypeToken
* @see Parse
* @see Cascade
*/
public class BlurMethodInterceptor implements MethodInterceptor {


/**
* Default constructor
*/
public BlurMethodInterceptor() {
}

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();
Parameter[] parameters = method.getParameters();

IntStream.range(0, parameters.length)
.filter(i -> requiresBlur(parameters[i].getAnnotatedType()))
.forEach(i -> arguments[i] = Blur.blur(arguments[i], AnnotatedTypeToken.of(parameters[i].getAnnotatedType())));

Object proceed = invocation.proceed();
AnnotatedType returnType = method.getAnnotatedReturnType();

return requiresBlur(returnType) ? Blur.blur(proceed, AnnotatedTypeToken.of(returnType)) : proceed;
}

/**
* Determines if an object, identified by its {@link AnnotatedType}, requires blurring.
* Objects marked with blur-related annotations are processed, while others are skipped
* to maintain performance by avoiding redundant processing or object creation.
* <p>
* Although using a dedicated {@code @Blur} annotation might simplify identification,
* this method avoids extra annotations by dynamically analyzing the presence of blur-triggering annotations.
* </p>
*
* @param annotatedType the {@link AnnotatedType} of the object to evaluate
* @return {@code true} if the object requires blurring, {@code false} otherwise
*/
private boolean requiresBlur(AnnotatedType annotatedType) {
return Arrays.stream(annotatedType.getDeclaredAnnotations()).anyMatch(annotation -> annotation.annotationType().isAnnotationPresent(Parse.class)) ||
annotatedType.getDeclaredAnnotation(Cascade.class) != null ||
switch (annotatedType) {
case AnnotatedTypeVariable annotatedTypeVariable ->
Arrays.stream(annotatedTypeVariable.getAnnotatedBounds()).anyMatch(this::requiresBlur);
case AnnotatedWildcardType annotatedWildcardType ->
Stream.of(annotatedWildcardType.getAnnotatedUpperBounds(), annotatedWildcardType.getAnnotatedLowerBounds())
.flatMap(Arrays::stream)
.anyMatch(this::requiresBlur);
case AnnotatedParameterizedType annotatedParameterizedType ->
Arrays.stream(annotatedParameterizedType.getAnnotatedActualTypeArguments())
.anyMatch(this::requiresBlur);
case AnnotatedArrayType annotatedArrayType ->
requiresBlur(annotatedArrayType.getAnnotatedGenericComponentType());
default -> false;
};
}

}
Loading

0 comments on commit 0721880

Please sign in to comment.