代理模式。
需求背景返回前端的用户信息包含手机号等敏感内容,并在系统上已经有大量接口。现拿到需求,需要在返回前端的对象中,找到手机号的属性并将手机号加密。
现状:
分散在各处的接口中都有手机号需要加密。
不同对象里对手机号命名可能不同,如 String number ; String phone。
思路:
按接口逐个添加加密逻辑过于繁琐,不利后续维护。
考虑创建一个Util类但不好兼容不同用户信息对象的类型,且属性名称也不统一。
考虑使用自定义注解+代理模式,即AOP思想
@Target(ElementType.METHOD) // 定义注解的作用目标**作用范围字段、枚举的常量/方法 @Retention(RetentionPolicy.RUNTIME) // RUNTIME:运行时可以获取相关属性。 @documented // 说明该注解将被包含在javadoc中 public @interface PhoneAccess { String phoneFiled() default "phone"; String phoneResource() default "phoneAccessBtn"; }
自定义注解创建后,虽然可以在系统任意方法上使用@PhoneAccess,但没有具体意义。
创建Aspect
- 使用@Aspect标记这是一个Aspect类
- 使用@Around("@annotation(pa)") 对注解刚刚创建的自定义注解:@PhoneAccess 环绕通知
- 方法需要传入 ProceedingJoinPoint point, PhoneAccess pa 。
- 利用 point.proceed(); 先执行被代理的目标函数
- PhoneAccess pa 将被代理的目标函数上的对象信息传入,再做加密
@Aspect // 使用@Aspect标记这是一个Aspect @Component public class PhoneAspect { private final Logger logger = LoggerFactory.getLogger(PhoneAspect.class); private static ObjectMapper mapper = new ObjectMapper(); @Around("@annotation(pa)") // 对注解:@PhoneAccess 环绕通知 public Object serviceLog(ProceedingJoinPoint point, PhoneAccess pa) throws Throwable { Object result = point.proceed(); // 执行目标函数 // 代理的内容 if(ObjectUtils.isEmpty(result)){ return result; } if(ObjectUtils.isEmpty(pa)){ return result; } Map map = mapper.readValue(mapper.writevalueAsString(result), Map.class); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpSession s = request.getSession(); if(ObjectUtils.isEmpty(s)){ return result; } Object o = s.getAttribute(UnifiedProfileConstants.UNIFIED_PROFILE); UnifiedProfile unifiedProfile = (UnifiedProfile)o; ListbuttonResourceList = unifiedProfile.getButtonList(); if(ObjectUtils.isEmpty(buttonResourceList)){ return result; } //判断是否资源权限 Boolean hasAccess = hasPhoneAccess(buttonResourceList, pa.phoneResource()); if(hasAccess){ return result; } //遮盖电话号码 recursiveAnalysis(map, pa.phoneFiled(), hasAccess); Object newResult = null; try { newResult = mapper.readValue(mapper.writevalueAsString(map), result.getClass()); } catch (Exception e) { e.printStackTrace(); return result; } return newResult; } private Boolean hasPhoneAccess(List buttonResources, String phoneResource) { Boolean hasAccess = Boolean.FALSE; if(!ObjectUtils.isEmpty(buttonResources)){ for(ButtonResource br: buttonResources){ if(br.getKey().equals(phoneResource)){ hasAccess = br.getHasAccess(); } } } return hasAccess; } public static void recursiveAnalysis(Object m, String fieldName, Boolean hasAccess){ Map mp = null; if(m instanceof Map || m instanceof linkedHashMap){ mp = (linkedHashMap)m; for(Iterator ite = mp.entrySet().iterator(); ite.hasNext();){ Map.Entry e = (Map.Entry) ite.next(); if(e.getValue() instanceof String){ if(!ObjectUtils.isEmpty(e.getValue()) && e.getValue().toString().length() == 11){ if(!hasAccess && e.getKey().equals(fieldName)){ String str = "****"; StringBuilder sb = new StringBuilder(e.getValue().toString()); sb.replace(3, 7, str); e.setValue(sb.toString()); } } }else if(e.getValue() instanceof linkedHashMap){ recursiveAnalysis((linkedHashMap)e.getValue(), fieldName, hasAccess); }else if(e.getValue() instanceof ArrayList){ recursiveAnalysis((ArrayList)e.getValue(), fieldName, hasAccess); } } } List ls = null; if(m instanceof List || m instanceof ArrayList){ ls = (ArrayList)m; for(int i=0;i 自此 可以在任意需要加密处理的接口打上注解@PhoneAccess,指定当前返回值中存放手机号的属性名,就能完成加密。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)