一步步教你实现JWT认证和授权



前言

本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!
也许一个人独行,可以走的很快,但是一群人结伴而行,才能走的更远!

一、引入

我们上篇文章中为大家介绍了token(令牌)认证的认证机制,而在本文,我们要为大家介绍JWT认证(JSON Web Token authentication)。

二、Token认证与JWT认证的关系

大家现在是否感觉压力山大,不仅学习了Cookie认证,Session认证,还学习了一个Token认证,现在又冒出来了个JWT认证,别担心,这个JWT认证并不是什么新颖的认证方式,本质上它就是一种基于Token的身份验证机制,Token认证是一种通用的身份验证方法,我们可以使用不同类型的Token去进行身份验证,而JWT就是其中的一种Token类型。

在这里我们要区分好,上篇文章为大家介绍的Token认证机制是一个广泛的概念,是一种概称,它涵盖了使用不同类型的Token进行身份验证的方法。除了JWT之外,还有其他类型的Token,例如基于时间的令牌(Time-based Tokens)和访问令牌(Access Tokens)。这些Token可以使用不同的格式和验证机制,但它们都是用于验证用户身份并授权访问受保护资源的凭据。

所以搞清了关系之后,接下来进入正文!

三、什么是JWT认证?

JWT认证(JSON Web Token authentication)是一种基于Token的身份验证机制。它使用JSON Web Token(JWT)作为身份验证的凭据,并通过对JWT进行验证来确认用户的身份和授权用户访问受保护的资源。

四、JWT的组成

上文已经提及,JWT只是众多token类型里的其中一种类型,所以关于它的组成,就是我们要学习的重点。

JWT是一种开放标准(RFC 7519),定义了一种紧凑且自包含的方式来表示和传输信息。它由三个部分组成:

  1. 头部(Header):包含描述JWT的元数据和算法信息,例如使用的签名算法。
  2. 载荷(Payload):包含JWT所声明的数据,可以包含用户身份信息、授权信息和其他自定义数据。
  3. 签名(Signature):使用密钥和指定的签名算法对头部和载荷进行签名,以实现数据完整性和验证。

在这里插入图片描述

1、头部(Header)

头部通常包含两个部分:令牌类型(typ)和签名算法(alg)。这些信息用于描述JWT的类型和使用的加密算法。例如,一个典型的头部可以是以下JSON格式:

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

在上面的例子中,"alg"表示签名算法为HMAC SHA-256(HS256),"typ"表示令牌类型为JWT。

2、载荷(Payload)

载荷是JWT的主体部分,包含要传输的数据。载荷可以包含一些预定义的声明(Claims),也可以包含自定义的声明。预定义的声明分为三类:

  • 注册声明(Registered Claims):这些声明是一组预定义的标准声明,包括iss(签发者)、sub(主题)、aud(受众)、exp(过期时间)、nbf(生效时间)和iat(发布时间)等。
  • 公共声明(Public Claims):这些声明是自定义的声明,供使用者自由定义,但建议遵循一定的命名规范,避免冲突。
    私有声明(Private Claims):这些声明也是自定义的声明,但为了避免冲突,建议将其命名为命名空间形式,例如"company_name"。
    以下是一个示例载荷的JSON格式:
{
  "sub": "user123",
  "name": "John Doe",
  "iat": 1629012345
}

在上面的例子中,"sub"表示主题为"user123"的用户,“name"表示用户姓名为"John Doe”,"iat"表示JWT的发布时间为1629012345(UNIX时间戳)。

3、签名(Signature)

签名是JWT的第三部分,用于验证JWT的完整性和真实性。签名通常使用头部和载荷中的数据以及一个密钥来生成。生成签名的过程是对头部和载荷进行编码,然后使用指定的签名算法(如HMAC、RSA等)进行加密。最终生成的签名字符串将附加在JWT的末尾。

签名的示例:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secretKey
)

在上面的例子中,"header"表示头部的Base64编码字符串,"payload"表示载荷的Base64编码字符串,"secretKey"表示用于生成签名的密钥。

通过将这三个部分使用句点(.)连接起来,即可形成一个完整的JWT:

base64UrlEncode(header) + "." +
base64UrlEncode(payload) + "." +
signature

最终的JWT可以作为身份验证凭据在客户端和服务器之间进行传输,并通过验证签名来验证JWT的真实性和完整性。

当然很多人会疑惑这个token的安全性,JWT是由三部分组成的,以点分开header、payload和signature

  1. header部分声明需要用什么算法来生成签名

  2. payload部分是一些特定的数据,比如有效期之类

  3. 接着header和payload两部分的内容会经由BASE64编码,注意是编码,不是加密,也就是很容易可以解码,虽然JWT不保存在服务器这里,但是服务器需要保存一段密码,这段密码要结合两段编码进行算法运算,最终得到签名信息。这里使用的算法就是刚刚header声明的算法。签名的信息,也就是signature部分了,这样一个完整的最大的JWT就可以发送给客户端了。如果我们修改三个部分,其中一个字符,整个最大JWT都会出错,三个部分是相关联的,因此JWT有一定的安全性。

