什么是JWT??-程序员宅基地

技术标签: java  前端  json  

一、什么是JWT

        JWT(JSON WEB TOKEN),通过数字签名的方式,以json对象为载体,在不同的服务终端之间安全的传输信息,用来解决传统session的弊端

        JWT在前后端分离系统,或跨平台系统中,通过JSON形式作为WEB应用中的令牌(token),用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程中,还可以完成数据加密、签名等相关处理。

        token一般都是用来认证的,比如我们系统中常用的用户登录token可以用来认证该用户是否登录。jwt也是经常作为一种安全的token使用。

二、为什么要用JWT?

        我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一次进行用户认证才行,因为根据http协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于session认证

传统的session认证有如下的问题:

  • 每个用户经过我们的应用认证之后,将认证信息保存在session中,由于session服务器中对象,随着认证用户的增多,服务器内存开销会明显增大;
  • 用户认证之后,服务端使用session保存认证信息,那么要取到认证信息,只能访问同一台服务器,才能拿到授权的资源。这样在分布式应用上,就需要实现session共享机制,不方便集群应用;
  • 因为session是基于cookie来进行用户识别的,cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。

自此, 可以看到了JWT认证的优势:

  • 简洁,可以通过URL、POST参数或Http  header发送,因为数据量小,传输速度快;
  • .自包含,负载(属于JWT的一部分)中包含了用户所需要的信息,不需要在服务器端保存会话信息,不占服务器内存,也避免了多次查询数据库,特别适用于分布式微服务
  • .因为token是以json加密的形式保存在客户端的,所以JWT可以跨语言使用,原则上任何WEB形式都支持。

三、JWT认证流程

  • 前端将用户名和密码发送到后端服务器。后端服务器对用户名和密码验证通过后,将用户信息作为JWT Payload负载,将其与头部进行Base64编号拼接后签名,形成JWT。形成的JWT本质上就是一个形如lll.zzz.xxx的字符串;
  • 后端将JWT字符串作为登陆成功的返回结果返回给客户端。前端可以将返回结果保存在localStorage,退出登陆时,前端删除保存的JWT即可;
  • 前端在每次请求时将JWT放入HTTP  Header中的Authorization位;
  • 后端检查是否存在,如果验证JWT有效,后端就可以使用JWT中包含的用户信息。

四、JWT的组成

        JWT其实就是一段字符串,由标头(Header)、有效载荷(Payload)和签名(Signature)这三部分组成,用  .  拼接。在传输的时候,会将JWT的三部分分别进行Base64编码后用.进行连接形成最终传输的字符串。

1.Header

        它会使用 Base64编码组成JWT结构的第一部分。

Header{ //标头
   ‘typ’:’JWT’,    表示token类型
   ‘alg’:’HS256’   表示签名算法
}

2.Payload

        用于存储主要信息,使用 Base64编码组成JWT结构的第二部分。由于该信息是可以被解析的,所以,在信息中不要存放敏感信息。

Payload //有效负载
{
   ‘userCode’:’43435’,
  ‘name’:’john’,
  ‘phone’:'13950497865'
}

3.Signature

        前面两部分都使用Base64进行编码,前端可以解开知道里面的信息, Signature需要使用编码后的header和payload以及我们提供的一密钥,然后使用header中指定的签名算法进行签名,以保证JWT没有被篡改过。

        使用Signature签名可以防止内容被篡改。如果有人对头部及负载内容解码后进行修改,再进行编码,最后加上之前签名组成新的JWT。那么服务器会判断出新的头部和负载形成的签名和JWT附带的签名是不一样的。如果要对新的头部和负载进行签名,在不知道服务器加密时用的密钥的话,得出来的签名也是不一样的。



五、JWT的使用

1.JWT的环境搭建

        在pom.xml文件中导入依赖:

        <!--   JWT依赖-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.13.0</version>
        </dependency>

        <!--        添加web启动器坐标-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

2.创建JWT工具类Util

  • JWT的创建

  /**
     * 创建JWT
     * @param map
     * @return
     */
    public String creatJWT(Map<String,String> map){
        //设置Jwt的超时时间
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE,30);
        //创建JWT,使用JWT.creat()方法进行创建
        //此时builder对象中默认设置了header--表头,默认为JWT
        JWTCreator.Builder builder = JWT.create();
        //将信息写入有效载荷中payload
        for (String key:map.keySet()){
            //通过withClaim方法传入传输到payload中
            builder.withClaim(key,map.get(key));
        }
        //利用builder和签名创健token,同时利用Calender类设置过期时间
        String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256("lovo"));
        return token;
    }

  • JWT的解码
   /**
     * 通过token和键解码信息
     * @param token
     * @param key
     * @return
     */
    public String verify(String token,String key){
        //创建解码对象,利用JWT签名去完成解码对象的创建
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("lovo")).build();
        //包含了JWT解码信息
        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        //利用键得到存放在有效负载payload的数据
        String value = decodedJWT.getClaim(key).asString();
        return value;
    }

  • 工具类测试
 public static void main(String[] args) {
        Util util = new Util();
        Map map = new HashMap();
        map.put("name","tom");
        map.put("pwd","123");
        System.out.println(util.creatJWT(map));

        String token = util.creatJWT(map);
        String value = util.verify(token,"pwd");
//        System.out.println(value);
    }

