JWT简介

作者: 明翼 | 来源:发表于2020-02-23 21:04 被阅读0次

    一 JWT是什么

    JWT是JSON Web Token的简称,是目前流行的跨域的认证解决方案,作为JSON对象在各方传递信息的凭证,它是由服务器端签发的,而且是带签名的,在不知道服务器端私钥的情况下,无法伪造的。服务器端通过数字签名来保证数据的完整性和有效性。

    1.1 为什么需要

    为什么需要这种token认证那,从http这个无状态的协议说起。早期http是短连接的,也就是一个报文一个tcp请求,得到返回的内容后就关闭了。(现在就算支持keep-alive的情况,如果用一个tcp代表一个用户的情况下,如果用户稍有断网,要重新登陆体验笔记差)这就造成一个问题,如果在网站上,比如购物网站上跟踪每个用户信息,就需要区分不同用户的请求内容,早期用session来实现,交互过程如下:

    1. 用户在浏览器登陆,发送username和password给服务器。
    2. 服务器收到后进行验证,验证成功后,将用户信息,比如用户对象保存到session中,且发送一个叫JSESSIONID的cookie给用户浏览器。
    3. 用户下次请求会带上这个JSESSIONID,服务器以此来识别不同的用户。

    这种session认证缺点:

    • session 存在服务器上,意味着用户下次请求还必须请求在这台服务器上,才能拿到授权的资源,限制了负载均衡能力。如果多个系统认证无法实现单点登陆功能。

    • session 保存在服务器上,用户越来越多后,在服务器占的内存也会越来越多,甚至有内存溢出的风险。

    1.2 JWT

    JWT是一个字符串,用于鉴权和认证。包含头信息,有效荷载,签名三部分组成,中间用.号分隔,如下格式:

    header.payload.signature
    
    • header
    header = Base64URL({"alg":"HS256","typ":"JWT"))
    

    说明:

    声明类型alg:表示令牌的类型,JWT令牌统一写JWT。
    签名密玥算法alg:HMAC256,RSA等。
    Base64URL:header头需要进行Base64URL处理,Base64URL使用"-"和"_"替换:"+"和"/",不使用"=" ,使用这个原因是因为URL中含有这些字符。

    • payload
      载荷信息,就是json格式封装的一些key和value,标准定义七个:
    {
        "iss": "John Wu JWT",
        "iat": 1441593502,
        "exp": 1441594722,
        "aud": "www.example.com",
        "sub": "jrocket@example.com",
        "from_user": "B",
        "target_user": "A"
    }
    

    iss:JWT签发者
    exp:到期时间UNIX时间戳
    sub:JWT对应的用户
    aud:用户
    nbf:在此之前不可用
    iat:发布时间
    jti:JWT ID用于标识该JWT,防止回放攻击。

    payload中一般保存非敏感信息,同样用Base64URL处理,如果需要保存敏感信息,需要对整个token进行加密。

    • signature
      签名,是通过上面选择的算法对上述两部分进行签名:
    HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret
    )
    

    secret 为密钥,签名这里面并不是为了保密,而是为了验证此token是本服务器签发的,而且还可以防止篡改,保证数据的完整性。
    官网示意图如下:


    encode和decode示意图

    二 如何使用JWT

    2.1 JWT的处理流程:

    1. 初次登陆,输入用户和密码请求发到服务器。
    2. 服务器取出数据库中的用户名和密码进行验证。
    3. 验证通过后,生成token,返回给客户端。
    4. 以后客户端所有请求都会在请求头中:
      Authentication字段都要有值,为JWT。
    5. 服务器验证此token是否合法。
    6. 将用户访问的信息返回给客户端


      JWT请求流程

    注意,如果使用token,涉及到跨域的话,需要设置:

    Access-Control-Allow-Origin: *
    

    所谓的跨域,即一个请求访问不是同一个域名或端口,这些就属于跨域,为安全期间,一般默认不准许跨域的。
    java使用JWT例子:
    pom文件引入相关包:

       <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>
    

    例子:

    public class JWTUtil {
    
        private static final long EXPIRE_TIME = SpringContextUtil.getBean(FebsProperties.class).getShiro().getJwtTimeOut() * 1000;
    
        /**
         * 校验 token是否正确
         *
         * @param token  密钥
         * @param secret 用户的密码
         * @return 是否正确
         */
        public static boolean verify(String token, String username, String secret) {
            try {
                Algorithm algorithm = Algorithm.HMAC256(secret);
                JWTVerifier verifier = JWT.require(algorithm)
                        .withClaim("username", username)
                        .build();
                verifier.verify(token);
                return true;
            } catch (Exception e) {
                log.info("token is invalid{}", e.getMessage());
                return false;
            }
        }
    
        /**
         * 从 token中获取用户名
         *
         * @return token中包含的用户名
         */
        public static String getUsername(String token) {
            try {
                DecodedJWT jwt = JWT.decode(token);
                return jwt.getClaim("username").asString();
            } catch (JWTDecodeException e) {
                log.error("error:{}", e.getMessage());
                return null;
            }
        }
    
        /**
         * 生成 token
         *
         * @param username 用户名
         * @param secret   用户的密码
         * @return token
         */
        public static String sign(String username, String secret) {
            try {
                username = StringUtils.lowerCase(username);
                Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
                Algorithm algorithm = Algorithm.HMAC256(secret);
                return JWT.create()
                        .withClaim("username", username)
                        .withExpiresAt(date)
                        .sign(algorithm);
            } catch (Exception e) {
                log.error("error:{}", e);
                return null;
            }
        }
    }
    
    

    三 JWT有什么缺点

    1. 默认不加密,如果传私密信息不够安全。
    2. 如果采用cookie保存仍然可以被伪造,建议放在localstorage中。
    3. 如果token信息过长,会影响性能,因为每个请求都需要带token信息。
    4. token颁发后,不能通过服务器端让令牌失效,只能等过期,当然这个可以通过保存在redis中,超时日期设置过token过期时间,验证时候先从redis中获取,如果没有直接判断非法,不通过普通的token验证流程验证。

    相关文章

      网友评论

          本文标题:JWT简介

          本文链接:https://www.haomeiwen.com/subject/mwijqhtx.html