登录认证(4):令牌技术:JWT令牌

如上文所说(登录认证(1):登录的基本逻辑及实现思路登录),因为 HTTP协议是无状态的协议,我们需要使用会话跟踪技术实现同一会话中不同请求之间的数据共享,但Cookie技术Session技术都有各自的使用局限,所以说在当今的企业开发中,越来越青睐令牌技术 来进行会话跟踪。

令牌技术概览

在登录认证中,令牌是用户的身份标识,是合法的身份凭证,好像十分神秘和高大上,但其本质是一个字符串。如果使用令牌技术进行会话跟踪,在浏览器发起请求,请求登录接口,如果登录成功,那么就在服务端生成一个令牌令牌就是用户的合法身份凭证,在响应数据的时候,就可以将令牌直接响应给前端。

前端程序接收到令牌之后,就需要将令牌存储起来,可以存储在cookie中,也可以存储在localStorage这样的其他存储空间。之后,在后续的每一次请求中,都需要携带令牌,服务端的统一拦截器需要校验令牌的有效性。如果令牌有效,则说明用户已经执行了登录操作,拦截器就可以放行;如果令牌无效(解析令牌报错),那么则说明用户没有执行登录操作,拦截器就需要拦截,并返回错误代码,让用户登录。整个流程如下图所示:

此时,在同一个会话的多次请求之间,我们就通过将数据存储在令牌中的方式完成了数据共享。令牌技术有很多优点,比如:支持多端,不但支持PC端,而且支持移动令牌技术也可以解决服务器集群的认证问题,因为只需要成功解析令牌,就可以证明令牌是有效的,是无需在服务器存储的令牌的安全性也非常强悍。但也是因为其强悍的性能,令牌使用起来会更加的复杂,但是这些劣势在优势面前就不值一提了。

JWT令牌

令牌的形式有很多,本文讲解功能强大、使用最广泛的JWT令牌。JWT令牌(JSON Web Token),定义了一种简洁的自包含的格式,可用于通信双方以Json数据格式安全的传输信息。

特性

简洁

JWT令牌的本质就是字符串,可以作为请求参数或者在请求头中直接传递。

自包含

JWT令牌虽然是一个字符串,但是可以根据需求,在令牌中存储自定义的数据内容,比如在登录操作中,可以在JWT令牌中存储用户相关信息。

安全

简单理解,JWT令牌就是将原本简单的Json数据进行了安全的封装,这样就可以直接在JWT令牌中进行信息传输了,并且由于数字签名的存在,这些信息是安全可靠的

组成

JWT令牌三个部分组成,每个部分之间使用.进行分隔。一个完整的JWT令牌如图所示:

第一部分:Header(头)

该部分主要是记录令牌类型令牌使用的签名算法等。例如:{"alg":"HS256", "type":"JWT"},从这个Header信息就可以看出:这是一个JWT令牌,使用了HS256签名算法

第二部分:Payload(有效载荷)

该部分主要是携带一些自定义的信息,或者一些默认的信息等,例如:{"id":"1","username":"Tom"},从这个Payload信息可以看出:这个令牌携带的数据是一个用户数据,id为1,username为Tom

第三部分:Signature(签名)

这个部分主要是令牌的签名,签名可以防止Token被篡改,可以提高令牌的安全性。其构成是将HeaderPayload两个部分,加入指定密钥(Secret),并通过指定的签名算法计算而来。正是因为数字签名,所以说JWT令牌是非常安全的,一旦令牌中的任何一个部分、任何一个字符被篡改了,整个令牌在校验时都会失效。

Base64编码

那么JWT令牌是如何将原始的Json数据,转变为字符串的呢?在生成JWT令牌的时候,对原始数据进行了Base64编码这并非是一种加密方式,只是一种编码方式)。

Base64编码:是一种基于64个可打印的字符来表示二进制数据的编码方式。所使用的64个字符分别是A到Za到z0-9一个加号(+),一个斜杠(/),加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。在有些情况下,Basae64编码可能会出现一个等号(=)。等号是一个补位的符号。

使用

想要使用JWT令牌,首先需要引入JWT对应的Maven坐标

<!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
生成JWT令牌

在引入了JWT依赖之后,就可以使用对应的工具类Jwts提供的API来完成JWT令牌生成与校验

/**
 * 生成JWT令牌
 */