3.书写控制类Controller,用于JWT的发送

@Controller
public class JWTController {


    /**
     * 模拟登录
     * @param name
     * @param pwd
     * @param response
     * @return
     */
    @RequestMapping("login")
    @ResponseBody
    public String login(String name, String pwd, HttpServletResponse response) {
        if (name.equals("java") && pwd.equals("123")) {
            //此时拥有该用户,则利用该用户的信息创建JWT
            Map map = new HashMap();
            map.put("name", name);
            map.put("pwd", pwd);
            String token = new Util().creatJWT(map);
            //通过响应头发送给客户端
            response.setHeader("token", token);
            return "ok";
        }
        return "no";
    }

    @RequestMapping("getLogin")
    @ResponseBody
    public String getLogin(HttpServletRequest request){
        String token = request.getHeader("token");
        //利用工具对其进行解码
        String name = new Util().verify(token,"name");
        String pwd = new Util().verify(token,"pwd");
        return "name="+name+"&pwd="+pwd;
    }

}

4.在resource目录下书写user.html,用于JWT的接收

        客户端通过axios接收响应头,并保存在sessionStorage中。

        客户端会再次请求,读取localStorage的JWT信息,以请求头的方式,发送给服务器。

        服务器从请求头中得到jwt字符串,解析后,得到数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="axios.min.js"></script>
</head>
<body>
<form action="#">
    用户名:<input type="text" id="name"><br>
    密码:<input type="text" id="pwd"><br>
    <input type="button" value="登录" onclick="login()">
    <input type="button" value="获取登录对象" onclick="getLogin()">
</form>

<script>
    function login(){
        let nameObj = document.getElementById("name")
        let pwdObj = document.getElementById("pwd")
        axios.get('/login',{
            params:{
                name:nameObj.value,
                pwd:pwdObj.value
            }
        }).then(res=>{
            console.log(res)
            if (res.data==='ok'){
                localStorage.setItem("token",res.headers.token)
            }else {
                alert("用户名或密码错误!")
            }
        })
    }


    /**
     * 获取登录对象
     */
    function getLogin(){
        //读取localStorage的信息,以请求头的方式,发送给服务器
        let token = localStorage.getItem("token");
        //添加配置,在请求头中加入JWT的信息
        let config = {
            headers:{"token":token}
        }
        axios.get('/getLogin',config).then(res=>{
            console.log(res)
        })
    }
</script>

</body>
</html>

5.MainServer测试

@SpringBootApplication
public class MainServer {
    public static void main(String[] args) {
        SpringApplication.run(MainServer.class,args);
    }
}

测试结果

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/as15282235592/article/details/126694874

智能推荐

NFC技术演进_nfc的演进-程序员宅基地

文章浏览阅读289次。RF演进protocol 演进_nfc的演进

1.3 wait 和notify 原理_wait 和 notify原理-程序员宅基地

文章浏览阅读384次。wait 和notify 是实现线程之间的协同工作,必须结合synchronized使用,wait 释放锁,notify 不释放锁(但是此时会通知在等待的wait,该notify完全执行完毕,才真正释放锁)public class DemoThread18{ //原子类 private volatile List&lt;String&gt; list = new ArrayList..._wait 和 notify原理

谷歌又出浏览器了Google Chrome_谷歌浏览器 我是人类-程序员宅基地

文章浏览阅读601次。相信大部分 做ui的朋友 都非常痛恨一件事情 就是程序以及css和不同浏览器的兼容问题,我就奇怪了google你不好好的做你的搜索引擎,弄什么浏览器呀,本让现在 作东西考虑各个浏览器兼容 已经够累的,你还真会添乱。本来做你就做也无所谓,还花那么大力气推广,要不说你有钱,有了用户群,写东西就不得不考虑你了,大哥我们这些 闷头写程序的不容易,我们还要养家户口呢,你就别添乱了行不行。 _谷歌浏览器 我是人类

微信公众号在线选房电子选车位房地产云开盘线上大屏幕抢房系统-程序员宅基地

文章浏览阅读551次,点赞18次,收藏9次。前端演示咨询客服:

MAKO Vimba2.0安装教程和qt中调用Vimba相机_vimba viewer-程序员宅基地

文章浏览阅读6.4k次,点赞7次,收藏29次。一、MAKO Vimba2.0安装教程1. 打开Vimba2.0安装软件,用户可到大恒官网下载最新驱动。2.选择选项Application Development和安装路径,注意:安装路径中不要存在空格。然后,点击Star,开始安装。 3.勾选Install Vimba Drivers,然后,点击Exit退出。4.接下来继续安装,勾选-选择“安装”,重复操作..._vimba viewer

【Linux4.1.12源码分析】协议栈报文接收之传输层处理分析(UDP)___udp4_lib_rcv-程序员宅基地

文章浏览阅读3k次。UDP报文的处理入口是udp_rcv函数,该函数是在ip_local_deliver_finish函数中被调用的。1、udp_rcv函数int udp_rcv(struct sk_buff *skb){ return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP);}2、__udp4_lib_rcv函数int __udp4_lib_rcv___udp4_lib_rcv

