1. 首页
  2. > 香港公司年审 >

开户行银行代码和银行联行号(开户行行号与银行联行号的区别)



通俗点说,就是自定义的注解加上这个注解,自定义的注解就会被Jackson的注解拦截器(JacksonAnnotationIntrospector)findSerializer发现拦截并处理


2.@JsonSerialize(using=SensitiveInfoSerialize.class)

com.fasterxml.jackson.databind.annotation.JsonSerialize



官方:JsonSerialize类 — 通过附加到“getter”方法或字段或值类,用于配置序列化方面的注释。注释值类时,配置用于值类的实例,但可以由更具体的注释(附加到方法或字段的注释)覆盖。
注释示例如下:
@JsonSerialize(using=MySerializer.class,
as=MySubClass.class,
typing=JsonSerialize.typing.STATIC
)
(这是as=MySubClass.class多余的,因为某些属性会阻止其他属性:特别是,“using”优先于“as”,后者优先于“typing”设置)
using属性 — 用于序列化关联值的序列化程序类。根据注释的内容,值 要么是注释类的实例(可在需要类序列化程序的任何地方全局使用);或者仅用于通过getter方法序列化属性访问。


这里我的理解是JsonSerialize注解是给我自定义的注解配置序列化工具的信息,using就是设置这个注解要用哪个序列化工具。


3. JsonSerializer

com.fasterxml.jackson.databind.JsonSerializer



抽象类,该类定义ObjectMapper(以及其他链式JSONSerializer)使用提供的JsonGenerator将任意类型的对象序列化为JSON所使用的API。com.fasterxml.jackson.databind.ser.std.StdSerializer而不是此类,因为它将实现此类的许多可选方法。
注意:各种serialize方法永远不会用空值调用——调用方必须处理空值,通常是通过调用SerializerProvider.findNullValueSerializer来获取要使用的序列化程序。这也意味着在序列化空值时,不能直接使用自定义序列化程序来更改要生成的输出。
如果序列化程序是聚合序列化程序(这意味着它通过使用其他序列化程序来委托对其某些内容的处理),那么它通常还需要实现com.fasterxml.jackson.databind.ser.ResolvableSerializer,它可以找到所需的辅助序列化程序。这对于允许序列化程序的动态重写很重要;需要单独的调用接口来分离辅助序列化程序的解析(可能直接或间接地将循环链接回序列化程序本身)。
此外,为了支持每个属性的注释(根据每个属性配置序列化的各个方面),序列化程序可能需要实现com.fasterxml.jackson.databind.ser.ContextualSerializer,它允许序列化程序的专门化:调用com.fasterxml.jackson.databind.ser.ContextualSerializer.createContext传递有关属性的信息,并可以创建新配置的序列化程序来处理该特定属性。
如果同时实现了com.fasterxml.jackson.databind.ser.ResolvableSerializer和com.fasterxml.jackson.databind.ser.ContextualSerializer,则序列化程序的解析将在上下文化之前进行。


序列化处理类,用于实现具体脱敏的逻辑,把原数据变成带*的数据


4. ContextualSerializer


官方:JsonSerializer可以实现的附加接口获得一个回调,该回调可用于创建序列化程序的上下文实例,以用于处理支持的类型的属性。这对于可以通过批注配置的序列化程序很有用,或者应具有不同的行为,具体取决于要序列化的属性的类型。


就是通过回调函数实现自定义注解中的字段信息传递给自定义JsonSerializer对象的操作,比如我们这的自定义注解中存在type和permission两个字段,自定义JsonSerializer序列化工具中也存在type和permission两个字段,把注解中的两个字段值赋值给自定义JsonSerializer。


实现步骤

前提:项目使用的是SpringMVC框架,且Spring自动序列化MessageConverter使用的是默认的Jackson序列化,如果项目已改成fastjson,类比实现fastjson的自定义注解。


1.声明自定义注解 SensitiveInfo

这里我们想要区分手机号、身份证、银行卡号等多种敏感信息类型,所以使用了type;我们想要区分权限来确定是否脱敏操作,所以使用了permission字段,代码如下:


