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

JWT学习:初学者指南

标签:
API
概述

JWT学习涵盖了JWT的基本概念、工作原理、组成部分以及如何在不同后端语言中生成和验证JWT,同时还介绍了JWT的安全注意事项和实战演练中的用户认证流程。

JWT简介

什么是JWT

JSON Web Token (JWT) 是一种开放标准 (RFC 7519),用于在网络应用环境间安全地传输信息。JWT使用JSON格式表示用户信息,通常用于身份验证和信息交换。JWT由三部分组成:Header、Payload和Signature。

JWT的工作原理

当用户通过认证(如输入用户名和密码)后,服务器会生成一个JWT并返回给客户端。客户端在后续的请求中将此JWT作为HTTP头部信息的一部分发送给服务器,服务器通过验证JWT来确认用户的认证状态。JWT验证后,服务器将用户的信息与数据库中的信息进行匹配,确认用户是否合法。如果匹配成功,服务器将接受请求并返回相应数据。

JWT与传统Session的区别

JWT与Session的区别在于存储方式与安全性。

  • 存储方式:传统Session将用户数据存储在服务器端,而JWT将用户信息编码在Token中,存储在客户端。
  • 安全性:Session依赖于服务器端存储,如果服务器端的安全措施不佳,Session信息容易被攻击者获取。JWT则通过加密传输,即使Token被截获,攻击者也难以解密Token中的数据。
JWT的基本组成部分

Header(头部)

JWT的Header通常是JSON格式,包含两部分信息:令牌类型(即JWT)和加密算法(例如,HMAC SHA256或RSA)。以下是一个典型的JWT Header:

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

Payload(载荷)

Payload是JWT的核心部分,包含了用户的信息(如用户ID、用户名等),这些信息会与服务器端的数据进行匹配以验证用户身份。Payload是一个包含一组声明的JSON对象,这些声明包含有关实体和其他上下文信息的声明。JWT标准定义了一些默认声明,如issexpsub等,但Payload还可以包含其他自定义声明。一个典型的Payload如下:

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "exp": 1516239022
}

Signature(签名)

Signature部分是通过Header中指定的算法,基于Header、Payload以及一个密钥进行加密生成的。签名保证了数据的完整性,确保了JWT在传输过程中没有被篡改。签名的生成过程如下:

  1. Header和Payload通过base64UrlEncode编码。
  2. 将编码后的Header和Payload用点.连接起来,形成字符串。
  3. 使用Header指定的算法(如HMAC SHA256)和密钥对上述字符串进行签名。

示例如下:

import base64
import hmac
import hashlib

def generate_signature(header_b64, payload_b64, secret):
    message = f"{header_b64}.{payload_b64}"
    signature = hmac.new(secret, message.encode(), hashlib.sha256).hexdigest()
    return base64.urlsafe_b64encode(signature.encode()).rstrip(b'=').decode()
如何生成和验证JWT

使用JWT库生成Token

JWT库可以简化生成和验证Token的过程。下面以Python的PyJWT库为例,展示如何生成一个JWT:

import jwt
import datetime

# 定义payload
payload = {
    'sub': '1234567890',
    'name': 'John Doe',
    'admin': True,
    'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300)
}

# 生成JWT
token = jwt.encode(payload, 'secret-key', algorithm='HS256')
print(token)

如何验证Token的有效性

验证Token的有效性需要以下几个步骤:

  1. 分解Token字符串。
  2. 重构Header和Payload。
  3. 使用Header指定的算法和密钥重新计算签名。
  4. 比较计算出的签名与Token中的签名是否一致。
  5. 检查Payload是否包含有效信息(如过期时间)。

示例如下:

import jwt
import datetime

# 解码并验证Token
try:
    decoded_token = jwt.decode(token, 'secret-key', algorithms=['HS256'])
    print(decoded_token)
except jwt.ExpiredSignatureError:
    print("Token已过期")
except jwt.InvalidTokenError:
    print("Token无效")

使用PHP生成和验证JWT

在PHP中,可以使用firebase/php-jwt库生成和验证JWT。

<?php
require 'vendor/autoload.php';

use Firebase\JWT\JWT;

$secret = 'secret-key';

// 生成JWT
$payload = array(
    'sub' => '1234567890',
    'name' => 'John Doe',
    'admin' => true,
    'exp' => time() + (10 * 60)
);

$jwt = JWT::encode($payload, $secret);
echo $jwt;

// 验证JWT
try {
    $decoded_token = JWT::decode($jwt, $secret, ['HS256']);
    echo json_encode($decoded_token);
} catch (Exception $e) {
    echo $e->getMessage();
}
?>
JWT在不同后端语言中的实现

Node.js中的JWT使用示例

在Node.js中,可以使用jsonwebtoken库生成和验证JWT。

const jwt = require('jsonwebtoken');
const secret = 'secret-key';

// 生成JWT
let payload = {
    sub: '1234567890',
    name: 'John Doe',
    admin: true,
    exp: Math.floor(Date.now() / 1000) + (10 * 60)
};

const token = jwt.sign(payload, secret, { algorithm: 'HS256' });
console.log(token);

// 验证JWT
try {
    const decoded = jwt.verify(token, secret);
    console.log(decoded);
} catch (error) {
    console.log(error.message);
}

