Java中使用JWT生成token

本章目录

  • 前言
  • jwt的使用

1. 前言

一个JWT由三部分组成:

  • Header(头部) —— base64编码的Json字符串
  • Payload(载荷) —— base64编码的Json字符串
  • Signature(签名)—— 使用指定算法,通过Header和Payload计算的字符串

各部分以" . "分割,下面就是一个完整的token:

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI2NjI2Y2FmNS01YTJkLTQxYjctYTE4Mi02ZjEwODNhMWY2YzAiLCJpc3MiOiJzeHRfand0X3Rlc3QiLCJzdWIiOiJ7XCJuYW1lXCI6XCJhZG1pbjFcIixcInBhc3N3b3JkXCI6XCJwYXNzd29yZDFcIn0iLCJpYXQiOjE1ODQ5NDc2OTUsImV4cCI6MTU4NDk0Nzc1NX0.u5wiBjWyptTNxgbqRurUJAtDyD4oMs2SZU7frZN-S1k


其中payload中存储的是claims,存放的用于传递的非敏感信息,可自由获取该内容,一般存储用户名等信息。

2. jwt的使用


整体的测试代码目录结构如下:

image

首先pom.xml中引入下面依赖:

<!-- 引入 thymeleaf 模板依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
		
<!-- JWT核心依赖 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.3.0</version>
</dependency>

<!-- java开发JWT的依赖jar包 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>
		
<!-- lombok依赖 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
</dependency>

然后直接上代码:


JWTUsers

/**
 * 模拟用户数据
 *
 * Created by ASUS on 2020/3/19.
 */
public class JWTUsers {

    /**
     * 存储用户的集合
     */
    private static final Map<String ,String> users = new HashMap<>(16);

    /**
     * 初始化10个用户账号
     */
    static {
        for (int i = 0; i < 10; i++) {
            users.put("admin" + i, "password" + i);
        }
    }

    /**
     * 判断用户是否登录
     * @return
     */
    public static boolean isLogin(String name, String password) {
        if (null == name || name.trim().length() == 0) {
            return false;
        }
        String pwd = users.get(name);
        if (null == pwd || !pwd.equals(password)) {
            return false;
        }

        return true;
    }
}


JWTSubject

/**
 * 作为Subject使用,也就是payload中保存的public claims
 * 其中不包含任何敏感数据
 * 开发中建议使用实体类型
 *
 * Created by ASUS on 2020/3/19.
 */
@Data
public class JWTSubject {

    private String name;

    private String password;
}


JWTResult

/**
 * 结果对象
 *
 * Created by ASUS on 2020/3/19.
 */
@Data
public class JWTResult {

    /**
     * 返回码
     */
    private int code;

    /**
     * 是否成功,代表结果的状态
     */
    private boolean success;

    /**
     * 验证过程中payload中的数据
     */
    private Claims claims;
}


JWTResponseData

/**
 * 发送给客户端的数据对象
 *
 * Created by ASUS on 2020/3/19.
 */
@Data
public class JWTResponseData {

    private Integer code;   // 返回码

    private Object data;    // 业务数据

    private String msg;     // 返回描述

    private String token;   // 身份标识
}


JWTUtils

/**
 * JWT工具类
 *
 * Created by ASUS on 2020/3/19.
 */
public class JWTUtils {

    //  服务器的key,用于做加解密的key数据
    private static final String JWT_SECERT = "jwt_secert_test";
    //  java对象和json的双向转换
    private static final ObjectMapper MAPPER = new ObjectMapper();
    //  token过期
    public static final int JWT_ERRCODE_EXPIRE = 1005;
    //  验证不通过
    public static final int JWT_ERRCODE_FAIL = 1006;

    /**
     * 创建生成token所需的密钥
     * @return
     */
    public static SecretKey generalKey() {
        try {
            //byte[] encodedKey = Base64.decode(JWT_SECERT);
            byte[] encodedKey = JWT_SECERT.getBytes("UTF-8");
            SecretKeySpec key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
            return key;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 签发JWT,生成token。
     *
     * @param id            jwt的唯一身份标识,主要用作一次性token,从而回避重放攻击
     * @param iss           jwt签发者
     * @param subject      jwt所面向的用户,payload中记录的public claims
     * @param ttlMillis    有效期,单位毫秒
     * @return              token
     */
    public static String createToken(String id, String iss, String subject, long ttlMillis) {
        //  加密算法
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        //  创建密钥
        SecretKey secretKey = generalKey();
        //  创建JWT的构建器,就是使用指定的信息和加密算法,生成token的工具。
        JwtBuilder builder = Jwts.builder()
                .setId(id)      //  设置身份标识,就是一个客户端的唯一标记,可以是用户主键、服务器生成的随机数等等。
                .setIssuer(iss)
                .setSubject(subject)
                .setIssuedAt(now)
                .signWith(signatureAlgorithm, secretKey);   //  设定密钥和算法

        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);    //   token过期时间
            builder.setExpiration(expDate);
        }

        return builder.compact();   //  生成token
    }