import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside //让此注解可以被Jackson扫描到 @JsonSerialize(using = SensitiveInfoSerialize.class) //配置处理此注解的序列化处理类 public @interface SensitiveInfo { /** * 拥有此权限不加密 * @return */ public String permission() default "MobileEncryptPermission"; /** * 脱敏 加密的类型 * @return */ public SensitiveType type() default SensitiveType.MOBILE_PHONE; }

2.声明序列化处理类

这里实现了hasPermission()方法,来判断是否拥有指定权限。
createContextual()是实现ContextualSerializer的回调函数,根据注解内容创建SensitiveInfoSerialize并赋值。
serialize()中实现具体的脱敏密文等处理


import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.BeanProperty; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.ContextualSerializer; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.PatternMatchUtils; import org.springframework.util.StringUtils; import java.io.IOException; import java.util.Collection; import java.util.Objects; public class SensitiveInfoSerialize extends JsonSerializer<String> implements ContextualSerializer { /**接收注解脱敏类型*/ private SensitiveType type; /**接收可查看的权限*/ private String permission; public SensitiveInfoSerialize() { } public SensitiveInfoSerialize(final SensitiveType type ,final String permission ) { this.type = type; this.permission = permission; } /**序列化的逻辑处理 */ @Override public void serialize(final String s, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException, JsonProcessingException { if (hasPermission(permission)){ //有权限,显示原文 jsonGenerator.writeString(s); return; } switch (this.type) { case CHINESE_NAME: { jsonGenerator.writeString(SensitiveInfoUtils.chineseName(s)); break; } case ID_CARD: { jsonGenerator.writeString(SensitiveInfoUtils.idCardNum(s)); break; } case FIXED_PHONE: { jsonGenerator.writeString(SensitiveInfoUtils.fixedPhone(s)); break; } case MOBILE_PHONE: { jsonGenerator.writeString(SensitiveInfoUtils.mobilePhone(s)); break; } case ADDRESS: { jsonGenerator.writeString(SensitiveInfoUtils.address(s, 4)); break; } case EMAIL: { jsonGenerator.writeString(SensitiveInfoUtils.email(s)); break; } case BANK_CARD: { jsonGenerator.writeString(SensitiveInfoUtils.bankCard(s)); break; } case CNAPS_CODE: { jsonGenerator.writeString(SensitiveInfoUtils.cnapsCode(s)); break; } } } /**自定义注解被拦截后的回调函数*/ @Override public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,final BeanProperty beanProperty) throws JsonMappingException { if (beanProperty != null) { // 为空直接跳过 if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) { // 非 String 类直接跳过 SensitiveInfo sensitiveInfo = beanProperty.getAnnotation(SensitiveInfo.class); if (sensitiveInfo == null) { sensitiveInfo = beanProperty.getContextAnnotation(SensitiveInfo.class); } if (sensitiveInfo != null) { // 如果能得到注解,就将注解的 value 传入 SensitiveInfoSerialize return new SensitiveInfoSerialize(sensitiveInfo.type(),sensitiveInfo.permission()); } } return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty); } return serializerProvider.findNullValueSerializer(beanProperty); } /** * 判断接口是否有任意xxx,xxx权限 * 这里写的是Spring Security的判断方式,如果使用的是shiro可以参考注掉的那两行代码,如果使用的其他的认证框架,自行参考实现逻辑 * @param permission 权限符 * @return {boolean} */ public boolean hasPermission(String permission) { /**Spring Securty验证方式*/ if (StringUtils.isEmpty(permission)) { return true; } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { return false; } Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); return authorities.stream() .map(GrantedAuthority::getAuthority) .filter(StringUtils::hasText) .anyMatch(x -> PatternMatchUtils.simpleMatch(permission, x)); /**Shiro 验证方式*/ // Subject subject = SecurityUtils.getSubject(); // return subject.isPermitted(permission); } }

3. 类型枚举实现

public enum SensitiveType { /** * 中文名 */ CHINESE_NAME, /** * 身份证号 */ ID_CARD, /** * 座机号 */ FIXED_PHONE, /** * 手机号 */ MOBILE_PHONE, /** * 地址 */ ADDRESS, /** * 电子邮件 */ EMAIL, /** * 银行卡 */ BANK_CARD, /** * 公司开户银行联号 */ CNAPS_CODE }

4. 密文处理工具实现

import org.apache.commons.lang3.StringUtils; public class SensitiveInfoUtils { /** * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**> */ public static String chineseName(final String fullName) { if (StringUtils.isBlank(fullName)) { return ""; } final String name = StringUtils.left(fullName, 1); return StringUtils.rightPad(name, StringUtils.length(fullName), "*"); } /** * [中文姓名] 只显示第一个汉字,其他隐藏为2个星号<例子:李**> */ public static String chineseName(final String familyName, final String givenName) { if (StringUtils.isBlank(familyName) || StringUtils.isBlank(givenName)) { return ""; } return chineseName(familyName givenName); } /** * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762> */ public static String idCardNum(final String id) { if (StringUtils.isBlank(id)) { return ""; } return StringUtils.left(id, 3).concat(StringUtils .removeStart(StringUtils.leftPad(StringUtils.right(id, 3), StringUtils.length(id), "*"), "***")); } /** * [固定电话] 后四位,其他隐藏<例子:****1234> */ public static String fixedPhone(final String num) { if (StringUtils.isBlank(num)) { return ""; } return StringUtils.leftPad(StringUtils.right(num, 4), StringUtils.length(num), "*"); } /** * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234> */ public static String mobilePhone(final String num) { if (StringUtils.isBlank(num)) { return ""; } return StringUtils.left(num, 2).concat(StringUtils .removeStart(StringUtils.leftPad(StringUtils.right(num, 2), StringUtils.length(num), "*"), "***")); } /** * [地址] 只显示到地区,不显示详细地址;我们要对个人信息增强保护<例子:北京市海淀区****> * * @param sensitiveSize 敏感信息长度 */ public static String address(final String address, final int sensitiveSize) { if (StringUtils.isBlank(address)) { return ""; } final int length = StringUtils.length(address); return StringUtils.rightPad(StringUtils.left(address, length - sensitiveSize), length, "*"); } /** * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com> */ public static String email(final String email) { if (StringUtils.isBlank(email)) { return ""; } final int index = StringUtils.indexOf(email, "@"); if (index <= 1) { return email; } else { return StringUtils.rightPad(StringUtils.left(email, 1), index, "*") .concat(StringUtils.mid(email, index, StringUtils.length(email))); } } /** * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234> */ public static String bankCard(final String cardNum) { if (StringUtils.isBlank(cardNum)) { return ""; } return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart( StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******")); } /** * [公司开户银行联号] 公司开户银行联行号,显示前两位,其他用星号隐藏,每位1个星号<例子:12********> */ public static String cnapsCode(final String code) { if (StringUtils.isBlank(code)) { return ""; } return StringUtils.rightPad(StringUtils.left(code, 2), StringUtils.length(code), "*"); } }

对应VO实体类加上注解

这一开始是想从数据库出库时就加密,但考虑到逻辑处理代码中可能会用到手机号做关联或者查询,最后只在视图对象VO上加了注解,只有返回给界面时才会加密,这就需要吧VO(视图对象)、PO(持久化对象)、DTO(数据传输对象)拆分开


@Data public class StoreInfoResponse implements Serializable { private static final long serialVersionUID=1L; //默认type是手机号加密,所以这省略了,没写 @SensitiveInfo(permission="sensitive_addressPhone_view") private String addressPhone; }

验证方式

main函数

因为这里使用的是Jackson 序列化,所以只有使用Jackson序列化时才能生效,使用fastjson序列化是不会生效的,这里我们使用手动序列化进行测试,实际项目中可以用页面请求的方式测试权限相关逻辑。


public static void main(String[] args) throws JsonProcessingException { StoreInfoResponse response = new StoreInfoResponse (); response.setAddressPhone("18231148754"); String jsonstr = new ObjectMapper().writeValueAsString(response); System.out.println("jackson序列化>>>" jsonstr); System.out.println("fastjson序列化>>>" JSON.toJSONString(response)); }

运行调试



运行结果:

jackson序列化脱敏后>>>{"addressPhone":"18******54"} fastjson序列化未进行脱敏加密>>>{"addressPhone":"18231148754"} 进程已结束,退出代码为 0




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至123456@qq.com 举报,一经查实,本站将立刻删除。

联系我们

工作日:9:30-18:30,节假日休息