为了账号安全,请及时绑定邮箱和手机立即绑定

JWT学习:新手入门教程

标签:
安全 API
概述

本文介绍了JWT的基本概念、工作原理、优势与应用场景,以及如何生成、验证和解析JWT。同时,文章还详细讲解了JWT在用户认证和API保护中的实际应用案例。

JWT简介
在现代互联网应用中,为了实现安全且高效的用户认证和数据传输,开发人员经常使用JSON Web Token (JWT)。JWT是一种开放标准,用来在网络应用环境间安全地将信息作为JSON对象传输。它由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature)。JWT通常用于登录认证、权限控制以及状态保持。

JWT的工作原理

  1. 生成JWT:客户端向服务器发送请求,要求生成JWT。服务器收到请求后,会根据请求中的数据生成一个JWT。
  2. 包含信息:JWT包含了一些声明,这些声明是JSON对象的键值对形式。声明可以分为三类:注册声明、公共声明、私有声明。
  3. 加密签名:在头部中指定了加密算法,利用该算法对载荷进行签名,生成签名部分。签名部分用于保证JWT的完整性和真实性。
  4. 返回客户端:服务器生成JWT后,将其返回给客户端。
  5. 验证JWT:客户端在每次请求时都会携带JWT,服务器收到请求后会验证JWT的有效性。
  6. 解析JWT:服务器解析JWT的内容,提取有用信息,从而确定客户端的认证状态和权限。

JWT的优势与应用场景

JWT的优势在于它是一种无状态的协议,这意味着服务器无须保存会话信息,从而减轻了服务器的负担。JWT通常用于以下场景:

  • 用户身份验证:JWT可以替代传统的会话Cookie,实现用户登录、注销和权限控制。
  • 授权:JWT可以帮助服务器根据令牌中的信息,决定客户端是否有权限访问特定资源。
  • 单点登录:JWT可以实现跨多个应用或服务的单点登录,用户只需一次登录即可访问多个应用。
  • API保护:JWT可以为API提供安全的认证机制,防止未授权的访问。

JWT的基本结构

JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这些部分通过点号(.)进行分隔。JWT的结构如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxhPnoEoB/sJecMlF23cJH9GF+Vr01eJj8u0A0p0

头部(Header)

头部通常包含两个部分:typ(令牌类型)和alg(加密算法)。

  • typ:指定了令牌的类型,通常为JWT
  • alg:指定了加密算法,常见的算法包括HS256(HMAC SHA-256)、RS256(RSA SHA-256)等。

头部示例:

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

载荷(Payload)

载荷包含了JWT的核心内容,通常包括以下声明:

  • iss(颁发者):生成令牌的一方。
  • sub(主题):令牌的预期用户。
  • aud(受众):令牌预期的接收者或服务。
  • exp(过期时间):令牌的有效期,是一个Unix时间戳。
  • nbf(生效时间):令牌的生效时间,是一个Unix时间戳。
  • iat(签发时间):令牌的签发时间,是一个Unix时间戳。
  • jti(唯一标识符):JWT的唯一标识符,通常是一个UUID。

载荷示例:

{
    "iss": "example.com",
    "sub": "123456",
    "exp": 1633072800
}

签名(Signature)

签名部分是对头部和载荷进行加密计算得到的结果。加密算法由头部中的alg指定。常见的加密算法包括HMAC SHA-256、RSA SHA-256等。

生成签名的过程如下:

  1. 将头部和载荷分别用Base64进行编码。
  2. 将编码后的头部和载荷用点号(.)连接起来,形成字符串。
  3. 使用头部中指定的加密算法,对上述字符串进行加密计算,生成签名部分。
  4. 将签名部分附加到头部和载荷后面,形成完整的JWT。

签名示例:

import base64
import hmac
import hashlib

header = b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'
payload = b'eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ'
secret_key = b'secret_key'

# 使用HMAC SHA-256算法生成签名
signature = base64.b64encode(hmac.new(secret_key, header + b'.' + payload, hashlib.sha256).digest())
jwt = header.decode('utf-8') + '.' + payload.decode('utf-8') + '.' + signature.decode('utf-8')

print(jwt)

如何生成JWT

使用在线工具生成JWT

在线工具如 jwt.io 提供了方便的界面来生成、验证和解析JWT。在这个网站上,你可以看到JWT的头部、载荷和签名部分,并进行实时解析。

使用编程语言生成JWT

使用编程语言生成JWT时,通常会借助专门的库来简化操作。以下是一些常见编程语言的示例代码。

Python生成JWT

import jwt

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

payload = {
    "sub": "123456",
    "name": "John Doe",
    "iat": 1633072800
}

secret_key = "secret_key"

# 生成JWT
jwt_token = jwt.encode(payload, secret_key, algorithm='HS256', headers=header)
print(jwt_token)

