SpringBoot自定义注解+数据脱敏(一看就懂)

一、注解的介绍        

        在Java中,注解(Annotation)是JDK5.0引入的一个重要特性。注解提供了一种元数据机制,可以用于描述和定义程序中的元素(类、方法、成员变量等)。注解是一种能被添加到java源代码中的元数据,方法、类、参数和包都可以用注解来修饰。注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。

二、创建一个注解的基本元素

修饰符
访问修饰符必须为public,不写默认为pubic;
关键字
关键字为@interface;
注解名称
注解名称为自定义注解的名称
注解类型元素
注解类型元素是注解中内容,根据需要标志参数

三、SpringBoot自定义注解 

3.1  创建注解

        自定义注解需要使用@interface关键字进行定义,并且需要指定该注解的作用目标(如类、方法、字段等)和需要包含的元数据信息(如属性及其默认值等)。

3.1.1  元注解(@Target、@Retention、@Inherited、@Documented)

@Target、@Retention、@Inherited、@Documented,这四个注解就是元注解,元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的元注解类型,它们被用来提供对其它 注解类型作标志操作(可以理解为最小的注解,基础注解)

  • @Target:用于描述注解的使用范围,该注解可以使用在什么地方

备注:例如@Target(ElementType.METHOD),标志的注解使用在方法上。如果我们将这个注解标志在类上,就会报错。

  • @Retention:表明该注解的生命周期
生命周期类型描述
RetentionPolicy.SOURCE编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASSJVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME由JVM 加载,包含在类文件中,在运行时可以被获取到

@Inherited:是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。
@Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化


SpringBoot自定义注解 只需要关注@Target、@Retention这两个元注解即可

这是一个自定义注解的示例:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.TYPE) // 指定这个注解可以用在类上
@Retention(RetentionPolicy.RUNTIME) // 指定这个注解在运行时可用
public @interface MyCustomAnnotation {String value() default ""; // 这是一个默认属性
}

上面的代码定义了一个名为MyCustomAnnotation的自定义注解。这个注解可以用在类上,并且在运行时可用。这个注解有一个属性value,它的默认值是空字符串。

3.2  使用注解

        在需要使用该注解的类、方法或字段上添加该注解即可。

@MyCustomAnnotation("This is a custom annotation example")
public class MyClass {// ...
}

在这个例子中,我们在MyClass类上使用了MyCustomAnnotation注解,并设置了value属性的值为"This is a custom annotation example"。

3.3  处理注解

        可以使用Spring Boot的AOP功能或其他方式编写拦截器来处理带有自定义注解的类、方法或字段。在拦截器中,可以读取自定义注解的元数据信息,并执行相应的逻辑(如打印注解的值或执行其他操作)。

例如,你可以使用AOP的@Before注解来指定在处理带有自定义注解的类之前执行的逻辑:

@Aspect
@Component
public class MyCustomAnnotationAspect {@Before("@annotation(MyCustomAnnotation)")public void handleMyCustomAnnotation(MyCustomAnnotation annotation) {System.out.println("Handling custom annotation: " + annotation.value());}
}

在这个例子中,我们在一个AOP切面中定义了一个方法,这个方法将在处理带有@MyCustomAnnotation注解的类之前执行。在方法中,我们可以读取MyCustomAnnotation注解的元数据信息(即value属性的值),并执行相应的逻辑。

四、自定义注解的使用DEMO

java自定义注解的使用范围:
        一般我们可以通过注解来实现一些重复的逻辑,就像封装了的一个方法,可以用在一些权限校验、字段校验、字段属性注入、保存日志、缓存

1.权限校验注解(校验token)

有些项目进入到接口后调用公用方法来校验token,这样看起来代码就有点不优雅,我们可以写自定义注解来进行校验token。
例如有个项目,前端是把token放到json里面传到后端(也有一些项目放到请求头的header里面,方式一样),没用注解之前,我们可能是通过调用公共的方法去校验token,如validateToken(token),然后每个接口都有这一段代码,我们用注解的模式替换

