JAVA 实现 JWT

  1. 引入JWT依赖,由于是基于Java,所以需要的是java-jwt
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version>
</dependency>
  1. 自定义注解用于判断是否需要验证
  • 用来跳过验证的PassToken
 @Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface PassToken {boolean required() default true;}
  • 需要登录才能进行操作的注解UserLoginToken
@Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface UserLoginToken {boolean required() default true;}

@Target:注解的作用目标

  • @Target(ElementType.TYPE)——接口、类、枚举、注解
  • @Target(ElementType.FIELD)——字段、枚举的常量
  • @Target(ElementType.METHOD)——方法
  • @Target(ElementType.PARAMETER)——方法参数
  • @Target(ElementType.CONSTRUCTOR) ——构造函数
  • @Target(ElementType.LOCAL_VARIABLE)——局部变量
  • @Target(ElementType.ANNOTATION_TYPE)——注解
  • @Target(ElementType.PACKAGE)——包
    @Retention:注解的保留位置
    RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。 RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。 RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
    @Document:说明该注解将被包含在javadoc中
    @Inherited:说明子类可以继承父类中的该注解

3.编写token的生成方法

  • 生成不携带自定义信息的 JWT token
  1. 构建头部信息
 Map<String, Object> map = new HashMap<String, Object>();map.put("alg", "HS256");map.put("typ", "JWT");
  1. 构建密钥信息
 Algorithm algorithm = Algorithm.HMAC256("secret");

Algorithm.HMAC256(): 使 HS256 生token,唯一密钥可以保存在服务端。“secret” 为相应的密钥

  1. 我们通过定义注册和自定义声明 并组合头部信息和密钥信息生成jwt token
 String token = JWT.create().withHeader(map)// 设置头部信息 Header .withIssuer("SERVICE")//设置 载荷 签名是有谁生成 例如 服务器.withSubject("this is test token")//设置 载荷 签名的主题// .withNotBefore(new Date())//设置 载荷 定义在什么时间之前,该jwt都是不可用的..withAudience("APP")//设置 载荷 签名的观众 也可以理解谁接受签名的.withIssuedAt(nowDate) //设置 载荷 生成签名的时间.withExpiresAt(expireDate)//设置 载荷 签名过期的时间.sign(algorithm);//签名 Signature
  • 生成携带自定义信息的 JWT token
    自定义信息通过 withClaim 方法进行添加,具体操作如下:
JWT.create().withHeader(map).withClaim("key", "value")
  1. 验证 JWT token
  • 构建密钥信息
  Algorithm algorithm = Algorithm.HMAC256("secret");
  • 通过密钥信息和签名的发布者的信息生成 JWTVerifier (JWT验证类)
 JWTVerifier verifier = JWT.require(algorithm).withIssuer("SERVICE").build();

不添加 .withIssuer(“SERVICE”) 也是可以获取 JWTVerifier 。

  • 通过 JWTVerifier 的verify获取 token中的信息。
DecodedJWT jwt = verifier.verify(token);

如下面代码所示就可以获取到我们之前生成 token 的 签名的主题,观众 和自定义的声明信息。

 String subject = jwt.getSubject();//获得签名主题List<String> audience = jwt.getAudience();//获得签名接收方Map<String, Claim> claims = jwt.getClaims();//获得自定义信息for (Entry<String, Claim> entry : claims.entrySet()) {String key = entry.getKey();Claim claim = entry.getValue();System.out.println("key:"+key+" value:"+claim.asString());}
  1. 编写拦截器 interceptor
public class AuthenticationInterceptor implements HandlerInterceptor {@AutowiredUserService userService;@Overridepublic boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object object) throws IOException {String token = req.getHeader("token");// 从 http 请求头中取出 token// 如果不是映射到方法直接通过if(!(object instanceof HandlerMethod)){return true;}//获得控制器函数所在的控制器类HandlerMethod handlerMethod = (HandlerMethod)object;//获得控制器函数Method method = handlerMethod.getMethod();//获得控制器函数所在的控制器类的ClassClass controllerClass = handlerMethod.getBean().getClass();//检查类中是否有跳过认证的注解if(controllerClass.isAnnotationPresent(PassToken.class)){PassToken passToken = (PassToken) controllerClass.getAnnotation(PassToken.class);if (passToken.required()) {return true;}}//检查方法是否有passtoken注释,有则跳过认证if (method.isAnnotationPresent(PassToken.class)) {PassToken passToken = method.getAnnotation(PassToken.class);if (passToken.required()) {return true;}}//判断类中是否有登陆注解if(controllerClass.isAnnotationPresent(UserLoginToken.class)){UserLoginToken userLoginToken = (UserLoginToken);controllerClass.getAnnotation(UserLoginToken.class);//验证tokenneedUserLogin(userLoginToken, token);}//判断方法中是否有登陆的注解if(method.isAnnotationPresent(UserLoginToken.class)){UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);//验证tokenneedUserLogin(userLoginToken, token);}return true;}//验证注解的函数public boolean needUserLogin(UserLoginToken userLoginToken, String token){if(userLoginToken.required()){//需要验证if(token == null){throw new RuntimeException("无token, 请重新登陆");}String un;try{//获取签名接受方un = JWT.decode(token).getAudience().get(0);}catch (JWTDecodeException j) {throw new RuntimeException("401");}//通过用户名获取用户信息User user = userService.findUserByUn(un);if(user == null){throw new RuntimeException("用户不存在,请重新登陆");}// 通过生成算法及密钥生成 token验证类JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPd())).build();try {//验证 token,验证错误或token超时会抛出异常jwtVerifier.verify(token);} catch (JWTVerificationException e) {throw new RuntimeException("401");}return true;}return true;}
}

