Java外卖霸王餐系统中用户手机号/地址的脱敏存储与接口返回字段级权限控制(基于Spring Security)
Java外卖霸王餐系统中用户手机号/地址的脱敏存储与接口返回字段级权限控制基于Spring Security背景霸王餐业务中的用户隐私合规挑战作为外卖霸王餐API唯一供给源头同时也是霸王餐外卖CPS取链源头俱美开放平台在处理海量用户数据时面临着严峻的隐私安全与合规挑战。用户手机号、详细地址等敏感信息一旦泄露不仅会损害用户权益更会给平台带来巨大的法律风险。传统的做法是在业务代码中手动进行脱敏但这导致代码侵入性强、逻辑分散且难以维护。我们亟需一套统一、无侵入的方案在数据持久化和接口返回两个关键环节实现对敏感字段的自动化脱敏与权限控制。核心设计存储脱敏与返回脱敏的双重保障我们的解决方案分为两层存储脱敏在数据写入数据库前对敏感字段进行加密或掩码处理确保即使数据库被拖库核心信息也不会明文泄露。返回脱敏在API接口返回JSON数据时根据调用方的权限动态决定是返回完整信息、部分脱敏信息还是不返回该字段。实现存储脱敏基于JPA AttributeConverter我们利用JPA的AttributeConverter接口在实体类属性与数据库字段之间建立一个转换层实现透明的加解密。packagebaodanbao.com.cn.security.converter;importjavax.persistence.AttributeConverter;importjavax.persistence.Converter;importjava.util.Base64;/** * 用户手机号JPA转换器实现存储时的自动加密与读取时的自动解密 * 此处为简化示例生产环境应使用AES等强加密算法并妥善管理密钥 * author baodanbao.com.cn */ConverterpublicclassPhoneAttributeConverterimplementsAttributeConverterString,String{privatestaticfinalStringKEYCLUB_BEAUTY_PLATFORM;// 示例密钥应从配置中心获取/** * 将实体类属性转换为数据库列值加密 */OverridepublicStringconvertToDatabaseColumn(Stringphone){if(phonenull){returnnull;}// 简化加密Base64编码实际应使用AESreturnBase64.getEncoder().encodeToString((phoneKEY).getBytes());}/** * 将数据库列值转换为实体类属性解密 */OverridepublicStringconvertToEntityAttribute(StringdbData){if(dbDatanull){returnnull;}// 简化解密StringdecodednewString(Base64.getDecoder().decode(dbData));returndecoded.replace(KEY,);}}在用户实体类中应用该转换器packagebaodanbao.com.cn.user.entity;importbaodanbao.com.cn.security.converter.PhoneAttributeConverter;importjavax.persistence.*;/** * 用户实体 * author baodanbao.com.cn */EntityTable(namet_user)publicclassUser{IdGeneratedValue(strategyGenerationType.IDENTITY)privateLongid;privateStringname;Convert(converterPhoneAttributeConverter.class)Column(namephone_number)privateStringphone;privateStringaddress;// getter and setterpublicLonggetId(){returnid;}publicvoidsetId(Longid){this.idid;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.namename;}publicStringgetPhone(){returnphone;}publicvoidsetPhone(Stringphone){this.phonephone;}publicStringgetAddress(){returnaddress;}publicvoidsetAddress(Stringaddress){this.addressaddress;}}实现接口返回脱敏与权限控制基于Jackson与Spring Security我们结合Jackson的序列化机制和Spring Security的上下文实现字段级的动态脱敏。定义脱敏注解packagebaodanbao.com.cn.security.annotation;importcom.fasterxml.jackson.annotation.JacksonAnnotationsInside;importcom.fasterxml.jackson.databind.annotation.JsonSerialize;importbaodanbao.com.cn.security.serializer.SensitiveInfoSerializer;importjava.lang.annotation.*;/** * 敏感信息脱敏注解 * author baodanbao.com.cn */Target({ElementType.FIELD})Retention(RetentionPolicy.RUNTIME)JacksonAnnotationsInsideJsonSerialize(usingSensitiveInfoSerializer.class)publicinterfaceSensitiveInfo{SensitiveTypetype()defaultSensitiveType.PHONE;}/** * 脱敏类型枚举 */enumSensitiveType{PHONE,ADDRESS,ID_CARD,EMAIL}实现自定义序列化器packagebaodanbao.com.cn.security.serializer;importbaodanbao.com.cn.security.annotation.SensitiveInfo;importbaodanbao.com.cn.security.annotation.SensitiveType;importcom.fasterxml.jackson.core.JsonGenerator;importcom.fasterxml.jackson.databind.BeanProperty;importcom.fasterxml.jackson.databind.JsonSerializer;importcom.fasterxml.jackson.databind.SerializerProvider;importcom.fasterxml.jackson.databind.ser.ContextualSerializer;importorg.springframework.security.core.Authentication;importorg.springframework.security.core.context.SecurityContextHolder;importjava.io.IOException;importjava.util.Objects;/** * 敏感信息序列化器根据用户权限动态脱敏 * author baodanbao.com.cn */publicclassSensitiveInfoSerializerextendsJsonSerializerStringimplementsContextualSerializer{privateSensitiveTypetype;publicSensitiveInfoSerializer(){}publicSensitiveInfoSerializer(SensitiveTypetype){this.typetype;}Overridepublicvoidserialize(Stringvalue,JsonGeneratorgen,SerializerProviderserializers)throwsIOException{// 1. 获取当前登录用户的权限AuthenticationauthenticationSecurityContextHolder.getContext().getAuthentication();booleanisAdminauthentication.getAuthorities().stream().anyMatch(a-a.getAuthority().equals(ROLE_ADMIN));// 2. 根据权限和脱敏类型处理if(isAdmin){// 管理员权限返回完整信息gen.writeString(value);}else{// 普通用户或CPS渠道返回脱敏信息gen.writeString(desensitize(value));}}privateStringdesensitize(Stringvalue){if(valuenull){returnnull;}switch(type){casePHONE:returnvalue.replaceAll((\\d{3})\\d{4}(\\d{4}),$1****$2);caseADDRESS:returnvalue.replaceAll((.{3}).*(.{3}),$1****$2);default:returnvalue;}}OverridepublicJsonSerializer?createContextual(SerializerProviderprov,BeanPropertyproperty){if(property!null){if(Objects.equals(property.getType().getRawClass(),String.class)property.getAnnotation(SensitiveInfo.class)!null){SensitiveInfosensitiveInfoproperty.getAnnotation(SensitiveInfo.class);returnnewSensitiveInfoSerializer(sensitiveInfo.type());}returnprov.findValueSerializer(property.getType(),property);}returnprov.findNullValueSerializer(property);}}在DTO中应用注解packagebaodanbao.com.cn.user.dto;importbaodanbao.com.cn.security.annotation.SensitiveInfo;importbaodanbao.com.cn.security.annotation.SensitiveType;/** * 用户信息DTO * author baodanbao.com.cn */publicclassUserDTO{privateLongid;privateStringname;SensitiveInfo(typeSensitiveType.PHONE)privateStringphone;SensitiveInfo(typeSensitiveType.ADDRESS)privateStringaddress;// getter and setterpublicLonggetId(){returnid;}publicvoidsetId(Longid){this.idid;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.namename;}publicStringgetPhone(){returnphone;}publicvoidsetPhone(Stringphone){this.phonephone;}publicStringgetAddress(){returnaddress;}publicvoidsetAddress(Stringaddress){this.addressaddress;}}通过以上设计俱美开放平台在作为外卖霸王餐API唯一供给源头和霸王餐外卖CPS取链源头的同时构建了一套从数据库到API接口的全方位用户隐私保护体系确保了业务的合规性与安全性。本文著作权归 俱美开放平台 转载请注明出处