JWT(实践篇)

前言

上篇文章介绍了jwt的组成包括安全方面的理论知识(JWT(理论篇)),这篇文章以java为例实现jwt令牌的颁发和验证接口。

本文不会从手写jwt生成跟,而是使用现有的内库来实现(现在的web应用很少有纯手写的了,都是跟拼积木一样,一个个插件组装起来的)。

引入jwt内库

在jwt官网有对应各种语言的jwt库安装介绍以及支持的签名算法: https://jwt.io

本文使用的是auth0这个库,在pom文件添加依赖:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.1</version>
</dependency>

依赖安装好后接下来编写我们的第一个接口生成token:

颁发token接口

private static String getToken() {
    Algorithm algorithm = Algorithm.HMAC256(secret);  // 定义签名算法
    Map<String,Date> map = JwtToken.calculateExpiredIssues();  // 定义过期时间

    return JWT.create()
           .withClaim("uid", 1) // 存放在payload中的用户id
           .withClaim("nickname", "路过一只大侠") // 存放在payload中的用户昵称
           .withExpiresAt(map.get("expiredTime")) // 设置过期时间
           .withIssuedAt(map.get("now")) // 设置颁发时间
           .sign(algorithm); // 使用上面定义的算法对payload、header签名生成token
}
  1. 第2行定义jwt签名使用的算法这里使用的,参数是你定义的秘钥这个秘钥不能泄漏。

  2. 第6行 withClaim 这个方法用来定义payload中的内容存放业务数据,这个由你自己定义想放什么就放什么。

  3. 第10行 sign 这个方法使用上面定义的算法对payload、header进行签名防止数据被篡改。

  4. 第8行 withExpiresAt 用来定义令牌的过期时间, auth0这个库设置过期时间需要手动去计算一个未来的时间点,什么是未来时间点? 比如颁发令牌的这一刻是 2014-8-13 10:00:00,
    你想要2小时后过期你就需要手动的把两小时加上变成 2014-8-13 12:00:00, 这点相对其它的jwt内库有点笨,其它的库只要传入2小时就会自动帮你计算出过期时间。

    为了计算出过期时间我们在下面定义了 calculateExpiredIssues 方法,就是第二所调用的。

calculateExpiredIssues

// 反回当前时间加上过期时间后的时间
private static Map<String, Date> calculateExpiredIssues() {
    Map<String, Date> map = new HashMap<>();
    Calendar calendar = Calendar.getInstance();
    Date now = calendar.getTime();

    // 当前时间加上120分钟、两小时后过期
    calendar.add(Calendar.MINUTE, 120);
    map.put("now", now);
    map.put("expiredTime", calendar.getTime());
    return map;
}

校验token接口

这个接口返回布尔值,验证过true否则false。

    public static boolean verifyToken(String token) {
        DecodedJWT decodedJWT;
        Algorithm algorithm = Algorithm.HMAC256(secret); // 定义签名算法
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            jwtVerifier.verify(token); // 比对数据签名是否一至
        } catch (Exception e) {
            return false;
        }
        return true;
    }
  1. 第2行 定义了跟颁发token接口一样的签名算法用于对前端传过来的token进行签名,再进行比对。

  2. 第6行 通过auth0库内部的verify方法对签名进行对比,一至说明token没有被改过,这个方法内部还会对过期时间进行判断,只要是签名不一致或是token过期了都会抛出一个异常,我们对这个方法进行try/catch,一旦捕获到异常就反回false表示校验失败,不然就反回true表示校验成功令牌有效。

获取payload中的值

获取payload中的值只需对 verifyToken 方法进行简单修改即可:

    public static Map<String, Claim> getClaims(String token) {
        DecodedJWT decodedJWT;
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            decodedJWT = jwtVerifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new ForbiddenException(10004);
        }
        return decodedJWT.getClaims();
    }

auth0库内部的verify方法如果校验成功会返回一个 DecodedJWT 对象,这个对象下有个 getClaims 方法可以获取到包含payload数据的map对象。

测试

使用的postman进行测试:

getToken

verifyToken

上篇文章:(JWT(理论篇)

完!

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注