@Test
public void testGenerateJWT() {
    Map<String, Object> claims = new HashMap<>();
    claims.put("id", 10);
    claims.put("username", "wzb");
    // 通过Jwts工具类中的builder方法构建一个JWT令牌
    String jwt = Jwts.builder().signWith(SignatureAlgorithm.HS256, "hello") // 加密算法和签名
            .addClaims(claims) // 添加数据:键值对
            .setExpiration(new Date(System.currentTimeMillis() + 12 * 3600 * 1000)) // 设置JWT令牌有效期
            .compact(); // 生成令牌
    log.info("jwt令牌是:{}", jwt);
}

这个代码主要是使用Jwts工具类提供的API生成一个JWT令牌。使用signWith方法,指定该令牌的加密算法为HS256,并且指定签名(这里的签名指定的十分粗糙,实际开发中需要指定更加复杂的签名来增加令牌安全性。);Jwt令牌中的数据都是以键值对的形式出现的,所以说可以直接把需要的数据封装为一个Map,然后使用addClaims方法,将Map中的数据封装到令牌中;然后再使用setExpiration方法,设置令牌的有效时间,此处设置的有效时间是12h;最后使用compact方法生成JWT令牌。运行这个测试方法,得到的JWT令牌

eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoid3piIiwiZXhwIjoxNzM3NTk0NDgxfQ.m4BT-AR8cMCEmMJoNNL0iuyNCHylKdasuu77ETsLHcw

将该令牌拿到JWT令牌官网(JSON Web Tokens - jwt.io)解析: 

JWT令牌的最后一个部分并非Base64编码,而是签名算法计算的,所以说最后一个部分不会解析。

成功解析令牌内容,加密算法、数据等都和代码封装得相同,说明我们成功生成了一个JWT令牌

校验JWT令牌

校验(解析)JWT令牌和生成一样,同样需要使用Jwts工具类提供的API

/**
 * 解析JWT令牌
 */
@Test
public void testParseJWT() {
    Claims claims = Jwts.parser().setSigningKey("hello")
            .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTAsInVzZXJuYW1lIjoid3piIiwiZXhwIjoxNzM3NDI1MTg" +
                    "5fQ.cHdgtWD-BtrnM2Xnl418CcB_6h-rB2ogGSK8aT4T0dk")
            .getBody();
    log.info("JWT解析结果为:{}", claims);
}

解析JWT令牌必须要使用和生成时同样的签名,通过Jwts中的parser方法解析一个JWT令牌,必须在setSignKey方法中传递和生成时一样的签名,否则解析失败;在parseClaimsJws方法中传递需要解析的JWT令牌,即可解析其Payload部分,也就是令牌中的数据;最后通过getBody方法即可获得令牌数据的键值对对象Claims,Claims继承了Map类,可以理解为一种特殊的Map。让我们用该程序解析刚才生成的JWT令牌

成功解析令牌,说明解析令牌方法成功。JWT令牌的解析(校验)十分严格,只要有一点错误,不论哪个部分,都无法解析,即使我们只是改变了一个字符的大小写,在运行解析方法的时候都会报错,可以看出JWT令牌的可靠性。过期的JWT令牌也无法解析,令牌过期之后,令牌就会马上失效,解析就会失败。

利用JWT令牌完善登录功能

上文已经对JWT令牌做出了详细的解释与分析,JWT令牌最典型的应用就是登录,所以说让我们用JWT令牌技术来完善登录功能。具体思路前面已经分析过了,主要分为两步操作:

生成令牌

在用户登录成功之后,生成一个JWT令牌,并且把这个令牌直接返回给客户端,之后的每一个请求,都要携带这个令牌。

校验令牌

请求携带了JWT令牌,统一拦截器需要拦截请求,从请求中获取令牌,并对令牌进行解析,如果成功解析,那么就放行;如果解析失败,则返回错误代码

我们先要创建一个JwtUtils工具类,这个工具类提供生成、解析令牌的方法,以便程序使用:

package com.wzb.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.Map;

public class JWTUtils {

    // 独创数字签名
    private static final String signKey = "hello";

    // 令牌过期时间
    private static final Long expire = 43200000L;

    /**
     * 生成的JWT令牌
     * @param claims JWT令牌中的数据,键值对
     * @return JWT令牌
     */
    public static String generateJWT(Map<String, Object> claims) {
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, signKey)
                .addClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + expire))
                .compact();
    }

    /**
     * 解析JWT令牌
     * @param jwt JWT令牌
     * @return Claims,JWT令牌中的数据
     */
    public static Claims parseJWT(String jwt) {
        return Jwts.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();
    }

}

