Jwt的新手入门教程

1.Jwt究竟是什么东东?

​ 先贴官网地址:JSON Web Tokens - jwt.io

​ 再贴官方的定义:

What is JSON Web Token?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.

Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

​ 我的理解总结:Jwt全称是Json Web Token,顾名思义就是一种通过Json方式来传输的令牌,并且他最大的特点就是signed tokens,签发者可以使用各种的加密方式对信息进行签名,更重要的是Jwt还能验证token是否被篡改或者token是否正确。当然了,这种方式注定是不安全的。我们很容易地就可以在官网得到这一结论。

​ 可以看到,只要我们获得token字符串,就可以获取到里面的大部分信息,除了签名的密钥。

所以Jwt简单来说,就是简单地存储部分不那么重要的信息,通过Json,对客户端进行验证的一种方式。

2.Jwt的组成

​ 从官网的Debugger界面,我们可以得知,Jwt由三部分组成。

  • 第一部分:Header,Header通常由令牌的类型和加密的算法组成,也就是
{
"alg": "HS256",
"typ": "JWT"
}

这个Header的含义就是"alg"--Algorithm(算法) 是HS256。

  • 第二部分: Payload,这部分主要是记录我们所存储的简单且不重要的信息。例如:用户名,过期时间,用户id等等。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

但是要注意的是,Payload记录的这些信息是完全公开的,所以千万不能把用户或者系统的敏感数据放到Payload中,Payload只是负责记录简单信息,并不具备加密的功能。

  • 第三部分:Signature, 签名里是由三部分组成,Header的Base64编码,Payload的Base64编码,还有secret,然后通过指定的加密方式,例如HS256,进行加密后得出的字符串。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

可以说这部分是Jwt的重点,因为他承担着两个作用,第一个:验证JwtToken在传输过程中没有受到篡改 第二个:验证签发人的身份

​ 详细说一下这两个作用。

验证JwtToken在传输过程中没有受到篡改,这个原理就比较好理解,因为Signature中有一个字符串,也就是secret,这个secret是由我们来设置的,相当于私钥,只有我们自己才知道,别人是不知道的。那么在后续验证过程中,只要我们自己生产的Token与客户端传来的Token进行比对,如果一致那就证明该Token没有受到篡改,反之则证明客户端传来的Token是非法的。

验证签发人的身份,其实第二个作用是从第一个作用中展现而来的,因为如果能证明Token在传输过程中没有受到篡改,那就更加说明服务端是这个签名的签发者,因为只有签发者才知道私钥

​ 其实secret我们也可以认为是盐(salt),我们知道如果单纯地在数据库中存储明文密码,或者是只经过一重MD5加密的密码,是非常的不安全,因为就算是经过MD5加密,仍然可以通过暴力穷举的方式来进行破解,可是如果在用户密码的基础上,加上我们生成的随机或固定的字符串,然后再进行加密,那么安全程度会大大提升。

​ 如果不知道盐(salt)的童鞋,可以去bind搜索一下相关资料,其实理解起来就是用户密码+我们设置的随机或固定字符串再进行多重加密。

3.编码实现

​ 我们来创建这几个类。

配置类:InterceptorConfig

@Configuration
public class InterceptorConfig implements WebMvcConfigurer { @Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login");
} @Bean
public JwtInterceptor authenticationInterceptor() {
return new JwtInterceptor();
} }

控制类: UserController

@RestController
public class UserController { @PostMapping("/login")
public Map<String, Object> login(@RequestBody User user) { Map<String, Object> map = new HashMap<>();
String username = user.getUsername();
String password = user.getPassword();
// 省略 账号密码验证
// 验证成功后发送token
String token = JwtUtil.sign(username, password);
if (token != null) {
map.put("code", "200");
map.put("message", "认证成功");
map.put("token", token);
return map;
}
map.put("code", "403");
map.put("message", "认证失败");
return map;
} @GetMapping(value = "/api/test")
public String get(){
System.out.println("执行了get请求");
return "success";
}
}

