我的开源工具beanfilter:实现基于注解(Annotation)的服务端(spring)动态字段过滤

发布于:2024-06-16 ⋅ 阅读:(17) ⋅ 点赞:(0)

beanfilter是我最近完成的一个开源Java工具,
项目地址 https://gitee.com/l0km/beanfilter
才做完成第一阶段,发布了第一个版本0.1.0
完成对spring的支持,后续还要实现对thrift服务的支持,以下为工具的使用说明

beanfilter

基于注解(Annotation)实现的服务端(spring/thrift)对JavaBean类型数据在序列化和反序列化阶段动态字段过滤(IFieldFilter)和值过滤(IValueFilter)工具。

为服务端提供了一个动态过滤服务方法输入输出的Java Bean参数字段的功能,字段过滤用于控制字段是否被输入/输出,值过滤用于控制字段输入/输出时的内容。

比如在字段安全保护场景,可以使用字段过滤器,将不允许用户自己修改的字段(balance)在作为服务方法输入参数反序列化时忽略处理,避免客户端远程修改该字段。

比如在隐私保护场景,可以使用值过滤器,将用户名字段 (name)在作为服务方法结果输出时加*号显示为王*华

特性

  • 基于服务方法注解支持服务端为每个服务方法定义不同的字段过滤
  • 支持jackson,fastjson的对Java Bean类型在序列化和反序列化时的字段过滤和值过滤
  • 支持Thrift Struct在序列化和反序列化时的字段过滤和值过滤【暂未实现】
  • 支持过滤器动态激活
  • 支持自定义字段过滤器和值过滤
  • 支持自定义过滤器注解

快速入门

beanfilter使用非常简单,只要如下两步。

服务方法注解定义

需要引入依赖

		<dependency>
			<groupId>com.gitee.l0km</groupId>
			<artifactId>beanfilter-annotations</artifactId>
			<version>0.1.0</version>
		</dependency>

如下在服务方法上定义一组过滤器注解,指定要过滤的字段,通过注解定义了服务方法上使用的过滤器

public class TestService {
    /** 
         * [序列化]字段过滤器定义:指定tokenTime字段在序列化时不输出,该方法返回的 TestUserBean 在序列化为JSON时就没有该字段
         * activeOn指定了自定义的过滤器激活器,只有激活器返回true时才启用过滤器
         */
    @FieldNameFilter(beanClass = TestUserBean.class,filterNames = {"tokenTime"},activeOn=MyAction.class)
    /** [反序列化]字段过滤器定义:指定tokenTime字段在反序列化时不输出,该方法反序列化时得到的输入参数TestDevice的 tokenTime 不会被赋值,为null */
    @FieldNameFilter(beanClass = TestDevice.class,filterNames = {"tokenTime"},serializingUsed =false,deserializingUsed = true)
    /** [序列化]字段过滤器定义:为TestUserGroupBean类型数据指定使用自定义字段过滤器用于反序列化 */
    @FieldFilter(filterClass=MyFieldFilter.class,beanClass=TestUserGroupBean.class)
    /** [序列化]字段过滤器定义:为TestUserGroupBean类型数据指定使用自定义值过滤器用于反序列化 */
    @FieldFilter(filterClass=IFieldFilter.DefaultFieldFilter.class,beanClass=TestUserGroupBean.class)
    /** [序列化]值过滤器定义:指定TestUserBean的 password 字段在序列化时输出为 '***'  */
    @ConstantValueFilter(beanClass = TestUserBean.class,filterName = "password",consant = "***")
    /** [序列化]值过滤器定义:指定TestUserBean的 name 字段在序列化时输出为 'anonymous'  */
    @ConstantValueFilter(beanClass = TestUserBean.class,filterName = "name",consant = "anonymous")
    public TestUserBean addUserFilterOut(TestUserBean input,TestUserGroupBean group) {
        return input;
    }
}

Spring 启动拦截器

需要引入依赖

		<dependency>
			<groupId>com.gitee.l0km</groupId>
			<artifactId>beanfilter-interceptor</artifactId>
			<version>0.1.0</version>
		</dependency>

如下在Spring服务启动注解(@SpringBootApplication)上指定扫描beanfilter spring拦截器FilterInterceptorConfig所在包就可以启动服务方法拦截器,激活了服务方法上定义的beanfilter过滤器

import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.gitee.l0km.beanfilter.spring.FilterInterceptorConfig;

/**
 * 应用服务启动配置
 */
@SpringBootApplication(scanBasePackageClasses = {FilterInterceptorConfig.class})
public class ApplicationBoot{
    //..../
}

过滤器说明

beanfilter-annotations定义了基本的字段过滤和值过滤器注解,可以直接用于服务方法上。

beanfilter-core中定义基本的字段过滤器和值过滤器实现类

内置过滤器注解

@SimpleFieldFilter

@SimpleFieldFilter是基于 com.gitee.l0km.beanfilter.SimpleFieldFilter 过滤实现的字段过滤器注解