随便推点

HOG特征——行人识别_hog特征识别行人 peopledetector=vision.peopledetector; i=-程序员宅基地

文章浏览阅读1.8k次,点赞4次,收藏24次。HOG特征简介HOG 全称为 Histogram of Oriented Gradients ,即方向梯度的直方图。HOG 是由 Navneet Dalal & Bill Triggs 在 CVPR 2005发表的论文中提出来的,目的是为了更好的解决行人检测的问题。先来把这几个字拆开介绍,首先,梯度的概念和计算梯度的方法已经在前一篇文章中介绍了,方向梯度就是说梯度的方向我们也要利用上,..._hog特征识别行人 peopledetector=vision.peopledetector; i=imread(

Spring Cloud 微服务的安全保护_springboot微服务安全-程序员宅基地

文章浏览阅读2.8k次,点赞2次,收藏9次。上一篇文章中介绍了如何使用Spring Cloud搭建微服务,在本文中讲讲如何对微服务进行安全保护。在Spring Cloud中对应用进行安全保护通常使用Spring Security,这种方式集成起来非常简单而且很容易扩展现有的应用场景。在分布式环境中Spring Security使用Spring Session和Redis来共享会话。共享会话可以将在微服务网关中登录的用户验证信息传递到系统..._springboot微服务安全

生物信息学中两种常用的文本文件_.fa.gz-程序员宅基地

文章浏览阅读961次。通过自学《碱基矿工》[http://mp.weixin.qq.com/mp/homepage?__biz=MzAxOTUxOTM0Nw==&hid=1&sn=d945cf61bd86e85724e146df42af5bcc&scene=18#wechat_redirect]下面分别介绍这两种格式FASTAFASTA常作为存储有顺序的序列数据的文件后缀,包括我们常用的..._.fa.gz

【centos安装mysql服务器并开启远程访问】_centos 查看 mysql 远程连接-程序员宅基地

文章浏览阅读1k次。centos安装mysql如果设置的密码太简单了会报错( ERROR 1819 (HY000): Your password does not satisfy the current policy requirements)解决方案如下:登录mysql执行:第一个密码强度等级,第二个是密码长度设置为6位(如果你设置的是8位就不做修改)另外可以通过语句查看密码设置规则2 赋权所有远程ip都可以进行登录(如果未开放端口得需要去腾讯云或者阿里云官网实例防火墙与策略开启端口,mysql默认的_centos 查看 mysql 远程连接

Linux(centos)下Nginx+Keepalived集群环境搭建_linux搭建nginx+keepalived-程序员宅基地

文章浏览阅读299次。本人使用的环境是CentOS-6.4-x86_64-bin-DVD1.rar,nginx-1.6.2.tar.gz,keepalived-1.2.18.tar.gz。三台机器ip:192.168.1.123,192.168.1.124。同时关闭两台虚拟机的防火墙:chkconfig iptables off(永久关闭防火墙)..._linux搭建nginx+keepalived

WebMagic Java 爬虫的简单应用_webmagic没反应-程序员宅基地

文章浏览阅读2k次。前段时间做旅游本体的知识库,我和老师反应说景点之间关系太少了,导致整个图很稀疏。。“你去wiki上抓一批数据吧”,就这样被自己坑了。由于一直在用java做项目,ZWQ师兄推荐的是selenium,这个我想说真的很强大,还支持JS渲染,不过当我看到这篇的时候,我决定学一下WebMagic。项目中文文档地址:http://webmagic.io/docs/zh/这个项目很容易上手,只要_webmagic没反应