详解基于JWT的token认证(Java实现)

详解基于JWT的token认证(Java实现)

观前提示:

本文所使用的IDEA版本为ultimate 2019.1,JDK版本为1.8.0_141。

1.简介

在计算机身份认证中是令牌(临时)的意思,token其实说的更通俗点可以叫暗号,在一些数据传输之前,要先进行暗号的核对,不同的暗号被授权不同的数据操作。

2.JWT

JSON Web Token,JSON Web令牌,我们下面的例子也是基于JWT实现的token认证,如下是一个完成的JWT串

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODMyOTQxNzEsInVzZXJOYW1lIjoidXNlciIsInBhc3N3b3JkIjoiMTExMTExMTEifQ.t6JLktclylpt2s5T_B1KDxYCJALr-fbZsy1J_YT57Jk

它是由三部分构成:header,payload和signature

2.1 header

由两部分构成

  1. 声明类型,为JWT。

  2. 声明加密的算法,使用 HMAC SHA256

JWT头部分是一个JSON对象,如下所示

{
	'typ': 'JWT',
	'alg': 'HS256'
}

对其进行Base64加密,则JWT构成第一部分。

2.2 payload

载荷部分是JWT的主体内容部分,也是一个JSON对象,由三部分构成

  1. 标准中注册的声明

  2. 公共的声明

  3. 私有的声明

标准中注册的声明提供了七个默认字段。

  1. iss:发行人

  2. exp:到期时间

  3. sub:主题

  4. aud:用户

  5. nbf:在此之前不可用

  6. iat:发布时间

  7. jti:JWT ID用于标识该JWT

公共的声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息(不推荐敏感信息)。

私有的声明

私有声明是提供者和消费者所共同定义的声明(不推荐敏感信息)。

如下例是一个样例的payload

{
	"sub": "1234567890",
	"name": "userName",
	"admin": true
}

对其进行Base64加密,构成JWT的第二部分。

注:默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。

2.3 signature

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。由三部分组成:

  1. header (Base64加密后的)

  2. payload (Base64加密后的)

  3. secret

服务器需要一个秘钥(secret),且仅仅保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

3.使用

客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。

此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。

使用格式如下

Authorization: Bearer JWT

JWT交互的整个流程如下图所示

在这里插入图片描述

4.例子

本例子仅仅提供了java实现JWT的签名和验证,http传输使用待以后补上。

pom.xml需要引用的jar包

<dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.10.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.10.2</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.10.2</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.10.2</version>
    </dependency>

工具类 TokenUtil.java

package token;

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.Date;
import java.util.HashMap;
import java.util.Map;

public class TokenUtil {
    
    

    //token过期时长30分钟
    private static final long EXPIRE_TIME = 30 * 60 * 1000;
    //token私钥
    private static final String TOKEN_SECRET = "abcdefg";

    public static String sign(String userName, String password) {
    
    

        String signData = "";
        //过期时间
        Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
        //私钥及加密算法
        Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
        //设置头信息
        Map<String, Object> header = new HashMap();
        header.put("typ", "JWT");
        header.put("alg", "HS256");

        signData = JWT.create()
                .withHeader(header)
                .withClaim("userName", userName)
                .withClaim("password", password)
                .withExpiresAt(date)
                .sign(algorithm);

        return signData;
    }

    /**
     * @Description token解码校验
     * @param token
     * @return
     * @Create 2020-03-03 by jjy
     */
    public static boolean verfiy(String token) {
    
    

        try {
    
    
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier jwtVerifier = JWT.require(algorithm).build();
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            String userName = decodedJWT.getClaim("userName").asString();
            String password = decodedJWT.getClaim("password").asString();
            if(!Test.USERNAME.equals(userName) || !Test.PASSWORD.equals(password)) {
    
    
                return false;
            }
            if(new Date().getTime() > decodedJWT.getExpiresAt().getTime()){
    
    
                return false;
            }
            
        } catch (Exception e) {
    
    
            return false;
        }

        return true;
    }
}

测试类 Test.java

package token;

public class Test {
    
    

    public static final String USERNAME = "user";
    public static final String PASSWORD = "11111111";

    public static void main(String[] args) {
    
    
        String token = TokenUtil.sign(USERNAME, PASSWORD);

        System.out.println("加密后的token为:" + token);

        boolean flag = TokenUtil.verfiy(token);

        if(flag){
    
    
            System.out.println("校验成功");
        } else {
    
    
            System.out.println("校验失败");
        }
    }
}

运行结果如下

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43611145/article/details/104649851