jwt——json web token介绍

https://www.bilibili.com/video/BV1i54y1m7cP

1. 什么是JWT

  • jwt(json web token)是一种开放标准(rfc 7519),它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象安全的传输信息。此信息可以验证和信任,因为它是数字签名的。jwt可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名
  • 通俗来讲,jwt就是通过JSON形式作为WEB应用中的令牌,用于在各方之间安全的将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理。

2. jwt能做什么

  1. 授权
    这是使用jwt最常见的方案。一旦用户登录,每个后续请求将包含JWT,从而允许用户访问该令牌允许的路由,服务和资源。单点登录是当今广泛使用JWT的一项功能,因为它的开销很小并且可以在不同的域中轻松使用。
  2. 信息交换
    jwt是在各方之间安全传输信息的好办法。因为可以对JWT进行签名,所以可以确保发件人是他们所说的人。此外,由于签名是使用标头和有效负载计算的,因此还可以验证内容是否遭到篡改。

3. 为什么是JWT

传统的基于session认证

因为http本身是一种无状态的协议,我们并不能知道是哪一个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器端存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为cookie,以便下次请求时发送给我们的应用,以便引用识别用户。

暴露问题:

  1. 每个用户经过我们的应用认证之后,我们的应用都要在服务端做一次记录,以便用户下次请求的鉴别,通常而言session是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大
  2. 用户认证之后,服务端做认证记录。如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须请求在这台服务器上,这样才能拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡器的能力,这也意味着限制了应用的扩展能力。
  3. 因为是基于cookie来识别用户的,当cookie被截获,用户很容易受到2跨站请求伪造的攻击。
  4. 在前后端分离系统中就更加痛苦:前后端分离([用户]< == > [前端web] < == > [代理层] < == > [后端应用] )在应用解耦后增加了部署的复杂性。通常用户一次请求就要转发多次。如果用session每次携带sessionid到服务器,服务器还要查询用户信息。同时如果用户很多。这些信息存储在服务器内存中,给服务器增加负担。还有就是CSRF(跨站伪造请求攻击),session是基于cookie进行用户识别的,cookie如果被截获,用户很容易受到跨站请求伪造的攻击。还有就是sessionid是一个特征值,表达的信息不够丰富。不容易扩展。入股后端应用是多节点部署,那么就要实现session共享机制。不方便集群应用。

jwt认证流程

  1. 首先,前端通过web表单将自己的用户名和密码发送到后端的接口。这一过程一般是一个HTTP POST请求。建议的方式是通过SSL加密传输(https协议),从而避免敏感信息被嗅探。
  2. 后端核对用户名和密码成功后,将用户的id等其他信息作为JWT payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个token。
  3. 后端将jwt字符串作为登陆成功的返回结果返回给前端。前端将返回的结果保存在localStorage或sessionStorage上,退出登录时前端删除保存的JWT即可。
  4. 前端在每次请求时将JWT放入HTTP Header中的Authorization位。(解决xss和xsrf问题)
  5. 后端检查是否存在,如存在验证JWT的有效性。例如,检查签名是否正确;检查token是否过期;检查token的接收方是否是自己(可选)。
  6. 验证通过后后端使用JWT包含的用户信息进行其它逻辑操作,返回相应结果。

jwt组成

jwt分为三个部分

  • 第一部分 header :标记使用什么算法 (HS256/RSA256)
    • 令牌的类型和所使用的签名算法,例如HMAC SHA256或RSA。
    • {“alg”:“HS256”,“typ”:“JWT”}
  • 第二部分 PayLoad (载荷)
    jwt存放的数据
  • 第三部分 PayLoad 采用MD5加密之后的签名值

! jwt不要存放重要数据
Base64.Encode(header). Base64.Encode(PayLoad ).签名值


    private static final String SIGN_KEY = "mayikt";
@Test
    public void test1(){
        JSONObject header = new JSONObject();
        header.put("alg","HS256");
        JSONObject payLoad = new JSONObject();
        payLoad.put("phone","18611011111");

        String payLoadStr = payLoad.toJSONString();
        //payLoad实现MD5加密
        String sign = DigestUtils.md5DigestAsHex((payLoadStr+SIGN_KEY).getBytes());
        Base64.Encoder encoder = Base64.getEncoder();
        String jwt = (String) encoder.encodeToString(header.toJSONString().getBytes())+"."+
                encoder.encodeToString(payLoadStr.getBytes())+"."
                +sign;
        System.out.println(jwt);//eyJhbGciOiJIUzI1NiJ9.eyJwaG9uZSI6IjE4NjExMDExMTExIn0=.badce2fbf728c40222e7fa75bcfa9e54
    }

生成的jwt可在https://jwt.io/验证

验证签名位true的情况下就可以获取payload数据,否则jwt无效。

优缺点

优点:

  • 简洁:数据量小,传输速度快
  • 自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库
  • jwt客户端就可验证,减轻服务端的压力,适合分布式微服务
  • 以JSON加密的形式保存在客户端,跨语言,原则上任何web形式都支持。
  • jwt查询效率比token高
  • 不容易被客户端篡改数据

缺点:

  • 一旦生成好一个jwt,后期比较难使它失效
  • 如果payload数据过多,占用服务器带宽资源。

建议在jwt的payload数据中增加时间戳,设置有效期

demo

  1. 引入依赖 com.auth0 java-jwt
<dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
  1. 生成token:
//生成时间戳
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,90);
        //生成令牌
        String token = JWT.create()
                .withClaim("user","yz")
                .withExpiresAt(instance.getTime()) //设置过期时间
                .sign(Algorithm.HMAC256("token!Q2W#E$RW"));//设置签名 保密 复杂
        //输出令牌
        System.out.println(token);//eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MDIxNzg3OTYsInVzZXIiOiJ5eiJ9.PavrIse8cAsM3bW4roGgkY8kHTAqBwiV8kZRT1K4eu8

解析结果:
header:{
  "typ": "JWT",
  "alg": "HS256"
}
payload: {
  "exp": 1602178796,
  "user": "yz"
}
  1. 根据令牌和签名解析数据
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("token!Q2W#E$RW")).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
System.out.println("user: " + decodedJWT.getClaim("user").asString());
//新增.withClaim("id",1)
System.out.println("id: " + decodedJWT.getClaim("id").asInt());
System.out.println("过期时间:" + decodedJWT.getExpiresAt());

整合spring boot

封装工具类JWTUtils

方法 getToken
传入Map<String,Object> map
foreach map -> withClaim

验证token方法 verify

获取token信息方法:getTokenInfo(String token,String key) 或直接返回DecodedJWT

连接数据库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值