Python中的JWT使用示例

在Python中,可以使用PyJWT库生成和验证JWT。

import jwt
import datetime

# 生成JWT
payload = {
    'sub': '1234567890',
    'name': 'John Doe',
    'admin': True,
    'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300)
}

token = jwt.encode(payload, 'secret-key', algorithm='HS256')
print(token)

# 验证JWT
try:
    decoded_token = jwt.decode(token, 'secret-key', algorithms=['HS256'])
    print(decoded_token)
except jwt.ExpiredSignatureError:
    print("Token已过期")
except jwt.InvalidTokenError:
    print("Token无效")

PHP中的JWT使用示例

在PHP中,可以使用firebase/php-jwt库生成和验证JWT。

<?php
require 'vendor/autoload.php';

use Firebase\JWT\JWT;

$secret = 'secret-key';

// 生成JWT
$payload = array(
    'sub' => '1234567890',
    'name' => 'John Doe',
    'admin' => true,
    'exp' => time() + (10 * 60)
);

$jwt = JWT::encode($payload, $secret);
echo $jwt;

// 验证JWT
try {
    $decoded_token = JWT::decode($jwt, $secret, ['HS256']);
    echo json_encode($decoded_token);
} catch (Exception $e) {
    echo $e->getMessage();
}
?>
JWT的安全注意事项

保持密钥的安全

密钥是JWT的核心部分之一,用于生成和验证Token。如果密钥泄露,攻击者可以生成伪造的Token,冒充合法用户。因此,密钥应该安全存储,不公开。可以考虑使用环境变量来存储密钥。

设置合理的过期时间

合理的过期时间对于保证安全非常重要。过期时间太短,用户需要频繁登录;过期时间太长,一旦Token泄露,攻击者可以长期冒充合法用户。建议根据不同场景设置合适的过期时间。

避免Token泄露

Token泄露的风险较高,因为Token通常存储在客户端(如浏览器的localStorage或cookie),容易被截获。可以通过HTTPS协议传输Token,以防止中间人攻击。同时,建议将Token存储在HTTP-only和Secure的cookie中,进一步提高安全性。

实战演练:使用JWT进行用户认证

登录时生成JWT Token

当用户登录时,服务器需要验证用户的身份,并生成一个有效的JWT Token。以下是一个简单的登录流程示例:

  1. 用户提交用户名和密码。
  2. 服务器验证用户名和密码的有效性。
  3. 生成JWT Token,并将其返回给客户端。

示例如下(使用Python):

import jwt
import datetime

def login(username, password):
    # 这里可以设置为数据库查询等操作
    if username == 'admin' and password == 'password':
        payload = {
            'sub': '1234567890',
            'name': 'John Doe',
            'admin': True,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300)
        }
        token = jwt.encode(payload, 'secret-key', algorithm='HS256')
        return token
    else:
        return None

# 用户登录
token = login('admin', 'password')
print(token)

在请求中使用JWT进行验证

客户端在请求时需要将JWT Token附加到请求头中,以便服务器验证Token的有效性。

示例如下(使用Python的Flask框架):

from flask import Flask, request, jsonify
import jwt

app = Flask(__name__)
secret = 'secret-key'

def protected():
    try:
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'message': '没有Token'}), 401

        token = token.split(" ")[1]
        decoded_token = jwt.decode(token, secret, algorithms=['HS256'])

        return jsonify({'message': '成功', 'user': decoded_token})
    except jwt.ExpiredSignatureError:
        return jsonify({'message': 'Token已过期'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'message': 'Token无效'}), 401

@app.route('/protected')
def protected_route():
    return protected()

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

会话的结束与Token的撤销

Token的撤销机制非常重要。在用户注销或Token过期后,服务器需要能够识别并拒绝过期或撤销的Token。一种常见的方法是将过期的Token加入黑名单,服务器在验证Token时检查Token是否在黑名单中。

示例如下(使用Python):

import jwt
import datetime
import redis

# 连接Redis
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)

def logout(token):
    try:
        decoded_token = jwt.decode(token, 'secret-key', algorithms=['HS256'])
        # 将Token加入黑名单
        redis_client.set(token, 'revoked', ex=300)
        return '注销成功'
    except jwt.ExpiredSignatureError:
        return 'Token已过期'
    except jwt.InvalidTokenError:
        return 'Token无效'

def protected():
    try:
        token = request.headers.get('Authorization')
        if not token:
            return jsonify({'message': '没有Token'}), 401

        token = token.split(" ")[1]
        if redis_client.get(token):
            return jsonify({'message': 'Token已被注销'}), 401

        decoded_token = jwt.decode(token, 'secret-key', algorithms=['HS256'])

        return jsonify({'message': '成功', 'user': decoded_token})
    except jwt.ExpiredSignatureError:
        return jsonify({'message': 'Token已过期'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'message': 'Token无效'}), 401

@app.route('/logout')
def logout_route():
    token = request.headers.get('Authorization')
    if not token:
        return jsonify({'message': '没有Token'}), 401
    token = token.split(" ")[1]
    return logout(token)

if __name__ == '__main__':
    app.run(debug=True)
点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消