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格式的字段字视为同一字段,如firstName 和first_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.FieldFilter中deserializingUsed,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