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

设计的预期:

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

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

开发步骤

1. pom.xml引入JJWT

  1. <!--JJWT-->
  2. <dependency>
  3. <groupId>io.jsonwebtoken</groupId>
  4. <artifactId>jjwt-api</artifactId>
  5. <version>0.11.2</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>io.jsonwebtoken</groupId>
  9. <artifactId>jjwt-impl</artifactId>
  10. <version>0.11.2</version>
  11. <scope>runtime</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>io.jsonwebtoken</groupId>
  15. <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
  16. <version>0.11.2</version>
  17. <scope>runtime</scope>
  18. </dependency>

2. application.yml增加appkey秘钥

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

3. 创建自定义注解@JwtToken

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

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

4. 在ArticleController增加doSth测试方法

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

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

  1. /**
  2. * 执行目标URI方法前,对存放在请求头中的Jwt进行校验,校验通过执行目标方法,校验失败则提示错误
  3. */
  4. public class TokenInteceptor implements HandlerInterceptor {
  5. @Value("${app.secretKey}")
  6. private String appKey = null;
  7. /**
  8. * 在目标方法执行前先执行preHandle进行前置处理
  9. * @param request 原生请求对象
  10. * @param response 原生响应对象
  11. * @param handler 处理器对象
  12. * @return true-请求向后送达到Controller, false-中断请求立即产生响应
  13. * @throws Exception
  14. */
  15. @Override
  16. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  17. System.out.println("TokenInterceptor.preHandle()");
  18. // 如果不是映射到方法直接通过
  19. if(!(handler instanceof HandlerMethod)){
  20. return true;
  21. }
  22. HandlerMethod handlerMethod = (HandlerMethod) handler;
  23. //获取目标方法的Method对象
  24. Method method = handlerMethod.getMethod();
  25. response.setContentType("text/json;charset=utf-8");
  26. ObjectMapper objectMapper = new ObjectMapper();
  27. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  28. //判断目标方法是否包含JwtToken注解
  29. if(method.isAnnotationPresent(JwtToken.class)){
  30. String token = request.getHeader("token");
  31. //判断请求头是否包含token属性,在拥有@JwtToken的方法处理时,未包含token则抛出安全异常
  32. if(token == null){
  33. JwtToken jwtToken = method.getAnnotation(JwtToken.class);
  34. if(jwtToken.required() == true) {
  35. response.setStatus(401);//未认证
  36. ResponseObject<Object> responseObject = new ResponseObject<>("SecurityException", "Token不存在,请检查请求头是否包含Token");
  37. String json = objectMapper.writeValueAsString(responseObject);
  38. response.getWriter().println(json);
  39. return false;
  40. }
  41. }else{//token存在时验证JWT的有效性
  42. String base64Key = new BASE64Encoder().encode(appKey.getBytes());
  43. SecretKey key = Keys.hmacShaKeyFor(base64Key.getBytes());
  44. try {
  45. Jws<Claims> claimsJws = Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
  46. String userJson = claimsJws.getBody().getSubject();
  47. System.out.println(userJson);
  48. User user = objectMapper.readValue(userJson, User.class);
  49. request.setAttribute("$user",user); //将当前登录用户对象保存到请求属性
  50. return true;
  51. }catch (JsonProcessingException e){
  52. e.printStackTrace();
  53. response.setStatus(500);
  54. ResponseObject<Object> responseObject = new ResponseObject<>(e.getClass().getSimpleName(), e.getMessage());
  55. String json = objectMapper.writeValueAsString(responseObject);
  56. response.getWriter().println(json);
  57. return false;
  58. }catch (JwtException e){//Jwt校验失败是抛出异常
  59. e.printStackTrace();
  60. response.setStatus(401);
  61. ResponseObject<Object> responseObject = new ResponseObject<>(e.getClass().getSimpleName(), e.getMessage());
  62. String json = objectMapper.writeValueAsString(responseObject);
  63. response.getWriter().println(json);
  64. return false;
  65. }
  66. }
  67. }
  68. return true;
  69. }
  70. }

6. 启动应用,

http://localhost:8100/dosth

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

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

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

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

  1. /**
  2. * 如果是VIP会员,可以查看所有普通文章与精选文章
  3. * 如果是普通会员,只能查看所有普通文章
  4. * @return
  5. */
  6. public List<Article> list(User user){
  7. int level = 0;
  8. if(user == null || user.getGrade().equals("normal")){
  9. level = 1;
  10. }else if(user.getGrade().equals("vip")){
  11. level = 2;
  12. }
  13. List<Article> list = articleMapper.list(level);
  14. for(Article article:list){
  15. ResponseObject<Video> videoResponseObject = videoFeignClient.findByArticleId(article.getArticleId());
  16. article.setVideo(videoResponseObject.getData());
  17. }
  18. return list;
  19. }

ArticleMapper进行修改,增加level参数

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

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. git pull origin master 报错问题解决 fatal: couldn‘t find remote ref master

    报错:fatal: couldn't find remote ref master 解决:使用以下命令 git pull origin main 替代报错命令: git pull origin mas ...

  2. Java的jstack命令使用详解

    jstack命令简介 jstack(Java Virtual Machine Stack Trace)是JDK提供的一个可以生成Java虚拟机当前时刻的线程快照信息的命令行工具.线程快照一般被称为th ...

  3. maven——使用阿里云镜像

    1.在本地的仓库目录下找到settings.xml文件,添加 <mirrors> <mirror> <id>alimaven</id> <name ...

  4. office online在线预览服务与https的tls证书兼容问题

    问题现象:k8s环境配置证书后,无法调用office online 服务,附件无法预览 问题原因:ingress默认启用得是tls1.2,不兼容以下版本 k8s环境解决方法:增加ingress配置,兼 ...

  5. Linux-timedatectl使用

    Linux下使用timedatectl命令时间时区操作详解 timedatectl命令对于RHEL / CentOS 7和基于Fedora 21+的分布式系统来说,是一个新工具,它作为systemd系 ...

  6. 让编程更轻松的 7 个 Visual Studio 扩展 : 以下几个扩展,BuildVision可以用

    是时候升级你最喜欢的IDE了!在这篇文章中,我将介绍一些我最喜欢的与众不同的 Visual Studio 扩展,是它们让我的日常编程工作变得更加轻松.对于一些明摆着的,例如 ReSharper 和 O ...

  7. (数据科学学习手札135)tenacity:Python中最强大的错误重试库

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 我们在编写程序尤其是与网络请求相关的程序, ...

  8. C printf格式化输出

    转载:https://blog.csdn.net/wucz122140729/article/details/98434702 格式化输出       格式化输出的函数有printf.sprintf和 ...

  9. Redis 常见的性能问题都有哪些?如何解决?

    Redis 常见的性能问题都有哪些?如何解决? Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最 ...

  10. Constant Pool和String Constant Pool详解

    Constant Pool常量池的概念: 在讲到String的一些特殊情况时,总会提到String Pool或者Constant Pool,但是我想很多人都不太明白Constant Pool到底是个怎 ...