SpringCloud系列:如何使用JWT进行身份验证

370 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第28天,点击查看活动详情

作者平台:

| CSDN:blog.csdn.net/qq_41153943…

| 掘金:juejin.cn/user/651387…

| 知乎:www.zhihu.com/people/1024…

| GitHub:github.com/JiangXia-10…

| 微信公众号:1024笔记

本文一共2144字,预计阅读9分钟

前言

在很多的登录注册的业务场景下都需要对用户登录身份进行校验,传统的用户身份验证是将服务无法与用户身份验证分开。通常过程如下:

  1. 首先用户向服务器发送用户名和密码,等到验证服务器后,相关数据(如用户角色,登录时间等)将保存在当前会话中。然后服务器会向用户返回session_id,session信息都会写入到用户的Cookie,这样用户的每个后续请求都将通过在Cookie中取出session_id传给服务器,服务器收到session_id之后会对比之前保存的数据,从而确认用户的身份。但是这种策略存在一个最大的问题就是结构单一,没有采用分布式,所以无法进行横向的拓展,那么一旦系统比较大各个系统有关联,就无法使用。

  2. 所以目前一种比较灵活的解决方案就是采用自包含令牌,客户端保存数据,但服务器不保存会话数据。 今天要说的JWT是这种解决方案之一。

什么是JWT

JWT的全程是JSON Web Token,是一种加密的token,JWT也是目前流行的跨域认证解决方案,是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

JWT的规则

JWT的规则是在服务器在进行了身份验证之后,会生成一个JSON对象并将其发送回客户端,当该客户端与服务器进行通信时,客户端在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。并且为了防止用户篡改数据,服务器将在生成对象时添加签名。该过程中服务器并不会保存任何的会话数据,即服务器变为无状态,从而使其更容易扩展。生成的json对象如下所示:

{
  "sub": "54457851524",
  "name": "Jiangxia",
  "admin": true
}

JWT的组成部分

JWT对象是由头部信息、载荷、签名哈希三段信息构成的,并且将该三段信息文本采用"."进行连接在一起,就构成了一个JWT字符串对象。在jtw加密解密网站:jwt.io/,有对于jwt进行加密…

JWT头部信息是一个描述JWT元数据的JSON对象,通常如下所示。

{
  "alg": "HS256",
  "typ": "JWT"
}

在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示的是令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

载荷又叫有效载荷,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。JWT指定七个默认字段供选择。

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

并且除以上默认字段外,我们还可以自定义私有字段,如下例:

{
  "sub": "696565623565",
  "name": "jiangxia",
  "admin": true
}

需要注意的是,JWT对象默认是不进行加密的,所以在默认的不加密的情况下,应该不要写入敏感数据 ,但是也可以在生成原始的Token之后,使用密钥再进行一次加密。JSON对象也使用Base64 URL算法转换为字符串保存。

签名哈希部分则是对上面两部分数据的签名,通过指定的算法生成哈希,以确保数据不会被篡改。首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)

在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

JWT在项目中的使用

首先需要在项目的pom文件中添加jwt相关的依赖:

 <!-- JWT依赖-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
</dependency>

然后需要创建一个jwt工具:JwtUtils首先需要设置有效期以及加密的密钥:

public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";

然后创建一个生成jwttoken的方法:

 public static String getJwtToken(String id, String nickname){
        String JwtToken = Jwts.builder()
                .setHeaderParam("typ""JWT")
                .setHeaderParam("alg""HS256")
                .setSubject("user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .claim("id", id)
                .claim("nickname", nickname)
                .signWith(SignatureAlgorithm.HS256APP_SECRET)
                .compact();
        return JwtToken;
    }

然后创建一个方法判断某个token是否存在:

    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

在创建一个根据jwttoken获取会员id的方法:

 public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
         return (String)claims.get("id");
    }

总结

JWT是目前较为主流的一种灵活的身份验证解决方法。其使用是客户端接收服务器返回的JWT,并将其存储在Cookie或localStorage中。此后,该客户端将在与服务器交互中都会携带该JWT信息。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

相关推荐