zoukankan      html  css  js  c++  java
  • 基于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();
        }
    }

     

  • 相关阅读:
    2017-2018 20155309南皓芯 信息安全系统基础设计第十三周博客
    2017-2018-1 20155309 《信息安全系统设计基础》实验五 通信协议设计
    20155309南皓芯《信息安全系统设计基础》实验四:外设驱动设备设计 实验报告
    2017-2018 20155309 南皓芯 信息安全基础设计第十一周博客
    2017-2018 20155309 南皓芯 信息安全基础设计第十周博客
    2017-2018 20155309南皓芯实验三实时系统
    linux pwd指令C实现
    2017-2018 20155309 南皓芯 信息安全基础设计第九周博客
    2017-2018-1 20155304 《信息安全系统设计基础》第十四周学习总结
    20155304 《信息安全系统设计基础》第十三周学习总结
  • 原文地址:https://www.cnblogs.com/wanghaichao/p/9201061.html
Copyright © 2011-2022 走看看