1) 首先我们创建一个注解,标志那些类需要校验token

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AppAuthenticationValidate {//必填参数String[] requestParams() default {};
}

2) 然后再创建一个AOP切面类来拦截这个注解

拦截使用这个注解的方法,同时获取注解上面的requestParams参数,校验json里面必填的属性是否存在

@Aspect
@Component
@Slf4j
public class AppAuthenticationValidateAspect {@Reference(check = false, timeout = 18000)private CommonUserService commonUserService;@Before("@annotation(cn.com.bluemoon.admin.web.common.aspect.AppAuthenticationValidate)")public void repeatSumbitIntercept( JoinPoint joinPoint) {//获取接口的参数Object[] o = joinPoint.getArgs();JSONObject jsonObject = null;String[] parameterNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();String source = null;for(int i=0;i<parameterNames.length;i++){String paramName = parameterNames[i];if(paramName.equals("source")){//获取token来源source = (String)o[i];}if(paramName.equals("jsonObject")){jsonObject = (JSONObject) o[i];}}if(jsonObject == null){throw new WebException(ResponseConstant.ILLEGAL_PARAM_CODE, ResponseConstant.ILLEGAL_PARAM_MSG);}String token = jsonObject.getString("token");if(StringUtils.isBlank(token)){throw new WebException(ResponseConstant.TOKEN_EXPIRED_CODE,"登录超时,请重新登录");}MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();AppAuthenticationValidate annotation = method.getAnnotation(AppAuthenticationValidate.class);String[] requestParams = annotation.requestParams();//校验必填参数ParamsValidateUtil.isNotBlank(jsonObject,requestParams);ResponseBean<String> response = null;if(StringUtils.isBlank(source)){response = this.commonUserService.checkAppToken(token);}else{response = this.commonUserService.checkAppTokenByAppType(token,source);}if (response.getIsSuccess() && ResponseConstant.REQUEST_SUCCESS_CODE == response.getResponseCode()) {String empCode = response.getData();log.info("---token ={}, empCode={}--", token, empCode);jsonObject.put(ProcessParamConstant.APP_EMP_CODE,empCode);} else {log.info("---token验证不通过,token ={}---", token);throw new WebException(ResponseConstant.TOKEN_EXPIRED_CODE, "登录超时,请重新登录");}}
}

3)把注解加在需要校验的接口方法上

这个注解同时校验了必填字段,校验完token后同时会把token的用户信息加在json对象里面

备注:有些项目会把token放到请求头header中,处理方式类似

2.角色校验注解(springsecurity中的角色校验)

我们在使用springsecurity有一个注解@PreAuthorize可以作用在类或方法上,用来校验是否有权限访问,我们可以模仿这个注解,写一个我们自定义注解来实现同样的功能

1)创建一个自定义注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface RoleAuthorize {String[] value() default {};
}

2)创建一个拦截器

这个拦截器拦截所有访问路径的url,如果访问方法上带有我们创建的自定义注解RoleAuthorize ,则获取这个注解上限定的访问角色,方法没有注解再获取这个类是否有这个注解,如果这个类也没有注解,则这个类的访问没有角色限制,放行,如果有则校验当前用户的springsecurity是否有这个角色,有则放行,没有则抛出和springsecurity一样的异常AccessDeniedException,全局异常捕获这个异常,返回状态码403(表示没有权限访问)
 

