JWT认证原理

基于HTTP协议的软件认证方式有很多,鉴于HTTP协议无状态性,所有的认证方式必须解决HTTP请求有状态性问题,也就是如何创建一个HTTP上下文,将区分哪些HTTP请求来自同一用户操作。传统解决方式是生成HTTP会话,并将会话ID存放在客户端浏览器中的Cookie或者URL重写这两种方式,伴随系统实现方式变化,越来越多的系统使用前后端分离的架构,系统遭受XSS,XSRF攻击的可能性也越大。现在JWT认证被广泛应用。下面介绍JWT认证原理:

Json web token (JWT), 根据官网的定义,是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519),
JWT加密签名后形成类似aaa.bbb.ccc形式的字符串,是由三部分组成,客户端需要记录在浏览器的本到localStorage,每次请求时将JWT字符串放到HTTP Header中的Authorization里面,服务器单将解析JWT字符串,判断是否被篡改过、是否Token过期等,如果验证通过放行,如果不通过返回错误信息。

JWT由三部分组成

第一部分:头部(Header)

{
  "alg": "HS256",//JWT加密算法(alg)
  "typ": "JWT" // JWT Token类型(typ)
}

将头部进行base64加密,生成aaa.bbb.ccc的第一部分(aaa部分)

eyJhbGciOiJIUzI1NiJ9

第二部分:载荷(payload)

这部分就是存放信息的地方了,可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,常用的由 iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)。

{
    "iss": "HK",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "",
    "sub": "userid"
}

iss: 该JWT的签发者,一般是服务器或系统名称,是否使用是可选的;
iat(issued at): 在什么时候签发的(UNIX时间),是否使用是可选的;
exp(expires): 什么时候过期,这里是一个Unix时间戳,是否使用是可选的;
aud: 接收该JWT的一方,是否使用是可选的;
sub: 该JWT所面向的用户,userid,是否使用是可选的;
上述属性是JWT规范中定义的,用户还可以自定义属性,其数据结构是JSON

{
    "iss": "HK",
    "iat": 1441593502,
    "exp": 1441594722,
    "aud": "",
    "sub": "userid",
    "role":["admin","user"],
    "username":"java"
}

载荷(payload)通过Base64加密后生成aaa.bbb.ccc的第二部分(bbb)

eyJqdGkiOiIxIiwic3ViIjoidXNlciIsImlzcyI6InVzZXIiLCJpYXQiOjE1OTMzMDczOTMsImV4cCI6MTU5MzMwNzQ5M30

第三部分:签名(signature)

HMACSHA256(  
    base64UrlEncode(header) + "." + //第一部分 aaa
    base64UrlEncode(payload), //第二部分 bbb
    SECREATE_KEY  //服务器端存储的密钥
)

加密算法是在JWT第一部分中定义的加密算法

//加密内容
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
//签名,密钥是存储在服务器端的,不可泄漏给其他人
var signature = HMACSHA256(encodedString, '密钥'); 

加密后的JWT字符串

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxIiwic3ViIjoidXNlciIsImlzcyI6InVzZXIiLCJpYXQiOjE1OTMzMDczOTMsImV4cCI6MTU5MzMwNzQ5M30.RwsRLF1b3LbUvV_qJ7YGeht5JvpNXomijeKto1NI8Uw

在这里插入图片描述

例程

本例程只演示生成JWT Token字符串和验证JWT Token字符串,没有集成到登录模块中。
POM.xml添加依赖

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
package com.bnzj.cloud.auth;

import java.sql.Date;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.encoders.Base64;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;