    // 验证token
    public static JWTResult validateJWT(String jwtStr) {
        JWTResult checkResult = new JWTResult();
        Claims claims = null;

        try {
            claims = parseJWT(jwtStr);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) {
            checkResult.setCode(JWT_ERRCODE_EXPIRE);
            checkResult.setSuccess(false);
        } catch (SignatureException e) {
            checkResult.setCode(JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        } catch (Exception e) {
            checkResult.setCode(JWT_ERRCODE_FAIL);
            checkResult.setSuccess(false);
        }

        return checkResult;
    }

    /**
     * 解析JWT字符串
     *
     * @param jwt  就是token
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();

        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();     //getBody()获取的就是token中记录的payload数据,就是payload中保存的所有claims,就是JWT的构建器中set的所有数据
    }

    /**
     * 生成subject信息(json格式的数据)-这里使用的是jackson,使用fastJson也都可以
     * @param subObj    要转换的对象,其实就是转换JWTSubject类对象
     * @return          java对象->json字符串出错时返回null
     */
    public static String generalSubject(Object subObj) {

        try {
            return MAPPER.writeValueAsString(subObj);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return null;
        }
    }

}


JWTController

@RestController
public class JWTController {

    @RequestMapping("/login")
    public Object login(String name, String password) {
        System.out.println(name + " : " + password);
        JWTResponseData responseData = null;
        //  认证用户信息
        if (JWTUsers.isLogin(name, password)) {
            JWTSubject subject = new JWTSubject();
            subject.setName(name);
            subject.setPassword(password);

            String token = JWTUtils.createToken(UUID.randomUUID().toString(), "sxt_jwt_test", JWTUtils.generalSubject(subject), 1*60*1000);
            responseData = new JWTResponseData();
            responseData.setCode(200);
            responseData.setData(null);
            responseData.setMsg("登录成功");
            responseData.setToken(token);
            System.out.println("登录成功");
        } else {
            responseData = new JWTResponseData();
            responseData.setCode(500);
            responseData.setData(null);
            responseData.setMsg("登录失败");
            responseData.setToken(null);
            System.out.println("登录失败");
        }

        return responseData;
    }

    @RequestMapping("/testAll")
    public Object testAll(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        JWTResult result = JWTUtils.validateJWT(token);

        JWTResponseData responseData = new JWTResponseData();
        if (result.isSuccess()) {
            responseData.setCode(200);
            responseData.setData(result.getClaims().getSubject());
            String newToken = JWTUtils.createToken(result.getClaims().getId(), result.getClaims().getIssuer(), result.getClaims().getSubject(), 1*60*1000);
            responseData.setToken(newToken);

            return responseData;
        } else {
            responseData.setCode(500);
            responseData.setMsg("用户未登录");
            System.out.println("用户未登录");

            return responseData;
        }

    }
}


index.html

<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head lang="en">
    <meta charset="UTF-8" />
    <title>Thymeleaf</title>
    <script type="text/javascript" src="../../static/js/jquery.min.js"></script>

    <script type="text/javascript">
        function login() {
            var name = $("#name").val();
            var password = $("#password").val();
            var params = "name=" + name + "&password=" + password;

            $.ajax({
                'url': "http://localhost:8080/login",
                'data': params,
                'success': function (data) {
                    if (data.code == 200) {
                        var token = data.token;
                        var localStorage = window.localStorage; //  浏览器提供的存储空间
                        localStorage.token = token;
                        alert(data.msg);
                    } else {
                        alert(data.msg);
                    }
                }
            })
        }
        
        function setHeader(xhr) {
            xhr.setRequestHeader("Authorization", window.localStorage.token);
        }
        
        function testLocalStorage() {
            $.ajax({
                'url': "http://localhost:8080/testAll",
                'success': function (data) {
                    if (data.code == 200) {
                        window.localStorage.token = data.token;
                        alert(data.data);
                    } else {
                        alert(data.msg);
                    }
                },
                'beforeSend': setHeader
            })
        }
        
    </script>
</head>
<body>
Thymeleaf模板引擎
<h3 th:text="${name}">hello world~~~~~~~</h3>

<center>
    <table>
        <caption>登录测试</caption>
        <tr>
            <td>登录名:</td>
            <td><input type="text" name="name" id="name"/></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="text" name="password" id="password"/></td>
        </tr>
        <tr>
            <td><input type="button" value="登录" onclick="login()"/></td>
        </tr>
    </table>
</center>
<input type="button" value="浏览器本地存储" onclick="testLocalStorage()"/>
</body>
</html>

测试结果


点击“登录”按钮

image


点击“浏览器本地存储”按钮

image


过1分钟再点击“浏览器本地存储”按钮(因为token有效期设置的1分钟)

image

# JWT 

评论

稻城 : 111
兜里的猫 : 是md
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×