JWT

1、JWT简介

  • JWT(JSON Web Token),通过JSON形式作为Web应用中的令牌,用于在各方之间安全的将信息作为JSON对象传输。在数据传输过程中还可以完成数据加密、签名等相关处理
    • 授权:这是使用JWT的最常见的方案,一旦用户登录,每个后序的请求将包含JWT,从而允许用户访问该令牌允许的路由,服务和资源
    • 信息交换:在各方之间安全的传输信息的好方法。因为可以对JWT进行签名

image.png

  • 认证流程
    • 前端通过web表单将自己的用户名和密码发送到后端接口。这一过程一般是HTTP POST请求
    • 后端校验用户名和密码成功后,将用户的id等其他信息作为JWT Payload(负载),将其与头部分别进行Base64编码拼接后签名,形成一个JWT
    • 后端将JWT字符串返回给前端。前端可以将返回的结果保存在localStorage或sessionStorage上,退出登录时删除保存的JWT即可
    • 前端每次请求将JWT放入HTTP Header中的Authorization位。
    • 后端检查是否存在JWT,如存在则验证JWT的有效性。例如,检查签名是否正确;检查Token是否过期;检查Token的接收方是否是自己
    • 验证通过后后端使用JWT中包含的用户信息进行其他逻辑操作,返回相应结果

  • JWT优势
    • 简洁:可以通过URL,POST参数或者在HTTP header发送,因为数据量小,船速速度也很快
    • 自包含:负载中包含了所有用户所需要的信息,避免了多次查询数据库
    • 因为Token是以JSON加密的形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持
    • 不需要在服务端保存会话信息,特别适用于分布式微服务

2、JWT结构

  • 令牌组成
    • 标头(Header):通常有两部分组成,令牌的类型(即JWT)和所使用的签名算法
    • 有效载荷(Payload):其中包含声明。有关实体(通常是用户)和其他数据的声明
    • 签名(Signature):前面两部分都是使用Base64进行编码的,即前端可以解开直到里面的信息。Signature需要使用编码后的header和payload以及我们提供的一个秘钥,然后使用header中指定的签名算法(HS256)进行签名。签名的作用保证JWT没有被篡改过

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

3、使用JWT

  • maven依赖
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
  • jwt实例
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.HashMap;

public class JWTDemo {
    public static void main(String[] args) {


        //创建token字符串,保存userId到token中
        String token = createToken("1001");

        //根据token字符串验证,得到用户Id
        String userId = jwtVerifier(token);

        System.out.println(userId);


    }

    //创建token
    public static String createToken(String userId){
        HashMap<String, Object> map = new HashMap<>();
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND,60);

        String token = JWT.create()
                .withHeader(map)   //header
                .withClaim("userId",userId)  //有效载荷
                .withExpiresAt(instance.getTime())  //指定令牌过期时间
                .sign(Algorithm.HMAC256("miyao"));  //签名

        return token;
    }

    //验证token
    public static String jwtVerifier(String token){
        //创建验证对象
        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("miyao")).build();

        //验证token
        DecodedJWT verify = jwtVerifier.verify(token);

        //获取token中的有效荷载
        String userId = verify.getClaim("userId").asString();

        return userId;
    }
}


  • 常见异常信息
    • SingatureVerificationException: 签名不一致异常
    • TokenExpiredException: 令牌过期异常
    • AlgorithmMismatchException: 算法不匹配异常
    • InvalidClaimException: 失效的payload异常

4、JWT工具类

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.util.Calendar;
import java.util.HashMap;

public class jwtUtil {

    private final String sign = "yaoshi";

    //创建token
    public String getToken(HashMap<String,String> map){
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.DATE,7);  //过期时间 7 天

        //得到Builder对象
        JWTCreator.Builder builder = JWT.create();

        //设置有效载荷
        map.forEach((k,v)->{
            builder.withClaim(k,v);
        });

        //设置令牌过期时间和签名
        String token = builder.withExpiresAt(instance.getTime())
                .sign(Algorithm.HMAC256(sign));

        return token;
    }

    //验证token,返回有效载荷
    public DecodedJWT verifier(String token){
        return JWT.require(Algorithm.HMAC256(sign)).build().verify(token);
    }
}
posted @ 2022-11-06 22:39  youmo~  阅读(39)  评论(0编辑  收藏  举报