引用但老师
token是服务器加密的用户信息,服务器将token发给浏览器,浏览器用cookie或storage保存cookie。然后浏览器每次发送请求就带上token,服务器将其解密并确认用户登录。

五、JWT认证的工作流程

JWT认证的工作流程一般涉及以下步骤:

  1. 用户身份验证:
    用户在进行身份验证时,通常会提供标识自己身份的凭据(例如用户名和密码)。服务器需要验证这些凭据的有效性,通常是通过与存储在数据库中的用户凭据进行比对。

  2. JWT的生成:
    如果用户通过身份验证,服务器将生成一个JWT作为身份验证的凭据。生成JWT的过程包括以下步骤:

    • 创建一个包含头部和载荷的JSON对象。
    • 对头部和载荷进行Base64编码,生成JWT的第一部分。
    • 使用服务器上的密钥和指定的签名算法对编码后的头部和载荷进行签名,生成签名。
    • 将签名与编码后的头部和载荷连接起来,生成完整的JWT。
  3. JWT的发送和存储:
    服务器将生成的JWT发送给客户端,通常是通过将JWT作为响应的一部分(例如在HTTP响应的头部或作为响应的一部分)返回给客户端。客户端通常会将JWT存储在本地,例如在浏览器的本地存储或内存中。

  4. 后续请求的身份验证:
    在后续的请求中,客户端将JWT作为身份验证凭据发送给服务器。通常是通过将JWT放置在请求的头部(例如Authorization头部)中或作为请求参数的一部分(例如查询字符串或表单数据)发送给服务器。

  5. JWT的验证和解析:
    服务器在接收到请求后,需要验证JWT的有效性和完整性,以及解析其中的信息。验证和解析的过程包括以下步骤:

    • 从请求中获取JWT。
    • 检查JWT的签名是否有效,以确保JWT未被篡改。
    • 解码JWT的头部和载荷,获取其中的信息。
  6. 用户授权和访问受保护资源:
    服务器通过验证JWT的有效性,并获取到其中的用户身份信息,确认用户的身份。基于用户的身份信息,服务器可以进行授权判断,决定用户是否有权访问请求的资源。如果用户被授权访问资源,服务器将返回相应的数据或执行相应的操作。

整个JWT认证的工作流程是基于无状态的,服务器无需在后端存储会话状态,每个请求都是独立的。JWT作为身份验证凭据,提供了一种简单、安全和可扩展的方式来验证用户身份,并授权用户访问受保护的资源。

在这里插入图片描述

六、代码举例

以下是一个使用Java JWT库(jjwt)的示例代码:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

public class JWTExample {
     
     
    private static final String SECRET_KEY = "yourSecretKey";
    private static final long EXPIRATION_TIME = 86400000; // 24小时

    public static void main(String[] args) {
     
     
        // 创建JWT
        String token = createJWT("user123");

        // 验证和解析JWT
        if (validateJWT(token)) {
     
     
            String username = parseJWT(token);
            System.out.println("解析到的用户名:" + username);
        } else {
     
     
            System.out.println("JWT验证失败");
        }
    }

    public static String createJWT(String username) {
     
     
        Date now = new Date();
        Date expiration = new Date(now.getTime() + EXPIRATION_TIME);

        String token = Jwts.builder()
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expiration)
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();

        return token;
    }

    public static boolean validateJWT(String token) {
     
     
        try {
     
     
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
     
     
            return false;
        }
    }

    public static String parseJWT(String token) {
     
     
        Jws<Claims> claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
        return claims.getBody().getSubject();
    }
}

在上述示例中,createJWT方法用于创建JWT,其中设置了用户名、签发时间、过期时间,并使用HS256算法进行签名。validateJWT方法用于验证JWT的合法性,通过解析和校验JWT的签名来进行判断。parseJWT方法用于解析JWT并提取其中的用户名信息。

请注意,上述代码中的SECRET_KEY是用于签名和验证JWT的密钥,请确保将其替换为实际的密钥,并妥善保管。另外,示例中设置的过期时间为24小时(86400000毫秒),我们可以根据自己的需求进行调整。

使用这个示例代码,可以创建一个包含用户名信息的JWT,并验证和解析JWT以获取其中的用户名。

七、总结

大家不要把三者想的太复杂

  1. session是诞生并保存在服务器那边的,由服务器主导一切。
  2. cookie这是一种数据载体,把session放在cookie中送到客户端那边,cookie跟随HTTP的每个请求发送出去。
  3. token是诞生在服务器,但保存在浏览器这边的,由客户端主导一切,可以放在cookie或者storage里面,持有token,就像持有令牌一样,可以允许访问服务器。
  4. 服务器验证是前提。cookie保存在客户端,服务器不加密不保存;session保存在客户端,服务器要加密并保存;token保存在客户端,服务器要加密不保存。
  5. Session是一种服务器端的机制,用于存储和管理用户的身份和状态信息,而Cookie是用于在客户端存储Session ID等数据的机制。Token是一种轻量级的身份验证和授权机制,可以作为无状态的凭据进行传输。Cookie和Token都可以用于在客户端和服务器之间传递身份信息和状态信息,实现身份验证和状态管理的功能。

猜你喜欢

转载自blog.csdn.net/weixin_52533007/article/details/132110578