利用注解开发一个通用的JWT前置校验功能

设计的预期:

系统中并不是所有的应用都需要JWT前置校验,这就需要额外设计一个注解Annotation来标识这个方法需要JWT前置校验.例如:

@GetMapping("/dosth")
//加入自定义注解JwtToken,该Controller方法在运行前就需要进行JWT校验
//JWT校验通过执行Controller方法
//JWT校验未通过则直接返回校验失败
@JwtToken
public ResponseObject doSth(){
return new ResponseObject("...");
}

开发步骤

1. pom.xml引入JJWT

<!--JJWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>

2. application.yml增加appkey秘钥

#JWT秘钥与Auth-Service保持一致
app:
secretKey: 1234567890-1234567890-1234567890

3. 创建自定义注解@JwtToken

//这个注解只能用在方法上

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) //运行时注解生效
public @interface JwtToken {
//当required=true说明请求必须包含token,未包含则报错
//当required=false说明token不是必须的,不会中断请求的传递,交由后面的业务代码处理
boolean required() default true;
}

4. 在ArticleController增加doSth测试方法

  @JwtToken //dosth进行jwt校验
@GetMapping("/dosth")
public ResponseObject dosth() {
return new ResponseObject("业务处理成功");
}

5. 创建TokenInterceptor实现代码拦截JWTToken业务验证逻辑

/**
* 执行目标URI方法前,对存放在请求头中的Jwt进行校验,校验通过执行目标方法,校验失败则提示错误
*/
public class TokenInteceptor implements HandlerInterceptor {
@Value("${app.secretKey}")
private String appKey = null; /**
* 在目标方法执行前先执行preHandle进行前置处理
* @param request 原生请求对象
* @param response 原生响应对象
* @param handler 处理器对象
* @return true-请求向后送达到Controller, false-中断请求立即产生响应
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("TokenInterceptor.preHandle()");
// 如果不是映射到方法直接通过
if(!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
//获取目标方法的Method对象
Method method = handlerMethod.getMethod(); response.setContentType("text/json;charset=utf-8"); ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); //判断目标方法是否包含JwtToken注解
if(method.isAnnotationPresent(JwtToken.class)){
String token = request.getHeader("token");
//判断请求头是否包含token属性,在拥有@JwtToken的方法处理时,未包含token则抛出安全异常
if(token == null){
JwtToken jwtToken = method.getAnnotation(JwtToken.class);
if(jwtToken.required() == true) {
response.setStatus(401);//未认证
ResponseObject<Object> responseObject = new ResponseObject<>("SecurityException", "Token不存在,请检查请求头是否包含Token");
String json = objectMapper.writeValueAsString(responseObject);
response.getWriter().println(json);
return false;
} }else{//token存在时验证JWT的有效性
String base64Key = new BASE64Encoder().encode(appKey.getBytes());
SecretKey key = Keys.hmacShaKeyFor(base64Key.getBytes());
try {
Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
String userJson = claimsJws.getBody().getSubject();
System.out.println(userJson);
User user = objectMapper.readValue(userJson, User.class);
request.setAttribute("$user",user); //将当前登录用户对象保存到请求属性
return true;
}catch (JsonProcessingException e){
e.printStackTrace();
response.setStatus(500);
ResponseObject<Object> responseObject = new ResponseObject<>(e.getClass().getSimpleName(), e.getMessage());
String json = objectMapper.writeValueAsString(responseObject);
response.getWriter().println(json);
return false;
}catch (JwtException e){//Jwt校验失败是抛出异常
e.printStackTrace();
response.setStatus(401);
ResponseObject<Object> responseObject = new ResponseObject<>(e.getClass().getSimpleName(), e.getMessage());
String json = objectMapper.writeValueAsString(responseObject);
response.getWriter().println(json);
return false;
} }
}
return true;
}
}

6. 启动应用,

http://localhost:8100/dosth

7. 将Token中包含的用户数据与业务逻辑绑定,如果Token验证成功,TokenInterceptor会把用户信息转为User对象放入当前请求$user属性中

之后在list方法中,使用@RequestArrtibute()进行提取即可.

/**
* 如果是VIP会员,可以查看所有普通文章与精选文章
* 如果是普通会员,只能查看所有普通文章
*
* @return
*/
@GetMapping("/list")
@JwtToken(required = false)
public ResponseObject list(@RequestAttribute(value = "$user", required = false) User user) {
//request.getAttribute()
System.out.println(user);
return new ResponseObject("0", "success", articleService.list(user));
}

ArticleService增加业务处理代码,根据用户级别查看不同的结果

/**
* 如果是VIP会员,可以查看所有普通文章与精选文章
* 如果是普通会员,只能查看所有普通文章
* @return
*/
public List<Article> list(User user){
int level = 0;
if(user == null || user.getGrade().equals("normal")){
level = 1;
}else if(user.getGrade().equals("vip")){
level = 2;
} List<Article> list = articleMapper.list(level);
for(Article article:list){
ResponseObject<Video> videoResponseObject = videoFeignClient.findByArticleId(article.getArticleId());
article.setVideo(videoResponseObject.getData());
}
return list;
}

ArticleMapper进行修改,增加level参数

@Mapper
public interface ArticleMapper {
@Select("select * from article where article_type <= #{value} order by create_time desc")
public List<Article> list(int level); 至此业务代码改造完毕
用户未登录或普通用户只能看到ArticleType=1的普通文章
而VIP身份用户则可以看到所有文章
}

SpringBoot利用自定义注解实现通用的JWT校验方案的更多相关文章

