什么是JWT??

一、什么是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);
    }
}

测试结果

 

 

JWTJSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519)。它是一种轻量级的安全传输方式,用于在网络应用间传递声明信息。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。 头部包含了关于令牌的元数据和加密算法的信息,通常由两部分组成:令牌类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。 载荷是JWT的主要内容,包含了一些声明信息,如用户ID、角色、权限等。载荷可以自定义,但建议只包含一些非敏感的信息,因为JWT是可解码的。 签名是对头部和载荷进行加密生成的,用于验证JWT的真实性和完整性。签名需要使用头部中指定的算法和密钥进行生成,接收方可以通过验证签名来确保JWT没有被篡改。 生成JWT token的过程如下: 1. 创建一个包含所需声明信息的JSON对象。 2. 使用Base64编码头部和载荷,形成两个字符串。 3. 将两个字符串用点号连接起来,形成一个未签名的JWT。 4. 使用指定的算法和密钥对未签名的JWT进行签名,生成签名字符串。 5. 将签名字符串添加到未签名的JWT末尾,形成最终的JWT token。 验证JWT token的过程如下: 1. 将接收到的JWT token按点号分割为头部、载荷和签名三部分。 2. 使用相同的算法和密钥对头部和载荷进行签名,生成一个新的签名字符串。 3. 将新生成的签名字符串与接收到的签名进行比较,如果相同,则说明JWT token是有效的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值