public class JwtUtils {
    /**
     * 生成JWT
     * @param id 唯一ID
     * @param subject 主题
     * @param ttlMillis 有效微秒数
     * @return  String 返回JWT JSON 字符串
     *
     */
    public static String createJWT(String id, String subject, long ttlMillis) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject)   // 主题
                .setIssuer("user")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            builder.setExpiration(expDate); // 过期时间
        }
        return builder.compact();
    }
    /**
     * 验证JWT Token是否正确
     */
    public static CheckResult validateJWT(String jwtStr) {
        CheckResult checkResult = new CheckResult();
        Claims claims = null;
        try {
            claims = parseJWT(jwtStr);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) {
            checkResult.setErrCode(Constant.JWT_ERRCODE_EXPIRE);
            checkResult.setSuccess(false);
        } catch (SignatureException e) {
            checkResult.setErrCode(Constant.JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        } catch (Exception e) {
            checkResult.setErrCode(Constant.JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
            
        }
        return checkResult;
    }
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.decode(Constant.JWT_SECERT);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    
    /**
     * 
     * 解析JWT字符串
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
            .setSigningKey(secretKey)
            .parseClaimsJws(jwt)
            .getBody();
    }
    
    public static void main(String[] args)
    {
    	String id = "1";
    	String subject = "user";
    	long ttlMillis = 100000L;
    	//生成JWT字符串
    	String jwt = JwtUtils.createJWT(id, subject, ttlMillis);
    	System.out.println(jwt);
    	//验证JWT JSON是否合法 
    	System.out.println(JwtUtils.validateJWT(jwt));
    }
}
class CheckResult{
	boolean isOk;
	Claims claims;
	String code;
	
	public void setSuccess(boolean b)
	{
		this.isOk = b;
	}
	public void setClaims(Claims claims)
	{
		this.claims = claims;
	}
	public void setErrCode(String code)
	{
		this.code = code;
	}
	public String toString()
	{
		return "CheckResult[isOk="+isOk+",code="+code+",chaims="+claims+"]";
	}
}
class Constant{
	//错误代码定义
	public static String JWT_ERRCODE_EXPIRE = "expire";
	public static String JWT_ERRCODE_FAIL = "fail";
	//密钥 Base64编码
	public static String JWT_SECERT = 
            "MIICXAIBAAKBgQCsPS1u1r7pl3VsEK4Tvc2NjNvXhaO2ACh+R889bMo3rz9PQQur\n" +
            "bj83pKfFDqcAbj/bhXbcQMG+Gs8WlUFmFNBVr651IL5h1gCrxfyivsQbWYbX5p4J\n" +
            "fk52rCMr2QJjnCvyFh1layHZ2gg5/cLQsMR8SqGTHaQmSLCHkoaVq7u8IQIDAQAB\n" +
            "AoGARSoH7YNkhI7igzOrg5frTBUtTr2GgRZNLHCPot3l0jiYVq4LDpsl3aVMDZCV\n" +
            "tVGQaQmOMmH6quk/EZV7/o8LHUTGcj6i1p2vKBqmAz9M9eUFE/eFctf+k6PAiqKc\n" +
            "kFa9HRSBukWne8SZw81qcxVxxw728dvzzahjP/n/mf9c/wUCQQDYjbZyrdK6QH86\n" +
            "9P+VhZHeqEBa+rerSMxY3KiCYfRDh8v+C5CNZXdvWVXafu3B/o/U4e7eSyReBomw\n" +
            "ybTlwdBrAkEAy5z+o0apRTsuUztAtJvhd0LGsuTyp8TzlI4nFywEHdLzJ47LNnQa\n" +
            "yD9P7VBQO4ZeYb+sKC4Qayf6vMR5q94YowJBALSIkA3S89bqZidUkK6qiA1D30L5\n" +
            "uZ1GN3Xtn13zI5wY3euQ4JXAfW2K4JQjNTuBaY9kO6t+oXbxpGCKCBFzHrkCQGFI\n" +
            "3L2EmIH0mdi4udzRkfOamzeEfpA8YSl8lh7TMBBT50viRSP6a4V8AqNfuUYHmHbZ\n" +
            "ztbP05ZvXrTspzm//0MCQHUcBARpGbzBnMI7j8xAjZJcrpmAhiMKjsix96sJPDjK\n" +
            "ocy7JgQCH1252bIPiaqO3wjCy+F31IClFNNaw3JQRCA=\n";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值