服务类:UserService

@Service
public class UserService { public String getPassword(){ return "admin";
}
}

实体类 User

@Data
public class User { private String username;
private String password; }

自定义拦截器类 JwtIntercepter

@Component
public class JwtInterceptor implements HandlerInterceptor { @Autowired
UserService userService; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 从 http 请求头中取出 token
String token = request.getHeader("token");
// 如果请求不是映射到方法直接通过
if(!(handler instanceof HandlerMethod)){
return true;
}
if (token != null){
String username = JwtUtil.getUserNameByToken(request);
// 这边拿到的 用户名 应该去数据库查询获得密码,简略,步骤在service直接获取密码
boolean result = JwtUtil.verify(token,username,userService.getPassword());
if(result){
System.out.println("通过拦截器");
return true;
}
}
return false;
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { }
}

Jwt工具类 JwtUtil

public class JwtUtil {

    // Token一天后过期
public static final long EXPIRE_TIME = 1000 * 60 * 60 * 24; //检验Token是否正确
public static boolean verify(String token, String username, String secret) {
try {
// 设置加密算法
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
// 效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
} public static String sign(String username, String secret) {
//现在系统的时间 + 一天
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//对密码进行加密
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
} public static String getUserNameByToken(HttpServletRequest request) {
String token = request.getHeader("token");
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username")
.asString();
} }

具体代码,我上传到github上:Alickx/JwtTokenDemo: JwtDemo代码 (github.com)

这里代码的secret则是用户的密码。

我们先通过PostMan向/login接口发送我们的账号密码,得到Jwt根据我们的账号密码生成的token。

{
"code": "200",
"message": "认证成功",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MzIzMTg1NjksInVzZXJuYW1lIjoiYWRtaW4ifQ.c_m3z11UOcFcS_hZN9KNlidzZ2j6y_Ugkb9awHQ3FGY"
}

这里认证成功后,服务器如果不经过我们其他的存储操作,是不会对生成的token进行持久化或其他控制的,所以一旦签发出去,这个token串就会变成无状态了。

​ 接着,我们访问一下我们的api测试接口,因为我们在config里配置了全局拦截器,且重写了HandlerInterceptor类,除了/login路径,其他全路径都需要请求的Header(请求头)中带有token字段,且需验证token成功后才会允许访问,否则进行拦截。

4.管理JwtToken的状态

​ 要做到管理JwtToken的状态,我们可以通过把token存储到Redis数据库中,通过设置key的过期时间,就可以做到对Jwt的过期操作,同时也能够对Token进行续签,失效等等操作。这部分先不去仔细探究,有个思路就可以了。具体的编码实现我相信也不难。

本文中所有的代码均已上传到github上,如有需要请下载。

Alickx/JwtTokenDemo: JwtDemo代码 (github.com)

JwtTokenDemo (gitee.com)

Jwt的新手入门教程的更多相关文章

  1. 安卓自动化测试(2)Robotium环境搭建与新手入门教程

    Robotium环境搭建与新手入门教程 准备工具:Robotium资料下载 知识准备: java基础知识,如基本的数据结构.语法结构.类.继承等 对Android系统较为熟悉,了解四大组件,会编写简单 ...

  2. Xorboot-UEFI新手入门教程

    Xorboot-UEFI新手入门教程        Xorboot-UEFI是一款UEFI下轻量级的图形化多系统引导程序,pauly于2014年国庆节期间发布了预览版.搜了下论坛,关于Xorboot- ...

  3. gulp的使用以及Gulp新手入门教程

    Gulp新手入门教程 原文  http://w3ctrain.com/2015/12/22/gulp-for-beginners/ Gulp 是一个自动化工具,前端开发者可以使用它来处理常见任务: 搭 ...

  4. 【LaTeX】E喵的LaTeX新手入门教程(6)中文

    假期玩得有点凶 ._.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版 [LaTeX]E喵的LaTeX新手入门教程(3)数学公式 ...