然后改造登录方法:

/**
 * 员工登录
 *
 * @param emp 登录请求数据封装的Emp实体对象
 * @return LoginInfo员工登录信息
 */
@Override
public LoginInfo login(Emp emp) {
    Emp empLogin = empMapper.getUserByUsernameAndPassword(emp);
    Map<String, Object> claims = new HashMap<>();
    if (empLogin != null) {
        Integer id = empLogin.getId();
        String username = empLogin.getUsername();
        String name = empLogin.getName();
        claims.put("id", id);
        claims.put("username", username);
        return new LoginInfo(id, username, name, JWTUtils.generateJWT(claims));
    }
    return null;
}

从数据库中查询到员工的信息之后,将其以键值对的方式封装到Map中,然后调用工具类中的方法生成JWT令牌之后,封装到登录信息类LoginInfo中返回。此时,就成功给客户端返回了令牌,此时再次发起登录请求: 

 

成功给客户端响应JWT令牌

总结

JWT令牌是现在越来越流行,使用越来越广泛的会话跟踪技术,可以在多端使用,并且有极强的安全性能,还可以应对服务器集群问题,是解决登录认证问题的最佳选择。现在已经有了令牌来标识用户已经登录,但是程序仍然没有对请求进行拦截,验证其是否登录,统一拦截器的部分且听下文分解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Powershell-2

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址&#xff1a;powershell&#xff08;2&#xff09;_哔哩哔哩_bilibili 一 、Powershell使用外部命令 在 Powershell 中&#xff0c;可以执行一些外部命令&…

Flowable 管理各业务流程:流程设计器 (获取流程模型 XML)、流程部署、启动流程、流程审批、流程挂起和激活、任务分配

文章目录 引言I 表结构主要表前缀及其用途核心表II 流程设计器(Flowable BPMN模型编辑器插件)Flowable-UIvue插件III 流程部署部署步骤例子:根据流程模型ID部署IV 启动流程启动步骤ACT_RE_PROCDEF:流程定义相关信息例子:根据流程 ID 启动流程V 流程审批审批步骤Flowable 审…

快速入门Flink

Flink是新一代实时计算平台&#xff0c;采用原生的流处理系统&#xff0c;保证了低延迟性&#xff0c;在API和容错上也是做的相当完善&#xff0c;本文将从架构、组件栈、安装、入门程序等进行基础知识的分析&#xff0c;帮助大家快速对Flink有一个了解。 一.简介 1.是什么 Ap…

VOSK实现【离线中文语音】识别

Vosk是一款开源的离线语音识别工具包&#xff0c;具有以下功能&#xff1a; 多语言支持&#xff1a;能够对20多种语言和方言进行语音识别&#xff0c;如中文、英语、德语、法语、西班牙语等&#xff0c;可满足不同用户的语言需求。 模型轻量化&#xff1a;每种语言的模型大小仅…

最新版pycharm如何配置conda环境

首先在conda prompt里创建虚拟环境&#xff0c;比如 conda create --prefix E:/projects/myenv python3.8然后激活 conda activate E:/projects/myenv往里面安装点自己的包&#xff0c;比如 conda install pytorch1.7.1 torchvision0.8.2 -c pytorch打开pycharm 注意&#x…

MySQL用户授权、收回权限与查看权限

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

【vitpress】静态网站添加访问量统计

这里要是用的插件是不蒜子。 1.安装插件 npm i busuanzi.pure.js2.添加VisitorPanel.vue文件 在.vitepress/theme/components文件添加VisitorPanel.vue文件&#xff0c;内容如下&#xff1a; <!-- .vitepress/theme/components/VisitorPanel.vue --> <template>…

Data Filtering Network 论文阅读和理解

目录 一、TL&#xff1b;DR 二、Introduction 2.1 apple的结论 2.2 业界做法&#xff1a; 2.3 我们的做法&#xff08;Apple&#xff09; 2.4 如何获取好的DFN 三、未完待续&#xff08;这周出去购物了&#xff0c;下周继续补充&#xff09; 一、TL&#xff1b;DR 核心…

MATLAB绘图时线段颜色、数据点形状与颜色等设置,介绍