实现一个拦截器就需要实现HandlerInterceptor接口

HandlerInterceptor接口主要定义了三个方法

  • boolean preHandle (): 预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行 postHandle()和afterCompletion();false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。
  • void postHandle(): 后处理回调方法,实现处理器的后处理(DispatcherServlet进行视图返回渲染之前进行调用),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
  • void afterCompletion(): 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值为true时才会执行,也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
    主要流程:
    1.从 http 请求头中取出 token
    2.判断是否映射到方法或控制器类
    3.检查是否有passtoken注释,有则跳过认证
    4.检查有没有需要用户登录的注解,有则需要取出并验证
    5.认证通过则可以访问,不通过会报相关错误信息

6.注册拦截器

在配置类上添加了注解@Configuration,标明了该类是一个配置类并且会将该类作为一个SpringBean添加到IOC容器内

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @UserLoginToken 注解 决定是否需要登录}@Beanpublic AuthenticationInterceptor authenticationInterceptor() {return new AuthenticationInterceptor();}
}

总结

JWT 就是一个生成 Token 的工具,如果不使用 JWT 我们也可以根据自己加密规则生成 Token。只不过 JWT 规范了生成 Token 定义了一个标准而已。JWT 的核心的功能就是:生成Token、解析Token。

参考博客:

  • https://blog.csdn.net/ljk126wy/article/details/82751787
  • https://www.jianshu.com/p/e88d3f8151db

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

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

相关文章

SaaS,PaaS,IaaS都是什么鬼

转载自 SaaS,PaaS,IaaS都是什么鬼IaaS Infrastructure as a Service&#xff0c;基础设施即服务。 假如你现在要做一个网站&#xff0c;你肯定要有一台服务器或者虚拟机&#xff0c;要么自己搭建&#xff0c;要么买服务器运营商的。说白了&#xff0c;IaaS就是解决企业硬件问题…

转:什么是 Base64编码

转&#xff1a; https://developer.aliyun.com/article/763589 一、定义 Base64编码 是一种基于 64 个可打印字符来表示二进制数据的方法。目前 Base64 已经成为网络上常见的传输 8 位二进制字节代码的编码方式之一。为什么会有 Base64 编码呢&#xff1f;&#xff08;Base…

什么是Spring Boot?

转载自 什么是Spring Boot? 什么是Spring Boot? Spring Boot是Spring开源组织下的子项目&#xff0c;是Spring组件一站式解决方案&#xff0c;主要是简化了使用Spring的难度&#xff0c;简省了繁重的配置&#xff0c;提供了各种启动器&#xff0c;开发者能快速上手。 官方网站…

JAVA并发知识

JAVA并发知识一、什么是线程和进程&#xff1f;二、线程与进程的关系&#xff0c;区别及优缺点&#xff1f;三、并发和并行有什么区别&#xff1f;四、为什么要使用多线程&#xff1f;五、使用多线程可能会带来什么问题&#xff1f;六、说说线程的生命周期和状态。七、java 中如…

servlet session持久化

1、 session持久化是什么&#xff1f; web服务器把 session中存储的属性存储到本地磁盘或数据库中&#xff1b; 2、为什么需要持久化&#xff1f; 因为 session是服务器维护会话状态的对象&#xff0c;即便客户端关闭连接或客户端长时间没有访问&#xff0c;服务器还依然存储…

高并发解决方案

扩容 垂直扩容&#xff1a;提高系统部件能力水平扩容&#xff1a;增加更多系统成员&#xff08;增加服务器数量&#xff09; 数据库扩容系统属于 读操作 频繁型&#xff0c;可采用垂直扩容 采用 memcache&#xff0c; redis&#xff0c; CDN等缓存系统属于 写操作 频繁型&#…

servlet session 跟踪用户上次访问时间

1、是什么&#xff1f; 上次访问时间&#xff1b;即用户最近一次登录时间&#xff1b; 2、为什么&#xff1f; 为了提示用户登录或访问记录&#xff0c;提高安全性&#xff0c;如qq登录提示&#xff1b; 3、怎么做&#xff1f; 通过cookie 实现&#xff1b; 用户第1次登录&…

