分布式环境认证和授权-基于springboot+JWT+拦截器实现-实操+源码下载

1、功能概述?

1、当用户登录的时候,将用户的信息通过JWT进行加密和签名,并将JWT产生了token信息保存到当前浏览器的localStoragee中,即本地存储中。

2、当用户登录成功后,访问其他资源的时候,程序从localStorage中获取token的信息,并将token的信息存入到请求头中。

3、拦截器拦截当前的请求,并从请求中获取到token的值,使用JWT进行验证,如果验证表示当前用户合法就放行,如果验证不通过表示不合法,不放行。

2、JWT概述

1、JSON WebToken(JWT)是一种紧凑、自包含方式的、遵循RFC 7519开放标准,是一种协议。

2、JWT中的声明(如何用户名/编号等信息)被编译成JSON对象,并且这些信息会经过数字签名,信息可以进行验证和信任。

3、JWT支持以下签名和验证算法: HMAC、RSA 或 ECDSA。

4、JSON WebToken是常用的跨域身份验证方案。

5、JWT生成的token内容可以被解析,因为采用的是base64算法,但是使用了签名所以数据不能被篡改,敏感信息不能放入其中如密码,放置信息泄露。

2.1、JWT优点

1、以json行书传输,数据量小,传输速度快且JWT是跨语言的。

2、适用于分布式和微服务架构,因为信息的保存不依赖于session或者cookie.

3、使用cookie不适合移动端、但是JWT既不依赖session也不依赖cookie,单点登录实现容易。

4、因此无论是单体结构还是分布式都可以使用JWT进行身份认证。

2.2、JWT的组成结构

我们可以使用官网:https://github.com/auth0/java-jwt,学习JWT相关的结构

JWT主要有三部分组成:标头(Header)+有效载荷(Payload)+签名(Signature)

格式通常为:Header.Payload.Signature

【第一部分:Header

由令牌类型和签名算法组成

【第二部分:Payload

有效负载,通常定义用户自定义信息,这部分构成JWT的第二部分数据,是推荐使用的,而非强制。主要包括

iss:发行人或签发者

exp:到期时间

sub:主题

aud:用户/jwt接收方

nbf:在此之前不可用

iat:发布时间/签发时间

jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击   

【第三部分:Signature

由于Header和Payload都是使用Base64进行编码,是可逆的,因此信息可以被解析出来,为了放置信息被篡改,加入了签名。同时如果提供的签名不正确,jwt生成的token不能被正确解析。

通过jwt.io这个网站观看jwt生成的token样式

2.3、JWT使用的大体流程

1、在登录验证通过后,给用户生成一个对应的随机token(注意这个token不是指jwt,可以用uuid等算法生成),然后将这个token作为key的一部分,用户信息作为value存入Redis,并设置过期时间,这个过期时间就是登录失效的时间;

将第1步中生成的随机token作为JWT的payload生成JWT字符串返回给前端;

前端之后每次请求都在请求头中的Authorization字段中携带JWT字符串;

后端定义一个拦截器,每次收到前端请求时,都先从请求头中的Authorization字段中取出JWT字符串并进行验证,验证通过后解析出payload中的随机token,然后再用这个随机token得到key,从Redis中获取用户信息,如果能获取到就说明用户已经登录。

3、使用JWT实现签发token/校验token/获取token信息

3.1、项目结构

3.2、创建springboot工程映入jwt的包信息

我们使用java-jwt实现

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.6</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.txc</groupId><artifactId>distribute-session-jwt</artifactId><version>0.0.1-SNAPSHOT</version><name>distribute-session-jwt</name><description>distribute-session-jwt</description><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><image><builder>paketobuildpacks/builder-jammy-base:latest</builder></image><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration></plugin></plugins></build>
</project>

3.3、创建login测试类签发token

注意:理论上,签名可以通过穷举法进行破除,所以一般签名不要使用的过于简单或者定期更换签名,增强系统的安全性。