  5. 【LaTeX】E喵的LaTeX新手入门教程(4)图表

    这里说的不是用LaTeX画图,而是插入已经画好的图片..想看画图可以把滚动条拉到底.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础 ...

  6. 【LaTeX】E喵的LaTeX新手入门教程(5)参考文献、文档组织

    这不是最后一篇,明天开始建模所以会从6号开始继续更新.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版 [LaTeX]E喵的La ...

  7. 【LaTeX】E喵的LaTeX新手入门教程(3)数学公式

    昨天熄灯了真是坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版上一期测试答案1.大家一开始想到的肯定是\LaTeX{}er ...

  8. 【LaTeX】E喵的LaTeX新手入门教程(1)准备篇

    昨天熄灯了真是坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版上一期测试答案1.大家一开始想到的肯定是\LaTeX{}er ...

  9. 【LaTeX】E喵的LaTeX新手入门教程(2)基础排版

    换了块硬盘折腾了好久..联想的驱动真坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇文档框架嗯昨天我们已经编写了一个最基本的文档,其内容是这样的:\documentclass{ar ...

随机推荐

  1. c++ 跨平台线程同步对象那些事儿——基于 ace

    前言 ACE (Adaptive Communication Environment) 是早年间很火的一个 c++ 开源通讯框架,当时 c++ 的库比较少,以至于谈 c++ 网络通讯就绕不开 ACE, ...

  2. MySQL 不完全入门指南

    由于 MySQL 的整个体系太过于庞大,文章的篇幅有限,不能够完全的覆盖所有的方面.所以我会尽可能的从更加贴进我们日常使用的方式来进行解释. 小白眼中的 MySQL 首先,对于我们来说,MySQL 是 ...

  3. MATLAB—信号与系统中的应用

    文章目录 一. 理论知识 1.线性系统的响应 2.微分方程的解 Ⅰ.经典解 Ⅱ.完全响应 3.零输入响应 4.零状态响应 5.冲激响应 6.阶跃响应 7.卷积求零状态响应 二.连续信号的MATLAB描 ...

  4. Nikitosh 和异或

    题面 设 \(l_{i}\) 为以 \(i\) 为结尾的区间中最大的一段异或值,\(r_{i}\) 为以 \(i\) 为开头的区间中最大的一段异或值. 则有 \[l_{i}=\max\left(l[i ...

  5. python中的logging日志

    logging使用 import logging import os from logging import handlers from constants.constants import Cons ...

  6. Intellj IDEA 光标显示insert状态解决办法

    使用idea过程中,不知道怎么回事,鼠标的光标老是insert状态,体验效果极其差劲,于是去百度,扒拉了好一阵,过滤了垃圾博客,发现了有两种方法可以解决此问题: 第一种方法: 在File------& ...

  7. git忽略文件夹提交以及gitignore修改后不生效的解决办法

    1.在 .gitgnore 文件加入需要忽略的问价夹正则表达式: 在配置完以后提交代码,你可能会发现git忽略配置不生效! 解决办法,将缓存的文件重新添加一下即可 2.打开命令行,将下面三个命令复制粘 ...

  8. AECC2018同时中英文切换多开使用,加倍提高你的工作效率

    最近相信不少人已经更新了AECC2018,升级之后第一件重要的事当然是中英文的切换了,要不然工作中很麻烦.对于一直习惯用中文的人来说,在用模板过程中会出现各种表达式报错极其不方便,而对于习惯英文操作朋 ...

  9. open62541(opcua)传输延迟探索小记

    缘起 将open62541作为中间件使用代替自定义数据的RPC,client通过订阅valueChange来接收数据.使用时发现有一些问题: 前后两次产生的数据相同时,不会触发valueChange ...

  10. nuxt.js服务端渲染中less的配置和使用

    第一步:npm 安装 less 和 less-loader ,文件根目录下安装,指令如下 npm install less less-loader --save-dev 第二步:直接在组件中使用 &l ...