  1. java 编程基础:【注解】 提取注解信息,利用自定义注解编写测试类,注解绑定事件

    提取注解信息 使用注解修饰了类.方法.成员变量等成员之后,这些注解不会自己生效,必须由开发者提供相应工具来提取并处理注解信息.   Java使用java.lang.annotation.Annotat ...

  2. Springboot+Redisson自定义注解一次解决重复提交问题(含源码)

    前言   项目中经常会出现重复提交的问题,而接口幂等性也一直以来是做任何项目都要关注的疑难点,网上可以查到非常多的方案,我归纳了几点如下:   1).数据库层面,对责任字段设置唯一索引,这是最直接有效 ...

  3. ssm+redis 如何更简洁的利用自定义注解+AOP实现redis缓存

    基于 ssm + maven + redis 使用自定义注解 利用aop基于AspectJ方式 实现redis缓存 如何能更简洁的利用aop实现redis缓存,话不多说,上demo 需求: 数据查询时 ...

  4. 利用Spring AOP自定义注解解决日志和签名校验

    转载:http://www.cnblogs.com/shipengzhi/articles/2716004.html 一.需解决的问题 部分API有签名参数(signature),Passport首先 ...

  5. Java利用自定义注解、反射实现简单BaseDao

    在常见的ORM框架中,大都提供了使用注解方式来实现entity与数据库的映射,这里简单地使用自定义注解与反射来生成可执行的sql语句. 这是整体的目录结构,本来是为复习注解建立的项目^.^ 好的,首先 ...

  6. springboot aop 自定义注解方式实现完善日志记录(完整源码)

    版权声明:本文为博主原创文章,欢迎转载,转载请注明作者.原文超链接 一:功能简介 本文主要记录如何使用aop切面的方式来实现日志记录功能. 主要记录的信息有: 操作人,方法名,参数,运行时间,操作类型 ...

  7. springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)

    https://www.cnblogs.com/wenjunwei/p/9639909.html https://blog.csdn.net/tyrant_800/article/details/78 ...

  8. (转)利用Spring AOP自定义注解解决日志和签名校验

    一.需解决的问题 部分API有签名参数(signature),Passport首先对签名进行校验,校验通过才会执行实现方法. 第一种实现方式(Origin):在需要签名校验的接口里写校验的代码,例如: ...

  9. Springboot使用自定义注解实现简单参数加密解密(注解+HandlerMethodArgumentResolver)

    前言 我黄汉三又回来了,快半年没更新博客了,这半年来的经历实属不易,疫情当头,本人实习的公司没有跟员工共患难, 直接辞掉了很多人.作为一个实习生,本人也被无情开除了.所以本人又得重新准备找工作了. 算 ...

随机推荐

  1. linux下更改文件字符格式为uft-8

    liunx下发布的.net Core 程序,发现短信签名不错误不能发出.后来检查发现配配文件中的字符为乱码才知道是因为字符格式问题. 因为服务器批较多,还是使用命令来解决比较快.使用iconv来更改. ...

  2. linux 常用命令:

    查看启动进程命令 ps -ef |  grep donet 1.vi 进入编辑界面 2.ls,ll 查看命令 3.cd 进入命令 4.i 进入插入界面 5. esc 退出编辑界面 6.输入:冒号,进入 ...

  3. JavaWeb 11_文件上传

    一.操作步骤 1.要有一个form标签,method=post 请求2.form标签的encType属性值必须为multipart/form-data值3.在form标签中使用input type=f ...

  4. Linux移植到自己的开发板(四)问题汇总

    @ 目录 1 使ubuntu支持两个版本的编译链: 2 版本问题: 3 ubuntu版本的vscode下载网速太慢: 4 ubuntu占用空间过大 5 执行make zImage 出错 lzop: n ...

  5. Django之 rest_framework (一基本组件)

    目录 RESTFUL 序列化 视图三部曲 认证与权限组件 解析器 分页 RESTFUL 一.什么是RESTFUL REST与技术无关代表的是一种软件架构风格,REST是Representational ...

  6. 74CMS 3.0 CSRF漏洞

    一. 启动环境 1.双击运行桌面phpstudy.exe软件 2.点击启动按钮,启动服务器环境 二.代码审计 1.双击启动桌面Seay源代码审计系统软件 2.因为74CMS3.0源代码编辑使用GBK编 ...

  7. WIFI-Pumpkin无线钓鱼渗透

    WIFI-Pumpkin无线钓鱼渗透 描述 WiFi-Pumpkin是一款专用于无线环境渗透测试的完整框架,利用该工具可以伪造接入点完成中间人攻击,同时也支持一些其他的无线渗透测试功能.旨在提供更安全 ...

  8. 知识点简单总结——Lyndon分解

    知识点简单总结--Lyndon分解 Lyndon串 定义:一个字符串的最小后缀就是整个串本身. 等效理解:这个串为其所有循环表示中最小的. Lyndon分解 定义:将字符串分割为 $ s_{1} s_ ...

  9. (bzoj4408)[FJOI2016]神秘数(可持久化线段树)

    (bzoj4408)[FJOI2016]神秘数(可持久化线段树) bzoj luogu 对于一个区间的数,排序之后从左到右每一个数扫 如果扫到某个数a时已经证明了前面的数能表示[1,x],那么分情况: ...

  10. CF1487G String Counting (容斥计数)

    传送门 考虑$c[i]>n/3$这个关键条件!最多有2个字母数量超过$n/3$! 没有奇数回文?长度大于3的回文串中间一定是长度为3的回文串,所以合法串一定没有长度=3的回文,也就是$a[i]\ ...