Jwt的新手入门教程
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)
Jwt的新手入门教程的更多相关文章
- 安卓自动化测试(2)Robotium环境搭建与新手入门教程
Robotium环境搭建与新手入门教程 准备工具:Robotium资料下载 知识准备: java基础知识,如基本的数据结构.语法结构.类.继承等 对Android系统较为熟悉,了解四大组件,会编写简单 ...
- Xorboot-UEFI新手入门教程
Xorboot-UEFI新手入门教程 Xorboot-UEFI是一款UEFI下轻量级的图形化多系统引导程序,pauly于2014年国庆节期间发布了预览版.搜了下论坛,关于Xorboot- ...
- gulp的使用以及Gulp新手入门教程
Gulp新手入门教程 原文 http://w3ctrain.com/2015/12/22/gulp-for-beginners/ Gulp 是一个自动化工具,前端开发者可以使用它来处理常见任务: 搭 ...
- 【LaTeX】E喵的LaTeX新手入门教程(6)中文
假期玩得有点凶 ._.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版 [LaTeX]E喵的LaTeX新手入门教程(3)数学公式 ...
- 【LaTeX】E喵的LaTeX新手入门教程(4)图表
这里说的不是用LaTeX画图,而是插入已经画好的图片..想看画图可以把滚动条拉到底.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础 ...
- 【LaTeX】E喵的LaTeX新手入门教程(5)参考文献、文档组织
这不是最后一篇,明天开始建模所以会从6号开始继续更新.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版 [LaTeX]E喵的La ...
- 【LaTeX】E喵的LaTeX新手入门教程(3)数学公式
昨天熄灯了真是坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版上一期测试答案1.大家一开始想到的肯定是\LaTeX{}er ...
- 【LaTeX】E喵的LaTeX新手入门教程(1)准备篇
昨天熄灯了真是坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版上一期测试答案1.大家一开始想到的肯定是\LaTeX{}er ...
- 【LaTeX】E喵的LaTeX新手入门教程(2)基础排版
换了块硬盘折腾了好久..联想的驱动真坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇文档框架嗯昨天我们已经编写了一个最基本的文档,其内容是这样的:\documentclass{ar ...
随机推荐
- Visio操作【未完】
Visio 1.如何操作文档 新建基本框图和空白框图 单击基本框图打开后有模具 空白框图打开之后并没有形状 左下角发现有 更改纸张方向大小 自动调整大小: 如果我们选择形状进入到我们的页面,如果放到边 ...
- 案例分享:Qt政务标签设计器,标签排版软件定制与打印
需求 1.标签设计器: 2.具备文字排版功能: 3.支持六种排版格式: 4.排版后可以输出打印(demo中不包含): 5.排版后可以输出标签的指定协议文本FBD格式: 6.可以调整对应标 ...
- K8S日志接入sls配置
背景 原有日志方案查询日志很不方便且效率低,而且也不支持基于日志的高级操作.如:聚合,图形展示,关键字检测等. 方案 接入阿里云的sls日志服务. 实施 1.通过环境变量进行日志接入 配置如下: 字段 ...
- [1.6W字]浏览器跨域请求的原理, 以及解决方法(可以纯前端实现) #flight.Archives011
Title/ 浏览器跨域(CrossOrigin)请求的原理, 以及解决方案详细指南 #flight.Archives011 序: 最近看到又有一波新的创作活动了, 官方给出的话题中有一个" ...
- 题解 party?
传送门 挺遗憾的一个题 考场上想到的思路是题解的退化版,可以有71pts(赛时以为只有20pts),但因为这一场的策略原因没有打-- 首先发现颜色种类数很少,可以直接bitset上树剖维护,炸不了空间 ...
- mac Charles抓包
手机配置http代理 1.配置iPhone或Android 的wifi配置.首先保证Mac电脑和手机是在同一个局域网内. 2.设置手机wifi配置,在HTTP代理中选择手选代理,服务器填写Mac的IP ...
- springmvc学习日志四
一.回顾 1.文件上传 1.1引入fileupload的jar包 1.2在springmvc的配置文件中引入CommonsMutilpartResolver文件上传解析器 1.3在控制层在写入代码 2 ...
- 【springcloud】服务熔断与降级(Hystrix)
转自:https://blog.csdn.net/pengjunlee/article/details/86688858 服务熔断 服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的 ...
- Spring @Order注解的使用
注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响: 1 ...
- SpringCloud商品服务调用方式之feign
简介:改造电商项目 order-service服务 调用商品服务获取商品信息 Feign: 伪RPC客户端(本质还是用http) 官方文档: https://cloud.spring.io/sprin ...