基于token的身份验证JWT

一、概念:

  JWT:Json Web Token。JWT 是JSON风格轻量级的授权和身份认证规范,可实现无状态、分布式的Web应用授权。是基于token的一种授权认证方式。就是一个字符串,经过加密处理与校验处理的字符串。JWT可以使用HMAC算法或者是RSA的公私秘钥对进行签名。简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。

二、token应用流程为:

1、初次登录:用户初次登录,输入用户名密码。

2、密码验证:服务器从数据库取出用户名和密码进行验证。

3、生成JWT:服务器端验证通过,根据从数据库返回的信息,以及预设规则,生成JWT。

4、返还JWT:服务器的HTTP RESPONSE中将JWT返还。

5、带JWT的请求:以后客户端发起请求,HTTP REQUEST HEADER中的Authorization字段都要有值,为JWT,用来验证用户身份以及对路由,服务和资源的访问权限进行验证。请求验证的url可以例如:http://127.0.0.1:8083/change/goodsMenu? token=JWT

三、JWT的结构

JWT包含了使用.分隔的三部分: Header 头部 Payload 负载 Signature 签名,它的结构是这样的Header.Payload.Signature,调用createJWT()方法生成的一个JWT如下:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdGFmZklkIjoiTFRIWFkxMzAiLCJwcm92aW5jZSI6IjEzIiwiZXhwIjoxNTI5NjM1ODE2LCJuYmYiOjE1Mjk2MzU4MTZ9.Sne5T_Iqx2NQdZIoVpaqHLT2HCjf0AbKEgEhbbNcu_0,是经过加密和编码之后生成的一串字符串,中间以.来分割,共三个部分:

Header:

Header格式为:

{

    "typ": "JWT",

    "alg": "HS256"

}

在header中通常包含了两部分:token类型和采用的加密算法。它就是一个json串,两个字段是必须的,不能多也不能少。alg字段指定了生成JWT的算法,默认值是HS256,生成JWT的算法可以不指定,默认为HS256,对这部分内容使用 Base64Url 编码组成了JWT结构的第一部分。

Payload:

Token的第二部分是负载,也就是需要交换的实际数据,它包含了claim, Claim是一些实体(通常指的用户)的状态和额外的需要传递的元数据。claim set是一个json数据,是表明用户身份的数据,可自行指定字段很灵活,也有固定字段表示特定含义。固定字段(见下图claims截图)有 iss(签发者),exp(过期时间戳), sub(面向的用户), aud(接收方), iat(签发时间),jti(JWT ID,针对当前token的唯一标识),nbf(not before,如果当前时间在nbf里的时间之前,则Token不被接受,一般都会留一些余地,比如几分钟)。将claim set加密后得到负载,经过Base64Url编码后作为JWT结构的第二部分。claims类的如下:

public interface Claims extends Map<String, Object>, ClaimsMutator<Claims> {

    /** JWT {@code Issuer} claims parameter name: <code>"iss"</code> */
    public static final String ISSUER = "iss";

    /** JWT {@code Subject} claims parameter name: <code>"sub"</code> */
    public static final String SUBJECT = "sub";

    /** JWT {@code Audience} claims parameter name: <code>"aud"</code> */
    public static final String AUDIENCE = "aud";

    /** JWT {@code Expiration} claims parameter name: <code>"exp"</code> */
    public static final String EXPIRATION = "exp";

    /** JWT {@code Not Before} claims parameter name: <code>"nbf"</code> */
    public static final String NOT_BEFORE = "nbf";

    /** JWT {@code Issued At} claims parameter name: <code>"iat"</code> */
    public static final String ISSUED_AT = "iat";

    /** JWT {@code JWT ID} claims parameter name: <code>"jti"</code> */
    public static final String ID = "jti";

Signature:

创建签名需要使用编码后的header和payload以及一个秘钥,使用header中指定签名算法进行签名。例如如果希望使用HMAC SHA256算法,那么签名应该使用下列方式创建: HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 签名用于验证消息的发送者以及消息是没有经过篡改的。完整的JWT格式的输出是以 . 分隔的三段Base64编码,JWT在HTTP和HTML环境中更容易传递。签名其实就是一个字符串。作用类似于CRC校验,保证加密没有问题。

常见错误:解析token报,说明token传入的格式不对,不符合JWT的正确格式:

io.jsonwebtoken.MalformedJwtException: JWT strings must contain exactly 2 period characters. Found: 0

io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:235)

io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:481)

io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:541)

JWT工具类代码:

public class JWT {

    /** 
    * @Title: generalKey 
    * @Description: 获取解析JWT和生成JWT的秘钥 
    * @param @param fromSys
    * @param @return 
    */
    public static SecretKey generalKey(String fromSys) {
        String stringKey = new Miner().getMiner("tokenKey").getString(fromSys);
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    public static Claims parseJWT(String jwt, String fromSys) {
        SecretKey key = generalKey(fromSys);
        Claims claims = Jwts.parser().setSigningKey(key).setAllowedClockSkewSeconds(3600 * 24).parseClaimsJws(jwt)
                .getBody();
        return claims;
    }
    
    public static String createJWT(String staffId, String province, long expiresSecond , Claims claims, String fromSys){
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        // 生成签名密钥
        SecretKey key = generalKey(fromSys);
        JwtBuilder builder = Jwts.builder()
            // 设置header, 可以调用setHeaderParams()方法同时设置token类型和加密算法,加密的默认值是HS256
            .setHeaderParam("typ", "JWT")
            // 添加构成JWT的参数, 自定义claim的属性, 也可以调用setId和setSubject等类自身方法
            .claim("staffId", staffId)
            .claim("province", province)
            .signWith(signatureAlgorithm, key);
        // 添加claims信息
        if(claims != null && !claims.isEmpty()) {
            builder.setClaims(claims);
        }
        // 添加Token过期时间
        if (expiresSecond  >= 0) {
            long expMillis = nowMillis + expiresSecond ;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp).setNotBefore(now);
        }
        return builder.compact();
    }
}

 

原文地址:https://www.cnblogs.com/wanghaichao/p/9201061.html