程序中通过jwt.create进行签发,并设置了签名信息,过期时间,及相关的用户信息。

@RestController
public class JWTController {@RequestMapping("/login")
public String login(@RequestParam String username, HttpServletResponse response, HttpServletRequest request){//创建map存放用户信息Map<String,Object> map=new HashMap<>();map.put("userid","1001");map.put("username",username);String token=null;try {//!@#$%^&123~:是我们使用的签名Algorithm algorithm = Algorithm.HMAC256("!@#$%^&123~");token = JWT.create().withIssuer("auth0")//角色权限.withClaim("userinfo",map)//设置token的过期时间为一个小时.withExpiresAt(new Date(System.currentTimeMillis()+3600000)).sign(algorithm);//将token信息添加到token中} catch (JWTCreationException exception){System.out.println("=====程序异常=======");}return token;
}
}

3.4、访问login后结果如下

3.5、解析之后效果如下

将浏览器生成的token的信息,拷贝到JSON Web Tokens - jwt.io地址中解析如下:

3.6、创建getLoginToken实现校验和获取token信息

此处为了测试方便,我们是直接手动的将token的值拷贝过来,放在地址栏中传递到该方法,实际的项目中,会将token的信息存放到请求的header中,用户通过header获取。

由于JWT是在请求头中传递的,所以为了避免网络劫持,推荐使用HTTPS来传输,更加安全

请求地址:localhost:8080/getLoginToken?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCIsInVzZXJpbmZvIjp7InVzZXJpZCI6MTAwMSwidXNlcm5hbWUiOiJ4aWFvY2h1biJ9LCJleHAiOjE3MDE4ODMwNjd9.ceGcf1Q_TG26HFCBf20E0TIoSXlJYK7gudAvfKOjUlw

说明1校验的时候签名千万不能写错了,否则会提示校验失败。

说明2如果抛出异常说明校验失败

说明3注意获取值的返回值结果

@RequestMapping("/getLoginToken")
public String getLoginToken(@RequestParam String token){//验证一个tokentry{Algorithm algorithm=Algorithm.HMAC256("!@#$%^&123~");JWTVerifier verifier=JWT.require(algorithm).withIssuer("auth0").build();//jwt通过verify进行校验DecodedJWT jwt=verifier.verify(token);//如果校验成功就获取userinfo的信息Map<String,Object> userinfo=jwt.getClaim("userinfo").asMap();//获取userinfo中的username值并转化成stringString username=userinfo.get("username").toString();int userid=Integer.valueOf(userinfo.get("userid").toString());//获取token信息的有效期Date exp=jwt.getExpiresAt();return username+"==="+userid+"==="+exp;}catch(Exception e){e.printStackTrace();return "校验失败";}
}

返回结果

4、拦截器统一处理token

案例实现当用户登录成功后,将当前token信息保存到浏览器本地存储位置。当再次在项目中发起请求的时候,从本地不去到token,将token信息保存到请求头中,拦截器获取到请求头中的token信息,如果验证成功就允许访问资源,如果验证失败重定向到登录页中。

4.1、在springboot中创建拦截器

1、下面的拦截器主要实现,如果没有从请求头中获取到token就抛出异常,程序不放行。如果获取了token的值,但是验证没有通过不放行。

2、DecodedJWT jwt=verifier.verify(token);就是验证方法,如果验证不通过会抛出异常。
3、hasText判断字符串是否为null用法:

   https://blog.csdn.net/tangshiyilang/article/details/134926299

@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println(request.getMethod()+"============拦截器执行===============");String token=request.getHeader("token");if(!StringUtils.hasText(token)){throw new TokenIsNullException();}try{//验证一个tokenAlgorithm algorithm=Algorithm.HMAC256("!@#$%^&123~");JWTVerifier verifier= JWT.require(algorithm).withIssuer("auth0").build();DecodedJWT jwt=verifier.verify(token);String username=jwt.getClaim("username").asString();request.setAttribute("username",username);}catch(Exception e){return false;}return true;}
}

