安全认证--JWT介绍及使用
安全认证--JWT介绍及使用
- 1.无状态登录原理
- 1.1.什么是有状态?
- 1.2.什么是无状态
- 1.3.如何实现无状态
- 1.4.JWT
- 1.4.1.简介
- 1.4.2.数据格式
- 2.编写JWT工具
- 2.1.添加JWT依赖
- 2.2.载荷对象
- 2.3.工具
- 2.4.测试
- 2.4.1.配置秘钥
- 2.4.2.测试类
1.无状态登录原理
有状态登录和无状态登录详解
1.1.什么是有状态?
有状态服务,即服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如tomcat中的session。
例如登录:用户登录后,我们把登录者的信息保存在服务端session中,并且给用户一个cookie值,记录对应的session。然后下次请求,用户携带cookie值来,我们就能识别到对应session,从而找到用户的信息。
缺点是什么?
- 服务端保存大量数据,增加服务端压力
- 服务端保存用户状态,无法进行水平扩展
- 客户端请求依赖服务端,多次请求必须访问同一台服务器
1.2.什么是无状态
微服务集群中的每个服务,对外提供的都是Rest风格的接口。而Rest风格的一个最重要的规范就是:服务的无状态性,即:
- 服务端不保存任何客户端请求者信息
- 客户端的每次请求必须具备
自描述信息
,通过这些信息识别客户端身份
带来的好处是什么呢?
- 客户端请求不依赖服务端的信息,任何多次请求不需要必须访问到同一台服务
- 服务端的集群和状态对客户端透明
- 服务端可以任意的迁移和伸缩
- 减小服务端存储压力
1.3.如何实现无状态
无状态登录的流程:
- 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
- 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
- 以后每次请求,客户端都携带认证的token
- 服务端对token进行解密,判断是否有效。
整个登录过程中,最关键的点是什么?
token的安全性
token是识别客户端身份的唯一标示,如果加密不够严密,被人伪造那就完蛋了。
采用何种方式加密才是安全可靠的呢?
我们将采用JWT
来生成token,保证token的安全性
1.4.JWT
1.4.1.简介
JWT,全称是Json Web Token, 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权;官网:https://jwt.io
GitHub上jwt的java客户端:https://github.com/jwtk/jjwt
1.4.2.数据格式
JWT包含三部分数据:
-
Header:头部,通常头部有两部分信息:
- token类型,这里是JWT
- 签名算法,自定义
我们会对头部进行base64加密(可解密),得到第一部分数据
-
Payload:载荷,就是有效数据,一般包含下面信息:
- 标准载荷:JWT规定的信息,jwt的元数据:
- JTI: JWT的id,当前jwt的唯一标识(像身份证号)
- IAT: issue at 签发时间
- EXP:过期时间
- SUB:签发人
- …
- 自定义载荷:
- 用户身份信息,(注意,这里因为采用base64加密,可解密,因此不要存放敏感信息)
这部分也会采用base64加密,得到第二部分数据
- 标准载荷:JWT规定的信息,jwt的元数据:
-
Signature:签名,是整个数据的认证信息。一般根据前两步的数据,再加上服务的的密钥(secret)(不要泄漏,最好周期性更换),通过加密算法生成。用于验证整个数据完整和可靠性
生成的数据格式:
可以看到分为3段,每段就是上面的一部分数据。
什么是 JWT – JSON WEB TOKEN
傻傻分不清之 Cookie、Session、Token、JWT
JWT详细教程与使用
2.编写JWT工具
我们会用到比较流行的java语言的JWT工具,jjw
2.1.添加JWT依赖
我们需要先在项目中引入JWT依赖:
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.2</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.2</version><scope>runtime</scope></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId><version>0.11.2</version><scope>runtime</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId></dependency><!--json工具--><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--日期时间工具类--><dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId></dependency><!--redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
2.2.载荷对象
JWT中,会保存载荷数据,我们计划存储2部分:
- jti:jwt的id
- UserDetail:用户数据
为了方便后期获取,我们定义一个类来封装。
添加一个实体类,代表载荷信息
import lombok.Data;//载荷对象
@Data
public class Payload {/*** tocken的id*/private String jti;/*** 用户数据*/private UserDetail userDetail;
}
载荷中的UserDetail信息,也需要一个实体类表示,这里我们定义一个UserDetail类。
这里我们假设用户信息包含2部分:
- id:用户id
- username:用户名
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor(staticName = "of")
@NoArgsConstructor
public class UserDetail {/*** 用户id*/private Long id;/*** 用户名*/private String username;
}
2.3.工具
我创建一个JwtUtils 工具类,用来封装几个方法:
- createJwt() :生成JWT
- parseJwt() :验证并解析JWT
import com.example.jwt.constants.RedisConstants;
import com.example.jwt.dto.Payload;
import com.example.jwt.dto.UserDetail;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.StringUtils;import javax.crypto.SecretKey;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import java.util.concurrent.TimeUnit;public class JwtUtils {/*** JWT解析器*/private final JwtParser jwtParser;/*** 秘钥*/private final SecretKey secretKey;// @Autowiredprivate StringRedisTemplate redisTemplate;private final static ObjectMapper mapper = new ObjectMapper();public JwtUtils(String key) {// 生成秘钥secretKey = Keys.hmacShaKeyFor(key.getBytes(Charset.forName("UTF-8")));// JWT解析器this.jwtParser = Jwts.parserBuilder().setSigningKey(secretKey).build();}/*** 生成jwt,自己指定的JTI** @param userDetails 用户信息* @return JWT*/public String createJwt(UserDetail userDetails) {try {// 生成tokenidString jti=createJti();//存入redis中
// this.redisTemplate.opsForValue().set(RedisConstants.JTI_KEY_PREFIX+userDetails.getUsername(),jti,30, TimeUnit.MINUTES);return Jwts.builder().signWith(secretKey).setId(jti).claim("user", mapper.writeValueAsString(userDetails)).compact();} catch (JsonProcessingException e) {throw new RuntimeException(e);}}/*** 解析jwt,并将用户信息转为指定的Clazz类型** @param jwt token* @return 载荷,包含JTI和用户信息*/public Payload parseJwt(String jwt) {try {Jws<Claims> claimsJws = jwtParser.parseClaimsJws(jwt);Claims claims = claimsJws.getBody();Payload payload = new Payload();payload.setJti(claims.getId());payload.setUserDetail(mapper.readValue(claims.get("user", String.class), UserDetail.class));return payload;} catch (IOException e) {throw new RuntimeException(e);}}private String createJti() {return StringUtils.replace(UUID.randomUUID().toString(), "-", "");}/*** 刷新jwt有效期* @param username*/public void refreshJwt(String username) {String key= RedisConstants.JTI_KEY_PREFIX+username;//重置key的过期时间redisTemplate.expire(key,30,TimeUnit.MINUTES);}
}
2.4.测试
2.4.1.配置秘钥
在application.yml
文件中配置秘钥:
yy:jwt:key: helloWorldJavaAuthServiceSecretKe
定义一个配置类,注册JwtUtils
注入到Spring的容器。
import com.example.jwt.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class JwtConfig {@Value("${yy.jwt.key}")private String key;@Beanpublic JwtUtils jwtUtils(){return new JwtUtils(key);}
}
2.4.2.测试类
@Autowiredprivate JwtUtils jwtUtils;@Testpublic void test() {// 生成jwtString jwt = jwtUtils.createJwt(UserDetail.of(112L, "lele"));System.out.println("jwt = " + jwt);// 解析jwtPayload payload = jwtUtils.parseJwt(jwt);System.out.println("payload = " + payload);}
结果:
jwt = eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIyZTRkMGI1NjZiODY0YjUzODAwZTI3NGNhOWE0MTcxYSIsInVzZXIiOiJ7XCJpZFwiOjExMixcInVzZXJuYW1lXCI6XCJsZWxlXCJ9In0.NGa42tISwsLg_hyONasdGPGDigFFxkWbH04wd4ELztY
payload = Payload(jti=2e4d0b566b864b53800e274ca9a4171a, userDetail=UserDetail(id=112, username=lele))
相关文章:

安全认证--JWT介绍及使用
安全认证--JWT介绍及使用1.无状态登录原理1.1.什么是有状态?1.2.什么是无状态1.3.如何实现无状态1.4.JWT1.4.1.简介1.4.2.数据格式2.编写JWT工具2.1.添加JWT依赖2.2.载荷对象2.3.工具2.4.测试2.4.1.配置秘钥2.4.2.测试类1.无状态登录原理 有状态登录和无状态登录详…...

【计算机组成原理】计算机硬件的基础组成、认识各个硬件部件
计算机组成原理(一) 计算机内部是通过电信号传递数据 电信号:分为高电平和低电平,分别代表1/0 数字、文字、图像如何用二进制表示? CPU如何对二进制数进行加减乘除? 如何存储这些二进制数的? 如何从内存中取出想要的数…...

使用ChIPSeeker进行ChIP-seq, ATAC-seq,cuttag等富集峰的基因组注释
二代测序产生的数据类型 常规的下一代高通量测序(next generation sequencing, NGS)实验通常产生大量短片段(reads),通常我们需要将这些reads比对到参考基因组/转录组上,即将它们置于生物学上有意义的基因背景下,才能…...

第九届蓝桥杯省赛——7缩位求和
题目:在电子计算机普及以前,人们经常用一个粗略的方法来验算四则运算是否正确。比如:248 * 15 3720把乘数和被乘数分别逐位求和,如果是多位数再逐位求和,直到是1位数,得2 4 8 14 > 1 4 5;1 5 65…...

【c++】STL常用容器5—list容器
文章目录list基本概念list构造函数list赋值和交换list大小操作list插入和删除list数据存取list反转和排序list基本概念 功能:将数据进行链式存储。 链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链…...

【牛客刷题专栏】0x0D:JZ5 替换空格(C语言编程题)
前言 个人推荐在牛客网刷题(点击可以跳转),它登陆后会保存刷题记录进度,重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏:个人CSDN牛客刷题专栏。 题目来自:牛客/题库 / 在线编程 / 剑指offer: 目录前言问题…...

聚观早报 | 苹果2024年放弃高通;腾讯回应进军类 ChatGPT
今日要闻:苹果2024年放弃高通;腾讯回应进军类 ChatGPT;小米发布无线AR眼镜探索版;50%的美国企业已在使用ChatGPT;Snap推出ChatGPT驱动的聊天机器人 苹果2024年放弃高通 高通公司 CEO 兼总裁克里斯蒂亚诺・安蒙…...

Elasticsearch:如何正确处理 Elasticsearch 摄取管道故障
在我之前的文章 “Elastic:开发者上手指南” 中的 “Ingest pipeline” 章节中个,我有很多文章是关于 ingest pipeline 的。在今天的文章中,我将重点介绍如何处理在摄取管道中的错误。在我之前的文章 “Elasticsearch:如何处理 in…...

指标体系—北极星指标体系
北极星指标体系 每个产品都有很多指标,每个指标都反映了对应业务的经营情况。但是在实际业务经营中,却要求我们在不同的产品阶段寻找到合适的指标,让这个指标可以代表当前产品阶段的方向和目标,让这个指标不仅对业务经营团队,而且对产品的用户、对产品的价值都能有很好的…...

【操作系统】内存管理
虚拟内存 虚拟内存的目的是为了让物理内存扩充成更大的逻辑内存,从而让程序获得更多的可用内存。 为了更好的管理内存,操作系统将内存抽象成地址空间。每个程序拥有自己的地址空间,这个地址空间被分割成多个块,每一块称为一页。…...

家庭消耗品跟踪管理软件HomeLists
什么是 HomeLists ? HomeLists 是一款自托管耗材统计软件,能通过提醒等帮助您跟踪家庭消耗品。 安装 在群晖上以 Docker 方式安装。 在注册表中搜索 homelists ,选择第一个 aceberg/homelists,版本选择 latest。 本文写作时&…...
django模型简要(1)
1. AbstractUser(内置用户模型类)的使用 ### 需要在settings.py中添加如下: AUTH_USER_MODEL app.MyUser 说明:这是为了覆盖django默认的User model;app即模型所属app,MyUser即AbstractUser实现类。 2.on_delete选项 从django3.…...

【shell 编程大全】sed详解
sed详解1. 概述 今天单独拉出一章来讲述下sed命令。因为sed命令确实内容太多,不过也是比较灵活的,好了不废话了。我们开始吧 1.2 原理解析 shell脚本虽然功能很多,但是它最常用的功能还是处理文本文件,尤其是在正常的业务操作流程…...

关于sudo配置
前言这里做一个小补充,主要讲一下关于利用sudo对指令提权以及普通用户无法使用sudo指令的问题。在前面的文章【Linux】一文掌握Linux权限中,我们讲到了关于权限的一些问题。我们知道root身份下,一切畅通无阻,而权限只是用来限制我…...

EEGLAB处理运动想象脑电数据
最近在看论文时,经常看到作者处理数据的过程,之前都是一代而过,知道怎么处理就可以了,一直没有实践,最近需要一些特殊的数据,需要自己处理出来,这里尝试着自己用MATLAB处理数据,记录…...
span标签的使用场景
目录 前言 一、span标签是什么? 二、span常用 1.可以嵌套a标签。 2.直接使用 3.加样式使用 4.加按钮使用 5.加a标签的综合使用 6.跟table结合使用 总结 前言 本篇章主要记录一下开发日常中,所常遇见的使用span标签的场景。 一、span标签是什么…...

Kafka面试问题总结
kafka架构2.基础概念Producer(生产者) : 产生消息的一方。Consumer(消费者) : 消费消息的一方。Broker(代理) : 可以看作是一个独立的 Kafka 实例。多个 Kafka Broker 组成一个 Kafka Cluster。同时&#x…...

FPGA案例开发手册——基于全志T3+Logos FPGA核心板
前 言 本文档主要提供评估板FPGA端案例测试方法,适用的开发环境为Windows 7 64bit和Windows 10 64bit。 本文案例基于创龙科技的全志T3+Logos FPGA核心板,它是一款基于全志科技T3四核ARM Cortex-A7处理器 + 紫光同创Logos PGL25G/PGL50G FPGA设计的异构多核全国产工业核心板…...

或许你想要的画图工具在这里
之前文章发布后,有小伙伴问下面的画怎么画的(偷偷告诉你,其实我是用铅笔水彩笔画的),哈哈,开玩笑了。其实这些图都是用Excalidraw 画出来的。 我们平常不管是工作中,还是在日常写文章&#x…...

2023年功能测试还值得入行吗?
前言 鉴于笔者从13年入行IT行业,经历了只有开发没有测试的阶段,经历了14年只要会基本的功能测试在一线就能薪资过万的阶段,经历了17年只要会一点自动化,会一点性能就能蒙骗过面试官的阶段,更经历了19年所有面试官对于…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

Razor编程中@Html的方法使用大全
文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...

Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...