@Component
public class RoleInterceptor extends HandlerInterceptorAdapter{@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {HandlerMethod handlerMethod = (HandlerMethod)handler;//在方法上寻找注解RoleAuthorize permission = handlerMethod.getMethodAnnotation(RoleAuthorize.class);if (permission == null) {//方法不存在则在类上寻找注解则在类上寻找注解permission = handlerMethod.getBeanType().getAnnotation(RoleAuthorize.class);}//如果没有添加权限注解则直接跳过允许访问if (permission == null) {return true;}//获取注解中的值String[] validateRoles = permission.value();//校验是否含有对应的角色for(String role : validateRoles){//从springsecurity的上下文获取用户角色是否存在当前的角色名称if(AuthUserUtils.hasRole("ROLE_"+role)){return true;}}throw  new AccessDeniedException("没有权限访问当前接口");}}

3)配置拦截器

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {@Autowiredprivate RoleInterceptor roleInterceptor;/*** 添加拦截器** @param registry*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(roleInterceptor).addPathPatterns("/**");super.addInterceptors(registry);}}

备注:
1.这里添加拦截器可以继承WebMvcConfigurerAdapter (已过时,在springboot2.0是继承 WebMvcConfigurationSupport或实现WebMvcConfigurer)
2.WebMvcConfigurationSupport–>不需要返回逻辑视图,可以选择继承此类.WebMvcCofigurer–>返回逻辑视图,可以选择实现此方法,重写addInterceptor方法
3.继承webmvcconfigurationsupport之后就没有springmvc的自动配置了 建议实现WebMvcConfigurer

4)把注解加到接口的类或方法上验证

可以看到接口会返回无权限访问

3.数据脱敏

1)自定义Jackson注解

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveJsonSerializer.class)
public @interface Sensitive {//脱敏策略SensitiveStrategy strategy();
}

2) 指定脱敏策略,这个规则根据业务具体需求去制定,下面只做演示

import java.util.function.Function;/*** 脱敏策略,枚举类,针对不同的数据定制特定的策略*/
public enum SensitiveStrategy {/*** 用户名*/USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")),/*** 身份证*/ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\w{4})", "$1****$2")),/*** 手机号*/PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),/*** 地址*/ADDRESS(s -> s.replaceAll("(\\S{3})\\S{2}(\\S*)\\S{2}", "$1****$2****"));private final Function<String, String> desensitizer;SensitiveStrategy(Function<String, String> desensitizer) {this.desensitizer = desensitizer;}public Function<String, String> desensitizer() {return desensitizer;}
}

3) 定制JSON序列化实现

import com.fasterxml.jackson.core.JsonGenerator;
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 java.io.IOException;
import java.util.Objects;/*** 序列化注解自定义实现* JsonSerializer<String>:指定String 类型,serialize()方法用于将修改后的数据载入*/
public class SensitiveJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {private SensitiveStrategy strategy;@Overridepublic void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throwsIOException {gen.writeString(strategy.desensitizer().apply(value));}/*** 获取属性上的注解属性*/@Overridepublic JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throwsJsonMappingException {Sensitive annotation = property.getAnnotation(Sensitive.class);if (Objects.nonNull(annotation)&&Objects.equals(String.class,property.getType().getRawClass())) {this.strategy = annotation.strategy();return this;}return prov.findValueSerializer(property.getType(), property);}
}

4) 新增User类,并对需要脱敏的字段添加注解,并指定脱敏策略 

import com.badao.demo.sensitive.Sensitive;
import com.badao.demo.sensitive.SensitiveStrategy;
import lombok.Data;
import java.io.Serializable;@Data
public class User implements Serializable {private static final long serialVersionUID = -5514139686858156155L;private Integer id;private Integer userId;@Sensitive(strategy = SensitiveStrategy.USERNAME)private String name;private Integer age;}

5)  编写controller进行测试

@RequestMapping("user")
@RestController
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("save")public String save() {User user = new User();user.setUserId(new Random().nextInt( 1000 ) + 1);user.setName("badao"+user.getUserId());user.setAge(new Random().nextInt( 80 ) + 1);userService.insert(user);return "save success";}@RequestMapping("select")public User select() {List<User> all = userService.findAll();return all.size()>0?all.get(0):new User();}
}

6)  测试效果

其他更多的可参考:springboot项目中自定义注解的使用总结、java自定义注解实战(常用注解DEMO)_自定义方法注解demo-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/210357.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[autojs]利用console实现悬浮窗日志输出