4.2、在工程中加载拦截器

实现WebMvcConfigurer接口,重写addInterceptors方法。

addInterceptor:表示需要加载的拦截器

addPathPatterns:需要拦截的地址

excludePathPatterns:不需要拦截的地址,如果拦截login地址会出现死循环的情况。

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/test.do").excludePathPatterns("/login");}}

4.3、创建自定义异常类

public class TokenIsNullException extends RuntimeException{public TokenIsNullException(){super("token为空!");}
}

4.4、创建登录使用的接口login1

说明1:为了便于测试,之前的login方法我们保留了下来,单独又写了一个login1用于当前的测试。

@RequestMapping("/login1")
@ResponseBody
public String login1(@RequestParam String username, HttpServletResponse response, HttpServletRequest request){System.out.println("=======login==========="+username);//创建map存放用户信息Map<String,Object> map=new HashMap<>();map.put("userid",1001);map.put("username",username);String token=null;try {//!@#$%^&123~:是我们使用的签名Algorithm algorithm = Algorithm.HMAC256("!@#$%^&123~");token = JWT.create().withIssuer("auth0")//角色权限.withClaim("userinfo",map)//设置token的过期时间为一个小时.withExpiresAt(new Date(System.currentTimeMillis()+3600000)).sign(algorithm);} catch (JWTCreationException exception){System.out.println("=====程序异常=======");return "0";//如果返回值为0,表示失败。}return "1";//如果返回值为1,表示成功。
}

4.5、创建test.do

这个方法主要用于模拟登录成功后,我们需要访问的资源,拦截器会获取请求中的token,如果验证成功允许访问,否则不允许访问。

@RequestMapping("/test.do")
@ResponseBody
public String test(){System.out.println("=====test.do========");return "恭喜你资源访问成功。";
}

4.6、创建登录页实现登录

说明1:localStorage.setItem("token",msg2);使用jquery向本地存储数据
说明2:window.location.href="show.html";//登录成功后,重定向到show.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="js/jquery-2.1.1.js"></script><script type="text/javascript">function login(){$.ajax({type:"get",url:"/login",cache:false,dataType:"text",data: {"username":"xiaochun"},success:function(msg2,request){alert("==msg2==="+msg2);//存储字段localStorage.setItem("token",msg2);//重定向到主页window.location.href="show.html";}});}</script>
</head>
<body><input type="text" name="username"> <br><button onclick="login()">登录</button>
</body>
</html>

登录页样式:

4.7、创建show.html页面