Java生成JWT

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtGenerator {
    public static void main(String[] args) {
        String header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
        String payload = "{\"sub\":\"123456\",\"name\":\"John Doe\",\"iat\":1633072800}";
        String secret_key = "secret_key";

        String jwt_token = Jwts.builder()
                .setHeaderString(header)
                .setPayload(payload)
                .signWith(SignatureAlgorithm.HS256, secret_key)
                .compact();
        System.out.println(jwt_token);
    }
}

JavaScript生成JWT

const jwt = require('jsonwebtoken');

const header = {
    "typ": "JWT",
    "alg": "HS256"
};

const payload = {
    "sub": "123456",
    "name": "John Doe",
    "iat": 1633072800
};

const secret_key = "secret_key";

// 生成JWT
const jwt_token = jwt.sign(payload, secret_key, { algorithm: 'HS256', header });
console.log(jwt_token);

如何验证JWT

验证JWT的有效性主要包括以下几个步骤:

  1. 检查签名:确保JWT的签名部分正确无误。
  2. 验证头信息:确保JWT的头部信息符合预期。
  3. 检查过期时间:确保JWT的有效期未过期。
  4. 验证载荷内容:确保载荷内容符合预期。

Python验证JWT

import jwt

secret_key = "secret_key"

# 验证JWT
try:
    decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
    print("JWT is valid")
    print(decoded)
except jwt.ExpiredSignatureError:
    print("JWT has expired")
except jwt.InvalidTokenError:
    print("JWT is invalid")

Java验证JWT

import io.jsonwebtoken.Jwts;

public class JwtValidator {
    public static void main(String[] args) {
        String jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxhPnoEoB/sJecMlF23cJH9GF+Vr01eJj8u0A0p0";
        String secret_key = "secret_key";

        try {
            Jwts.parser().setSigningKey(secret_key).parseClaimsJws(jwt_token);
            System.out.println("JWT is valid");
        } catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
            System.out.println("JWT is malformed");
        } catch (ExpiredJwtException e) {
            System.out.println("JWT has expired");
        } catch (UnsupportedJwtException e) {
            System.out.println("JWT is unsupported");
        } catch (IllegalArgumentException e) {
            System.out.println("JWT is invalid");
        }
    }
}

JavaScript验证JWT

const jwt = require('jsonwebtoken');

const secret_key = "secret_key";

// 验证JWT
try {
    const decoded = jwt.verify(jwt_token, secret_key, { algorithms: ['HS256'] });
    console.log("JWT is valid");
    console.log(decoded);
} catch (error) {
    console.log("JWT is invalid");
    console.error(error);
}

如何解析JWT

解析JWT内容的过程类似于验证JWT,但主要目的是提取和使用载荷中的信息。解析JWT的内容通常用于进一步处理业务逻辑,例如根据用户ID获取用户信息等。

Python解析JWT

import jwt

secret_key = "secret_key"

# 解析JWT
try:
    decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
    print("JWT is valid")
    print("User ID:", decoded['sub'])
    print("Name:", decoded['name'])
except jwt.ExpiredSignatureError:
    print("JWT has expired")
except jwt.InvalidTokenError:
    print("JWT is invalid")

Java解析JWT

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;

public class JwtParser {
    public static void main(String[] args) {
        String jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxhPnoEoB/sJecMlF23cJH9GF+Vr01eJj8u0A0p0";
        String secret_key = "secret_key";

        try {
            Claims claims = Jwts.parser().setSigningKey(secret_key).parseClaimsJws(jwt_token).getBody();
            System.out.println("JWT is valid");
            System.out.println("User ID: " + claims.getSubject());
            System.out.println("Name: " + claims.get("name"));
        } catch (Exception e) {
            System.out.println("JWT is invalid");
            e.printStackTrace();
        }
    }
}

JavaScript解析JWT

const jwt = require('jsonwebtoken');

const secret_key = "secret_key";

// 解析JWT
try {
    const decoded = jwt.verify(jwt_token, secret_key, { algorithms: ['HS256'] });
    console.log("JWT is valid");
    console.log("User ID:", decoded.sub);
    console.log("Name:", decoded.name);
} catch (error) {
    console.log("JWT is invalid");
    console.error(error);
}

JWT的安全性考虑

JWT的安全性考虑主要包括以下几个方面:

  • 时间戳与过期时间:JWT通常包含过期时间(exp),用于限制令牌的有效期。过期时间是一个Unix时间戳,表示令牌何时过期。服务器在验证JWT时,会检查当前时间是否超过过期时间,如果超过,则认为JWT已过期,不可用。
  • 签名的重要性:签名是JWT的重要组成部分,它确保了令牌的完整性和真实性。没有有效的签名,服务器无法验证令牌是否被篡改或伪造。因此,签名是保证JWT安全的关键因素。
  • 存储与传输的安全性:存储与传输JWT时需要注意以下几点:
    • 不要存储在客户端的本地存储中:存储在客户端的本地存储中(如localStorage或sessionStorage)容易被攻击者利用。
    • 使用HTTPS:在传输JWT时,必须使用HTTPS来加密数据,防止中间人攻击。
    • 避免将JWT作为URL参数传递:URL参数可以通过浏览器的历史记录和缓存泄露敏感信息。

