使用JWT做用户登陆token校验

一、什么是JWT

JWT全称JSON Web Token,由三部分组成: header(头)、payload(载体)、signature(签名)。 随着技术的发展,分布式web应用的普及,通过session管理用户登录状态成本越来越高,因此慢慢发展成为token的方式做登录身份校验,然后通过token去取redis中的缓存的用户信息,随着之后jwt的出现,校验方式更加简单便捷化,无需通过redis缓存,而是直接根据token取出保存的用户信息,以及对token可用性校验,单点登录更为简单。

  • header
    JWT第一部分是header,header主要包含两个部分,alg指加密类型,可选值为HS256、RSA等等,typ=JWT为固定值,表示token的类型。

  • Payload
    JWT第二部分是payload,payload是token的详细内容,一般包括iss (发行者), exp (过期时间), sub(用户信息), aud (接收者),以及其他信息,详细介绍请参考官网,也可以包含自定义字段。


{
    "iat": 1493090001,
    "name": "张三"
}
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
  • signature
    JWT第二部分是signature,这部分的内容是这样计算得来的:
1、EncodeString = Base64(header).Base64(payload)
2、最终token = HS256(EncodeString,"秘钥")

签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

二、JSON Web Tokens是如何工作的

无论何时用户想要访问受保护的路由或者资源的时候,用户代理(通常是浏览器)都应该带上JWT,典型的,通常放在Authorization header中,用Bearer schema。

header应该看起来是这样的:

Authorization: Bearer

服务器上的受保护的路由将会检查Authorization header中的JWT是否有效,如果有效,则用户可以访问受保护的资源。如果JWT包含足够多的必需的数据,那么就可以减少对某些操作的数据库查询的需要,尽管可能并不总是如此。

如果token是在授权头(Authorization header)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用cookie。

- 验证过程

  1. 签名验证

当接收方接收到一个JWT的时候,首先要对这个JWT的完整性进行验证,这个就是签名认证。它验证的方法其实很简单,只要把header做base64url解码,就能知道JWT用的什么算法做的签名,然后用这个算法,再次用同样的逻辑对header和payload做一次签名,并比较这个签名是否与JWT本身包含的第三个部分的串是否完全相同,只要不同,就可以认为这个JWT是一个被篡改过的串,自然就属于验证失败了。接收方生成签名的时候必须使用跟JWT发送方相同的密钥,意味着要做好密钥的安全传递或共享

  1. 载体验证

iss(Issuser):如果签发的时候这个claim的值是“a.com”,验证的时候如果这个claim的值不是“a.com”就属于验证失败

sub(Subject):如果签发的时候这个claim的值是“liuyunzhuge”,验证的时候如果这个claim的值不是“liuyunzhuge”就属于验证失败

aud(Audience):如果签发的时候这个claim的值是“['b.com','c.com']”,验证的时候这个claim的值至少要包含b.com,c.com的其中一个才能验证通过

exp(Expiration time):如果验证的时候超过了这个claim指定的时间,就属于验证失败;nbf(Not Before):如果验证的时候小于这个claim指定的时间,就属于验证失败

iat(Issued at):它可以用来做一些maxAge之类的验证,假如验证时间与这个claim指定的时间相差的时间大于通过maxAge指定的一个值,就属于验证失败

jti(JWT ID):如果签发的时候这个claim的值是“1”,验证的时候如果这个claim的值不是“1”就属于验证失败

注意:在验证一个JWT的时候,签名认证是每个实现库都会自动做的,但是payload的认证是由使用者来决定的。因为JWT里面可能不会包含任何一个标准的claim,所以它不会自动去验证这些claim。

以登录认证来说,在签发JWT的时候,完全可以只用sub跟exp两个claim,用sub存储用户的id,用exp存储它本次登录之后的过期时间,然后在验证的时候仅验证exp这个claim,以实现会话的有效期管理。

三、实战DEMO

  • 验证流程

1.用户携带用户名和密码请求访问
2.服务器校验用户凭据
3.应用提供一个token给客户端
4.客户端存储token,并且在随后的每一次请求中都带着它
5.服务器校验token并返回数据

  • 注意
  1. 每一次请求都需要token

  2. Token应该放在请求header中

  3. 我们还需要将服务器设置为接受来自所有域的请求,用Access-Control-Allow-Origin: *

1、引入相关pom

        
            io.jsonwebtoken
            jjwt-api
            0.10.5
        
        
            io.jsonwebtoken
            jjwt-impl
            0.10.5
            runtime
        
        
            io.jsonwebtoken
            jjwt-jackson
            0.10.5
            runtime
        
        
            com.auth0
            java-jwt
            3.2.0
        

2、编写JWT生成解析工具类


public class JWTUtils {
    public static String createJWT(String id,String subject,long ttlMillis,SecretKey key){
        //获取签名算法
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject)
                .setIssuer("user")
                .setIssuedAt(now)
                .signWith(key,signatureAlgorithm);
        if(ttlMillis >= 0){
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            //设置过期时间
            builder.setExpiration(expDate);
        }
        return builder.compact();

    }


    public static Claims parseJWT(String jwt,SecretKey keySpec){
        return Jwts.parser()
                .setSigningKey(keySpec)
                .parseClaimsJws(jwt)
                .getBody();
    }

    public static ResultCode validateJWT(String jwt){
        ResultCode checkResult = new ResultCode();
        Claims claims = null;
        try {
            claims = parseJWT(jwt);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) {
            checkResult.setErrCode("解析失败");
            checkResult.setSuccess(false);
        }
        return checkResult;
    }
    public static void main(String[] args) {
        //生成密匙
        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        String jwt = JWTUtils.createJWT("1","test",10000,key);
        System.out.println(jwt);
        System.out.println(JWTUtils.parseJWT(jwt,key));
    }
}

3、控制器层代码

  • 登陆逻辑
    @RequestMapping("/login")
    public String login(用户信息){
        //在这里进行用户身份校验
        ......
        ......
        //校验身份成功生成token返回
        String jwt = JWTUtils.createJWT(用户信息);
        return jwt;
    }
  • 所有需要用户身份验证的接口逻辑

    //这里只大致写一些,具体验证失败原因以及返回格式问题可以根据自身细调

    public String toIndex(HttpServletRequest request){
        //获取token
        String authorization = request.getHeader("authorization");
        if(authorization == null){
            //返回登陆
            return "需要登陆";
        }else{
            //进行校验
            try{
                Claims claims = JWTUtils.parseJWT(authorization);
                return "验证成gong";
            }catch (Exception e){
                e.printStackTrace();
                return "验证失败";
            }
        }

    }

3、客户端代码

  • 输入用户信息,申请登陆

    $.ajax({
        url:'http://localhost:8080/demo/login',
        type:'GET',
        data:userLoginData,
        success:function(res) {
            localStorage.setItem("token",res)
        },
        error:function (res) {
            alert(res)

        }

    })
  • 当请求需要身份验证接口时
var token = localStorage.getItem("token");

$.ajax({
        url:'http://localhost:8080/demo/toIndex',
        type:'GET',
        header:{
            'Authorization':token
        },
        beforeSend : function(request) {
            request.setRequestHeader("Authorization",token );
        },
        success:function(res) {
          
        },
    })

四、参考连接

https://github.com/jwtk/jjwt#install-jdk-maven
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
https://www.jianshu.com/p/f25d62305c70
https://www.cnblogs.com/cjsblog/p/9277677.html
https://www.jianshu.com/p/fe67b4bb6f2c

你可能感兴趣的:(使用JWT做用户登陆token校验)