注解字段名 类型 默认值 说明
beanClass Class<?> Object.class 字段过滤作用的Java Bean类型,如果不指定作用于所有Java Bean类型
autoCase boolean false 是否将snake-case和camel-case格式的字段字视为同一字段,如firstNamefirst_name,为true时视为同一个名字。在这个字段数据库对象类型上比较有用,数据库字段一般的命名习惯是snake-case,而Java Bean的字段命名为camel-case,该字段为true,就可以避免因为定义的字段名格式不匹配而导致的过滤器失效
whiteList boolean false 为true时为白名单模式,filterNames指定字段名被允许输入/输出,否则为黑名单模式,filterNames指定字段名被禁止输入/输出
filterNames String[] 过滤的字段名列表
activeOn Class<?>[] {} 过滤器激活器类型列表,必须为Activation接口实现
activeOnClassNames String[] {} 过滤器激活器类名列表,必须为Activation接口实现类名,与activeOn参数作用相同,用字符器定义类名在特定场景可以减少依赖
activeAnd boolean true 指定多个过滤器激活器时,激活判断模式,为true为AND模式,所有激活器条件都匹配才能激活过滤器,否则为OR模式,任意一激活器条件都匹配就可以激活过滤器
deserializingUsed boolean false 过滤器使用场景:是否用于反序列化
serializingUsed boolean true 过滤器使用场景:是否用于序列化
@ConstantValueFilter

@ConstantValueFilter是基于 com.gitee.l0km.beanfilter.SimpleValueFilter 过滤实现的值过滤器注解

注解字段名 类型 默认值 说明
beanClass Class<?> Object.class 同@SimpleFieldFilter
autoCase boolean false 同@SimpleFieldFilter
filterName String 要过滤字段名
consant String 返回的字段值
constantType Class<?> Object.class 要求的返回字段值类型
activeOn Class<?>[] {} 同@SimpleFieldFilter
activeOnClassNames String[] {} 同@SimpleFieldFilter
activeAnd boolean true 同@SimpleFieldFilter
deserializingUsed boolean false 同@SimpleFieldFilter
serializingUsed boolean true 同@SimpleFieldFilter

自定义过滤器和注解

用户可以参照 SimpleFieldFilter实现 IFieldFilter接口来实现自定义的字段过滤器,同理可以参照 SimpleValueFilter实现IValueFilter接口来实现自定义的值过滤器。

有了自定义过滤器,用户可以参照@FieldNameFilter@ConstantValueFilter来定义对应的注解。

以自定义字段过滤为例,如下,我们定义一个字段过滤器:

	public class MyFieldFilterImpl implements IFieldFilter{
		final String testName;
		final Class<?> testType;
		public MyFieldFilterImpl(String testName, Class<?> testType) {
			super();
			this.testName = testName;
			this.testType = testType;
		}
		@Override
		public boolean permit(Class<?> clazz, String fieldName) {
			return false;
		}		
	}

根据上面的字段过滤器,可以定义如下注解@MyFieldFilter,我们希望该注解是可重复的,所以同时定义了@MyFieldFilters

	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.METHOD)
	@FieldFilter(filterClass = MyFieldFilterImpl.class,beanClass = Object.class)
	@Repeatable(MyFieldFilters.class)
	public @interface MyFieldFilter {
		@CtorArg(0)
		String testName();
		@CtorArg(1)
		Class<?> testType() default Object.class;
		@AliasFor(annotation = FieldFilter.class,attribute = "deserializingUsed")
		boolean deserializingUsed() default false;
		@AliasFor(annotation = FieldFilter.class,attribute = "serializingUsed")
		boolean serializingUsed() default true;
	}
	@Retention(RetentionPolicy.RUNTIME)
	@Target(ElementType.METHOD)
	public @interface MyFieldFilters {
		MyFieldFilter[] value();
	}

上面的@MyFieldFilter注解中@CtorArg用于定义该字段是过滤器实现类MyFieldFilterImpl类的构造方法MyFieldFilterImpl(String testName, Class<?> testType)的参数,@CtorArg中定义的数字为该字段在构造方法参数列表中的索引 ,@CtorArg(0)即为构造方法的第一个参数,以此类推,不能搞错否则服务方法拦截器在构造过滤器实例时会抛出异常。

上面的注解中deserializingUsed,serializingUsed字段可以不定义,如果不定义则默认该过滤器只用于序列化。参见 @com.gitee.l0km.beanfilter.annotations.FieldFilterdeserializingUsed,serializingUsed字段的默认值

自定义过滤器完整的示例代码参见 :

com.gitee.l0km.beanfilter.CustomFilterTest

Spring服务方法拦截

beanfilter-interceptor中com.gitee.l0km.beanfilter.spring.InstallFilterInterceptor 基于org.springframework.web.servlet.HandlerInterceptor实现了Spring WEB请求拦截,在请求被序列化之前,根据服务方法上的注解构建过滤器,注入Java Bean对应的序列化器和反序列化器。在请求执行完成之后,删除过滤器。

完整说明参见项目仓库: https://gitee.com/l0km/beanfilter


网站公告

今日签到

点亮在社区的每一天
去签到