MATLAB在绘图时&#xff0c;设置线段颜色和数据点的形状与颜色是提高图形可读性与美观性的重要手段。本文将详细介绍如何在 MATLAB 中设置这些属性。 文章目录 线段颜色设置单字母颜色表示法RGB 值表示法 数据点的形状与颜色设置设置数据点颜色和形状示例代码 运行结果小结 线段…

部署 Harbor 私有云仓库

参考链接&#xff1a;https://docs.tianshu.org.cn/docs/setup/deploy-harbor-cloud-warehouse 使用

150 Linux 网络编程6 ,从socket 到 epoll整理。listen函数参数再研究

一 . 只能被一个client 链接 socket例子 此例子用于socket 例子&#xff0c; 该例子只能用于一个客户端连接server。 不能用于多个client 连接 server socket_server_support_one_clientconnect.c /* 此例子用于socket 例子&#xff0c; 该例子只能用于一个客户端连接server。…

Java基础 (一)

基础概念及运算符、判断、循环 基础概念 关键字 数据类型 分为两种 基本数据类型 标识符 运算符 运算符 算术运算符 隐式转换 小 ------>>> 大 强制转换 字符串 拼接符号 字符 运算 自增自减运算符 ii赋值运算符 赋值运算符 包括 强制转换 关系运算符 逻辑运算符 …

项目集成RabbitMQ

文章目录 1.common-rabbitmq-starter1.创建common-rabbitmq-starter2.pom.xml3.自动配置1.RabbitMQAutoConfiguration.java2.spring.factories 2.测试使用1.创建common-rabbitmq-starter-demo2.目录结构3.pom.xml4.application.yml5.TestConfig.java 配置交换机和队列6.TestCon…

Codeforces Round 1000 (Div. 2) B and C

B. Subsequence Update 链接&#xff1a;Problem - B - Codeforces 题意&#xff1a;给定一个数组 可以选择任意个元素 后对这些元素进行排序 问你给定一个区间 这个区间的最小值 算法&#xff1a;贪心 排序 思路&#xff1a;下标1到r的最小个&#xff08;r-l1&#xff09;…

进程的哪些内存类型容易引起内存泄漏

相信你在平时的工作中&#xff0c;应该遇到过下面这些场景&#xff1a; 伴随着服务器中的后台任务持续地运行&#xff0c;系统中可用内存越来越少&#xff1b; 应用程序正在运行时忽然被 OOM kill 掉了&#xff1b; 进程看起来没有消耗多少内存&#xff0c;但是系统内存就是不够…

如何给自己的域名配置免费的HTTPS How to configure free HTTPS for your domain name

今天有小伙伴给我发私信&#xff0c;你的 https 到期啦 并且随手丢给我一个截图。 还真到期了。 javapub.net.cn 这个网站作为一个用爱发电的编程学习网站&#xff0c;用来存编程知识和面试题等&#xff0c;平时我都用业余时间来维护&#xff0c;并且还自费买了服务器和阿里云…

Glarysoft Malware Hunter 多语检测和删除各种恶意软件和间谍软件 v1.195.0.824

Glarysoft Malware Hunter 是一款专业的安全工具&#xff0c;旨在帮助用户检测和删除各种恶意软件和间谍软件。它可以扫描和删除计算机上的病毒、木马、广告软件和其他安全威胁。 软件功能 病毒扫描&#xff1a;Malware Hunter可以快速而全面地扫描计算机&#xff0c;以查找潜…

通过Ukey或者OTP动态口令实现windows安全登录

通过 安当SLA&#xff08;System Login Agent&#xff09;实现Windows安全登录认证&#xff0c;是一种基于双因素认证&#xff08;2FA&#xff09;的解决方案&#xff0c;旨在提升 Windows 系统的登录安全性。以下是详细的实现方法和步骤&#xff1a; 1. 安当SLA的核心功能 安…

Windows远程连接Docker服务

问题背景 本地开发了一个SpringBoot项目&#xff0c;想通过Docker部署起来&#xff0c;我本地是Window11系统&#xff0c;由于某些原因不能虚拟化并且未安装Docker-Desktop&#xff0c;所以我在想有没有办法本地不需要虚拟化也不需要安装Docker-Desktop来实现支持Docker命令远…

Ubuntu20.04 运行 Cartographer demo bag

官方文档&#xff1a; Running Cartographer ROS on a demo bag — Cartographer ROS documentation Running Cartographer ROS on a demo bag Now that Cartographer and Cartographer’s ROS integration are installed, you can download example bags (e.g. 2D and 3D b…