Python设置过期时间

import jwt
import time

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

payload = {
    "sub": "123456",
    "name": "John Doe",
    "iat": int(time.time()),
    "exp": int(time.time()) + 3600  # JWT将在1小时后过期
}

secret_key = "secret_key"

# 生成JWT
jwt_token = jwt.encode(payload, secret_key, algorithm='HS256', headers=header)
print(jwt_token)
``

#### Python验证签名
```python
import jwt
import time

secret_key = "secret_key"

# 验证JWT
try:
    decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
    print("JWT is valid")
    print(decoded)
except jwt.ExpiredSignatureError:
    print("JWT has expired")
except jwt.InvalidTokenError:
    print("JWT is invalid")
``

#### 示例代码
```javascript
// 不推荐的存储方式
localStorage.setItem('jwt_token', jwt_token);

// 推荐的存储方式
sessionStorage.setItem('jwt_token', jwt_token);

实际应用案例

使用JWT进行用户认证

JWT通常用于实现用户认证,具体步骤如下:

  1. 用户登录:用户提交用户名和密码。
  2. 生成JWT:服务器验证用户凭证后,生成JWT并返回给客户端。
  3. 客户端存储JWT:客户端将JWT存储在安全的地方,如sessionStorage或HTTP-only Cookie。
  4. 验证JWT:客户端在每次请求时携带JWT,服务器验证JWT的有效性。
  5. 获取用户信息:服务器根据JWT中的用户信息(如sub)来获取用户数据。

Python示例代码

import jwt
import time

# 用户登录
def login(username, password):
    # 验证用户名和密码
    if username == 'john' and password == 'password':
        header = {
            "typ": "JWT",
            "alg": "HS256"
        }
        payload = {
            "sub": "123456",
            "name": "John Doe",
            "iat": int(time.time())
        }
        secret_key = "secret_key"

        # 生成JWT
        jwt_token = jwt.encode(payload, secret_key, algorithm='HS256', headers=header)
        return jwt_token
    else:
        return None

# 验证JWT
def verify_jwt(jwt_token):
    secret_key = "secret_key"
    try:
        decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
        return True
    except jwt.ExpiredSignatureError:
        return False
    except jwt.InvalidTokenError:
        return False

# 用户登录并获取JWT
jwt_token = login('john', 'password')
if jwt_token:
    print("JWT token:", jwt_token)

    # 验证JWT
    if verify_jwt(jwt_token):
        print("JWT is valid")
    else:
        print("JWT is invalid")
else:
    print("Login failed")

使用JWT保护API

JWT通常用于保护API,防止未授权的访问。具体步骤如下:

  1. 生成JWT:用户通过登录获取JWT。
  2. 保护API:在每个API请求中,客户端必须携带JWT。
  3. 验证JWT:服务器在处理每个API请求之前,验证JWT的有效性。
  4. 访问控制:根据JWT中的信息,决定客户端是否有权限访问特定资源。

Python示例代码

from flask import Flask, request, jsonify
import jwt
import time

app = Flask(__name__)

# 用户登录
@app.route('/login', methods=['POST'])
def login():
    data = request.json
    if data.get('username') == 'john' and data.get('password') == 'password':
        header = {
            "typ": "JWT",
            "alg": "HS256"
        }
        payload = {
            "sub": "123456",
            "name": "John Doe",
            "iat": int(time.time())
        }
        secret_key = "secret_key"

        # 生成JWT
        jwt_token = jwt.encode(payload, secret_key, algorithm='HS256', headers=header)
        return jsonify({'token': jwt_token})
    else:
        return jsonify({'message': 'Invalid credentials'}), 401

# 保护API
@app.route('/protected', methods=['GET'])
def protected_api():
    jwt_token = request.headers.get('Authorization', '').split(' ')[-1]
    secret_key = "secret_key"

    try:
        # 验证JWT
        decoded = jwt.decode(jwt_token, secret_key, algorithms=['HS256'])
        return jsonify({'message': 'Access granted'})
    except jwt.ExpiredSignatureError:
        return jsonify({'message': 'Token expired'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'message': 'Invalid token'}), 401

if __name__ == '__main__':
    app.run(debug=True)

总结

JWT是一种广泛使用的协议,用于在网络应用环境中安全传输信息。它具有无状态、紧凑、易于验证和解析等优势,非常适合用于用户认证和API保护等场景。通过本教程的学习,你已经掌握了JWT的基本结构、生成、验证、解析和安全性考虑,以及如何在实际应用中使用JWT。希望这对你理解和应用JWT有所帮助。更多学习资源可以参考慕课网或其他相关资源。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消