如果登录成功后,页面会重定向到show.html中。这个时候点击a标签,触发getUserToken函数访问test.do资源,在访问资源的同时会从本地获取token信息,放入到请求头中。这个时候请求会被拦截器拦截,拦截器获取请求头中的token信息,如果验证成功允许访问test.do,如果验证失败拒绝访问。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script src="js/jquery-2.1.1.js"></script><script type="text/javascript">function getUserToken(){//获取保存在localStore中的数据var mytoken=localStorage.getItem("token");alert("===token==="+token);$.ajax({type:"post",url:"/test.do",headers: {'token':mytoken},success:function(msg2){//存储字段localStorage.setItem("token",msg2);//重定向到主页window.location.href="show.html";}});}</script>
</head>
<body>我是主页 <br><a href=" javaScript:void(0)"  onclick="getUserToken()">发起请求访问test.do资源</a>
</body>
</html>

4.8、登录成功后的信息

登录成功后重定向到show.html.

4.9、通过show.html访问test.do资源

当我们点击”发起请求访问test.do资源”,经过拦截器校验token成功,返回成功访问,且从请求头中可以看到token的信息。

4.10、手动清除token信息,再次访问

从下面的地址可以看出,test.do没有成功访问,拦截器没有放行。

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

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

相关文章

二蛋赠书十一期:《TypeScript入门与区块链项目实战》

前言 大家好&#xff01;我是二蛋&#xff0c;一个热爱技术、乐于分享的工程师。在过去的几年里&#xff0c;我一直通过各种渠道与大家分享技术知识和经验。我深知&#xff0c;每一位技术人员都对自己的技能提升和职业发展有着热切的期待。因此&#xff0c;我非常感激大家一直…

Backtrader 文档学习-Quickstart

Backtrader 文档学习-Quickstart 0. 前言 backtrader&#xff0c;功能十分完善&#xff0c;有完整的使用文档&#xff0c;安装相对简单&#xff08;直接pip安装即可&#xff09;。 优点是运行速度快&#xff0c;支持pandas的矢量运算&#xff1b;支持参数自动寻优运算&#x…

DNS漫游指南:从网址到IP的奇妙之旅

当用户在浏览器中输入特定网站时发生的整个端到端过程可以参考下图 1*4vb-NMUuYTzYBYUFSuSKLw.png 问题&#xff1a; 什么是 DNS&#xff1f; 答案 → DNS 指的是域名系统&#xff08;Domain Name System&#xff09;。DNS 是互联网的目录&#xff0c;将人类可读的域名&#…

cobalt strike基础使用

coblat strike使用 服务搭建 首先将server端文件放进kali中 对其赋权 执行时需要root权限 设置ip 启动服务 ./teamserver 10.4.7.138 123456回到win11启动cs&#xff0c;输入刚才配置的信息 上线方式 木马&#xff08;exe上线&#xff09; 查看一下开放的端口 添加监听 …

最新科研成果:在钻石中存储多比特数据,实现25GB数据密度

近日&#xff0c;纽约城市大学&#xff08;CUNY&#xff09;的研究人员已经成功地利用钻石原子结构中的小型氮缺陷作为“颜色中心”来写入数据进行存储&#xff08;然后是检索&#xff09;。这项发表在《自然纳米技术》上的技术允许通过将数据编码为多个光频率&#xff08;即颜…

T5论文个人记录

参考&转载自&#xff1a; 介绍Google推出的大一统模型—T5_谷歌大模型_深度之眼的博客-CSDN博客 T5 和 mT5-CSDN博客 T5&#xff1a;Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer&#xff08;万字长文略解T5&#xff09;_t5论文…

tomcat软件部署

1.tomcat 2.tomcat功能组件 3.请求过程 4.tomcat部署 一.tomcat tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器&#xff0c;却不如nginx&#xff0c;apache功能强大&#xff0c;通常作为 Servlet 和 JSP容器&#xff0c;单…

2023 CCF中国软件大会(CCF ChinaSoft)“软件定义汽车”论坛成功召开

2023年12月1日下午&#xff0c;2023年度CCF中国软件大会“软件定义汽车”论坛成功召开。 本次论坛由华东师范大学蒲戈光教授、武汉光庭信息技术股份有限公司朱敦尧董事长以及华东师范大学张越龄副教授联合组织举办。论坛主要关注汽车的智能网联化与电动化的融合&#xff0c;包括…

scala方法与函数

定义方法定义函数方法和函数的区别scala的方法函数操作 1.9 方法与函数 1.9.1 定义方法 定义方法的基本格式是&#xff1a; def 方法名称&#xff08;参数列表&#xff09;&#xff1a;返回值类型 方法体 def add(x: Int, y: Int): Int x y println(add(1, 2)) // 3 //也…

揭秘高效大型语言模型:技术、方法与应用展望

近年来&#xff0c;大型语言模型&#xff08;LLMs&#xff09;在自然语言处理领域取得了显著的进展&#xff0c;如GPT-series(GPT-3, GPT-4)、Google-series(Gemini, PaLM), Meta-series(LLAMA1&2), BLOOM, GLM等模型在各种任务中展现出惊人的能力。然而&#xff0c;随着模…

初学python的体会心得20字,初学python的体会心得2000

大家好&#xff0c;小编来为大家解答以下问题&#xff0c;学了python的心得体会200字&#xff0c;初学python的体会心得20字&#xff0c;现在让我们一起来看看吧&#xff01; 本学期&#xff0c;我们学习了杨老师的《python语言程序设计》这门课程&#xff0c;其实早在大一期间…

人工智能导论习题集(1)

第二章&#xff1a;知识表示 题1题2题3题4题5 题1 题2 题3 题4 题5

HarmonyOS创建一个page并实现界面跳转(JavaScript)

上文 HarmonyOS创建JavaScript(类 Web开发模式)项目中 我们接触了这咋类Web开发模式 并创建了一个项目 之前 我们 ArkTS 开发模式的项目 resources目录 下的 base目录下的 profile目录下的 main_pages.json中存放了 我们page目录的配置 但是 我们javaScript模式 下 好像没有哦 …

DataFunSummit:2023年数据治理在线峰会-核心PPT资料下载

一、峰会简介 数据治理&#xff08;Data Governance&#xff09;是组织中涉及数据使用的一整套管理行为。由企业数据治理部门发起并推行&#xff0c;关于如何制定和实施针对整个企业内部数据的商业应用和技术管理的一系列政策和流程。 数据治理是一个通过一系列信息相关的过程…

披荆斩棘的「矿区无人驾驶」,能否真正打开千亿级市场?

随着2022年备受瞩目的台泥句容矿无人驾驶运输项目硬核落地&#xff0c;以及相关科技公司开放该矿24小时无人矿卡生产运营直播以证明其项目并非在演示&#xff0c;2023年全国开启了大规模矿区无人驾驶商业化落地&#xff0c;堪称矿区无人驾驶元年。虽然我国矿区无人驾驶市场渗透…

zookeeper集群介绍

一个leader&#xff0c;多个follower&#xff0c;组成的集群 集群中只要有半数以上得节点存活&#xff0c;zookeeper集群就能正常服务 顺序一致性&#xff1a; 来自同一个client的更新请求按其发送顺序依次执行 原子性&#xff1a; 更新操作要么成功要么失败&#xff0c; 没有…

flink-1.17.2的单节点部署

flink 简介 Apache Flink 是一个开源的流处理和批处理框架&#xff0c;用于大数据处理和分析。它旨在以实时和批处理模式高效处理大量数据。Flink 支持事件时间处理、精确一次语义、有状态计算等关键功能。 以下是与Apache Flink相关的一些主要特性和概念&#xff1a; 流处理…

养牛场北斗综合管理系统解决方案

1.系统架构 随着我国北斗卫星导航定位系统的快速发展和定位精度的持续不断提高&#xff0c;在牛身上穿戴定位终端后可以实现对牛的位置和温度的测量&#xff0c;在蜂窝网络正常的情况下&#xff0c;定位和温度数据通过蜂窝网络通信方式回传到监控云平台&#xff0c;在蜂窝网络缺…

uniapp实现拨打电话跳转手机拨号界面 (ios和安卓通用)

效果展示&#xff1a;左边为安卓系统展示&#xff0c;右边为ios系统展示 代码&#xff1a; toPhone(){uni.makePhoneCall({phoneNumber: "10086", //要拨打的手机号success: (res) > {// console.log("调用成功")},fail: (res) > {// console.log(调…

784. 字母大小写全排列 dfs + 回溯算法 + 图解 + 笔记

784. 字母大小写全排列 - 力扣&#xff08;LeetCode&#xff09; 给定一个字符串 s &#xff0c;通过将字符串 s 中的每个字母转变大小写&#xff0c;我们可以获得一个新的字符串。 返回 所有可能得到的字符串集合 。以 任意顺序 返回输出 示例 1&#xff1a; 输入&#xf…