Spring MVC表单防重复提交

转载自 Spring MVC表单防重复提交 利用Spring MVC的过滤器及token传递验证来实现表单防重复提交。 创建注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface Token {boolean create() default false;boolean remove() default f…

SpringMvc @RequestParam、 @RequestBody、@RequestPart 的区别

注解RequestParam接收的参数是来自requestHeader中&#xff0c;即请求头。 RequestParam可以接受简单类型的属性&#xff0c;也可以接受对象类型。 RequestParam有三个配置参数&#xff1a; required 表示是否必须&#xff0c;默认为 true&#xff0c;必须。defaultValue 可…

利用session防止表单重复提交

1、是什么&#xff1f;一个表单不能多次提交&#xff1b; 2、为什么&#xff1f; 在网络不好或者并发请求时会导致多次重复提交数据的问题。防止重复提交&#xff0c;可以维护数据一致性&#xff1b; 3、怎么做&#xff1f; 把 session的编号和当前时间戳经过 MD5 加密得到to…

Druid-目前最好的连接池

转载自 Druid-目前最好的连接池 Druid是什么 Druid是阿里开源的连接池&#xff0c;是Java语言中最好的数据库连接池.Druid能够提供强大的监控和扩展功能&#xff0c;是为监控而生的数据库连接池&#xff01; GitHub&#xff1a;https://github.com/alibaba/druid/ 添加依赖 &l…

Nginx 部署 Vue 项目刷新页面出现404

问题 使用Vue.框架&#xff0c;利用vue-route编写了一个单页路由项目&#xff0c;运维协助在服务器端配置nginx。部署完成后&#xff0c;访问首页没问题&#xff0c;从首页里打开二级页面没问题&#xff0c;但是所有的二级页面打开后&#xff0c;再次刷新&#xff0c;就会出现…

repost: intro2token

repost 4 https://blog.csdn.net/Jason_Fangh/article/details/55113627 对于初学者来说&#xff0c;对Token和Session的使用难免会限于困境&#xff0c;开发过程中知道有这个东西&#xff0c;但却不知道为什么要用他&#xff1f;更不知道其原理&#xff0c;今天我就带大家一…

vue - resource 使用过程的坑

一. get 传参的坑&#xff1a;加params对象传参&#xff08;不能直接get(url, params)!!!&#xff09; this.$http.get(url, {params: { offset: this.offset, label: this.categray }})二. 使用post请求&#xff1a; 知识点 post参数的形式 form data(表单&#xff0c;通过url…

repo-关于URL编码

repost 4 http://www.ruanyifeng.com/blog/2010/02/url_encoding.html 一、问题的由来 URL就是网址&#xff0c;只要上网&#xff0c;就一定会用到。 一般来说&#xff0c;URL只能使用英文字母、阿拉伯数字和某些标点符号&#xff0c;不能使用其他文字和符号。比如&#xff0c…

Spring零配置之@Configuration注解详解

转载自 Spring零配置之Configuration注解详解 Configuration介绍 Spring3.0之前要使用Spring必须要有一个xml配置文件&#xff0c;这也是Spring的核心文件&#xff0c;而Spring3.0之后可以不要配置文件了&#xff0c;通过注解Configuration完全搞定。 Configuration即用来代替S…

session实现购物车

1、是什么&#xff1f; session 可以存储会话级变量&#xff0c;基于其实现购物车&#xff1b; 2、为什么&#xff1f; session是会话级变量&#xff0c;可以吧多次请求的数据串联起来&#xff0c;放到会话里&#xff1b; 3、怎么做&#xff1f; 【荔枝】转自 张孝祥 登录…

commons-logging,log4j,logback,slf4j之间的关系详解

转载自 commons-logging,log4j,logback,slf4j之间的关系详解commons-logging是apache最早提供的日志的门面接口。它的主要作用是提供一个日志门面&#xff0c;使用者可以使用不同的日志实现。用户可以自由选择第三方的日志组件作为具体实现&#xff0c;像log4j&#xff0c;或者…

Vue代理配置

在 package.json 的同级目录&#xff08;项目根目录&#xff09;下创建 vue.config.js在 vue.config.js 写入下列内容 module.exports {devServer: {proxy: http://localhost:8080}}这会告诉开发服务器将任何未知请求 (没有匹配到静态文件的请求) 代理到 http://localhost:40…

@Resource,@Autowired,@Inject3种注入方式详解

转载自 Resource,Autowired,Inject3种注入方式详解 概况 Resource,Autowired,Inject 这3种都是用来注入bean的&#xff0c;它们属于不同的程序中。 ANNOTATIONPACKAGESOURCEResourcejavax.annotationJava JSR-250Injectjavax.injectJava JSR-330Autowiredorg.springframework.b…