"ui"; ui.layout(<vertical><button id"autoFloatWindow" text"开启悬浮窗" textSize"15sp" /><button id"autoService" text"开启无障碍服务" textSize"15sp" /><button id"…

SA实战 ·《SpringCloud Alibaba实战》第14章-服务网关加餐:SpringCloud Gateway核心技术

大家好,我是冰河~~ 一不小心《SpringCloud Alibaba实战》专栏都更新到第14章了,再不上车就跟不上了,小伙伴们快跟上啊! 在《SpringCloud Alibaba实战》专栏前面的文章中,我们实现了用户微服务、商品微服务和订单微服务之间的远程调用,并且实现了服务调用的负载均衡。也基…

【SpringMVC】 参数传递

一.项目目录 SpringBoot项目创建之后会生成很多目录 删除不需要的这四个文件/目录 目录 二.Spring MVC 和 MVC Spring MVC(Spring Web MVC) Spring Web MVC 是⼀个 Web 框架 MVC : Model View Controller 它是一种思想 , 它把一个项目分成了三个部分. View视图层 界面显示…

AnalyticDB for PostgreSQL 实时数据仓库上手指南

AnalyticDB for PostgreSQL 实时数据仓库上手指南 2019-04-016601 版权 本文涉及的产品 云原生数据仓库 ADB PostgreSQL&#xff0c;4核16G 50GB 1个月 推荐场景&#xff1a; 构建的企业专属Chatbot 立即试用 简介&#xff1a; AnalyticDB for PostgreSQL 提供企业级数…

JS PromiseLike 的判定与使用

目录 一. $.ajax()返回值遇到的问题二. Promise A 规范三. 判断是否为PromiseLike3.1 判断ES6的new Promise()3.2 判断包含then方法的对象3.3 判断$.ajax()返回的对象 一. $.ajax()返回值遇到的问题 当我们执行如下js代码时&#xff0c;可以看到$.ajax()执行后&#xff0c;得到…

Unity收费对谁影响最大

Unity的收费政策对以下几类人群影响最大&#xff1a; 游戏开发商&#xff1a;Unity收费政策中最直接的影响对象就是游戏开发商。对于那些使用Unity引擎制作游戏的开发商来说&#xff0c;他们将需要考虑新的许可证费用和服务费用&#xff0c;这可能会对他们的盈利和发展产生影响…

【分子指纹】化学分子指纹的概念和应用

Drug Discov Today&#xff5c;化学分子指纹的概念和应用 2022年9月13日&#xff0c;哈尔滨医科大学药物基因组信息学教研室陈秀杰教授、哈尔滨医科大学生物信息科学与技术学院解洪波副教授团队在期刊Drug Discovery Today上发表论文“Concepts and applications of chemical fi…

计算机网络之运输层

一、概述 物理层、数据链路层以及网络层它们共同解决了将主机通过异构网络互联起来所面临的的问题&#xff0c;实现了主机到主机的通信 但实际上在计算机网络中进行通信的真正实体是位于通信两端主机中的进程 如何为运行在不同主机上的应用进程提供直接的通信服务时运输层的任务…

GoLand 2023.2.5(GO语言集成开发工具环境)

GoLand是一款专门为Go语言开发者打造的集成开发环境&#xff08;IDE&#xff09;。它能够提供一系列功能&#xff0c;如代码自动完成、语法高亮、代码格式化、代码重构、代码调试等等&#xff0c;使编写代码更加高效和舒适。 GoLand的特点包括&#xff1a; 1. 智能代码补全&a…

Grafana如何实现折线柱状图

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一份大厂面试资料《史上最全大厂面试题》&#xff0c;Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

配电室智慧运维

配电室智慧运维是一种基于先进技术和智能化管理理念的配电室运维模式。依托电易云-智慧电力物联网&#xff0c;它结合云计算、物联网、大数据等技术&#xff0c;对配电室进行全方位的智能化管理和运维&#xff0c;旨在提高配电室的运行效率、安全性和可靠性。 配电室智慧运维的…

MDK AC5和AC6是什么?在KEIL5中添加和选择ARMCC版本

前言 看视频有UP主提到“AC5”“AC6”这样的词&#xff0c;一开始有些不理解&#xff0c;原来他说的是ARMCC版本。 keil自带的是ARMCC5&#xff0c;由于ARMCC5已经停止维护了&#xff0c;很多开发者会选择ARMCC6。 在维护公司“成年往事”项目可能就会遇到新KEIL旧版本编译器…