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

JWT资料详解:新手入门教程

标签:
杂七杂八
概述

本文详细介绍了JWT的定义、工作原理、优势及应用场景,包括JWT的基本结构和生成方法,以及头部、负载和签名部分的示例代码。文章还提供了JWT验证和解析的具体步骤,以及如何处理过期和被篡改的JWT。此外,本文展示了JWT在Web应用和移动应用中的实际应用,提供了具体的代码示例。

JWT资料详解:新手入门教程
1. JWT简介

什么是JWT

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。JWT的核心思想是将用户身份信息编码为一个紧凑且自包含的字符串,可以安全地传输并通过验证。JWT由三部分组成:头部、负载和签名。

JWT的工作原理

  1. 生成JWT:客户端向服务器发送身份验证请求,服务器验证身份后生成一个JWT,并返回给客户端。
  2. 存储JWT:客户端将JWT存储起来,通常存放在浏览器的localStorage或sessionStorage中。
  3. 发送JWT:每次访问受保护的资源时,客户端将JWT包含在HTTP请求头中发送给服务器。
  4. 验证JWT:服务器收到请求后,验证JWT的签名,确认JWT是否有效。
  5. 响应请求:如果JWT有效,服务器返回请求的数据;如果无效,拒绝请求。

JWT的优势和应用场景

  • 安全性高:JWT通过哈希算法对信息进行加密,确保数据在传输过程中的安全性。
  • 无状态:服务器不需要存储会话状态,减轻服务器负担。
  • 跨语言支持:支持多种语言实现,适用于多种平台。
  • 方便传输:JWT是一个紧凑的编码字符串,适合通过URL、POST请求或Cookie传输。
  • 应用场景:适用于API认证、单点登录、分布式系统中用户信息的传输等场景。
2. JWT的基本结构

Header(头部)

JWT的头部包含两个部分:声明类型(typ)和算法(alg)。

  • typ: 声明此令牌的类型,一般为JWT
  • alg: 声明用来签名的加密算法,常用的算法有HS256(HMAC SHA-256)和RS256(RSA SHA-256)。

示例:

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

Payload(负载)

负载部分包含声明,这些声明被加密签名以确保信息的安全性。声明通常分为三类:注册声明(推荐但不是强制的声明)、公共声明(不推荐修改的声明)、私有声明(自定义声明)。

示例:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

解析:

  • sub: 主题,表示JWT所指向的用户。
  • name: 自定义的用户信息。
  • iat: 发行时间,表示JWT创建的时间戳。

Signature(签名)

签名部分用于验证JWT的完整性和真实性。通过使用头部指定的算法,例如HS256,对头部和负载的Base64编码字符串进行加密,生成签名。

示例:

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

签名算法示例:

import hmac
import hashlib

def generate_signature(header, payload, secret):
    encoded_header = base64UrlEncode(header)
    encoded_payload = base64UrlEncode(payload)
    hash_input = f"{encoded_header}.{encoded_payload}".encode('utf-8')
    signature = hmac.new(secret.encode('utf-8'), hash_input, hashlib.sha256).hexdigest()
    return signature

def base64UrlEncode(input):
    return base64.urlsafe_b64encode(input).rstrip(b'=').decode('utf-8')

header = {"typ": "JWT", "alg": "HS256"}
payload = {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
secret = "my_secret_key"

signature = generate_signature(header, payload, secret)
print(signature)
3. 如何生成JWT

生成Header

生成头部的示例代码:

import base64

def generate_header(algorithm='HS256'):
    header = {"typ": "JWT", "alg": algorithm}
    encoded_header = base64UrlEncode(json.dumps(header).encode('utf-8'))
    return encoded_header

def base64UrlEncode(input):
    return base64.urlsafe_b64encode(input).rstrip(b'=').decode('utf-8')

header = generate_header()
print(header)

生成Payload

生成负载的示例代码:

import base64
import json
import time

def generate_payload(sub='1234567890', name='John Doe'):
    payload = {
        "sub": sub,
        "name": name,
        "iat": int(time.time())
    }
    encoded_payload = base64UrlEncode(json.dumps(payload).encode('utf-8'))
    return encoded_payload

def base64UrlEncode(input):
    return base64.urlsafe_b64encode(input).rstrip(b'=').decode('utf-8')

payload = generate_payload()
print(payload)

生成Signature

生成签名的示例代码:

import hmac
import hashlib
import base64

def generate_signature(header, payload, secret):
    hash_input = f"{header}.{payload}".encode('utf-8')
    signature = hmac.new(secret.encode('utf-8'), hash_input, hashlib.sha256).digest()
    return base64.urlsafe_b64encode(signature).decode('utf-8')

header = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ"
secret = "my_secret_key"

signature = generate_signature(header, payload, secret)
print(signature)

综合示例

完整的JWT生成示例代码:

import json
import time
import base64
import hmac
import hashlib

def base64UrlEncode(input):
    return base64.urlsafe_b64encode(input).rstrip(b'=').decode('utf-8')

def generate_header(algorithm='HS256'):
    header = {"typ": "JWT", "alg": algorithm}
    return base64UrlEncode(json.dumps(header).encode('utf-8'))

def generate_payload(sub='1234567890', name='John Doe'):
    payload = {
        "sub": sub,
        "name": name,
        "iat": int(time.time())
    }
    return base64UrlEncode(json.dumps(payload).encode('utf-8'))

def generate_signature(header, payload, secret):
    hash_input = f"{header}.{payload}".encode('utf-8')
    signature = hmac.new(secret.encode('utf-8'), hash_input, hashlib.sha256).digest()
    return base64UrlEncode(signature).decode('utf-8')

def generate_jwt(sub='1234567890', name='John Doe', secret="my_secret_key"):
    header = generate_header()
    payload = generate_payload(sub, name)
    signature = generate_signature(header, payload, secret)
    return f"{header}.{payload}.{signature}"

jwt_token = generate_jwt()
print(jwt_token)
4. 如何验证和解析JWT

验证Signature

验证JWT签名的示例代码:

import hmac
import hashlib
import base64
import json

def base64UrlDecode(input):
    padding = b'=' * (4 - len(input) % 4)
    return base64.urlsafe_b64decode(input + padding)

def verify_signature(header, payload, signature, secret):
    hash_input = f"{header}.{payload}".encode('utf-8')
    expected_signature = hmac.new(secret.encode('utf-8'), hash_input, hashlib.sha256).digest()
    return base64UrlDecode(signature) == expected_signature

header, payload, signature = jwt_token.split('.')
secret = "my_secret_key"

is_valid_signature = verify_signature(header, payload, signature, secret)
print(is_valid_signature)

解析JWT的各部分

解析JWT各部分的示例代码:

import base64
import json

def parse_jwt(jwt_token):
    parts = jwt_token.split('.')
    header = base64.urlsafe_b64decode(parts[0] + '==').decode('utf-8')
    payload = base64.urlsafe_b64decode(parts[1] + '==').decode('utf-8')
    signature = base64.urlsafe_b64decode(parts[2] + '==').decode('utf-8')
    return json.loads(header), json.loads(payload), signature

jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.eyJhYmMiOiJiYWNjZGVmIiwidmFsaW9uIjoibGFzdGluZyJ9"
header, payload, signature = parse_jwt(jwt_token)
print("Header:", header)
print("Payload:", payload)
print("Signature:", signature)

验证Payload中的数据

验证负载数据的示例代码:

import json
import time

def validate_payload(decoded_payload):
    if 'sub' not in decoded_payload:
        return False
    if 'iat' not in decoded_payload:
        return False
    return True

decoded_payload = {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": 1516239022
}

is_valid_payload = validate_payload(decoded_payload)
print(is_valid_payload)
5. 使用JWT的常见问题和解决方法

如何处理过期的JWT

JWT可以通过设置过期时间(exp)来控制其有效时长。当JWT过期时,需要重新生成一个新的JWT。

示例:

import json
import time
import base64
import hmac
import hashlib

def generate_payload(sub='1234567890', name='John Doe', exp=int(time.time()) + 3600):
    payload = {
        "sub": sub,
        "name": name,
        "iat": int(time.time()),
        "exp": exp
    }
    return base64UrlEncode(json.dumps(payload).encode('utf-8'))

def verify_expiration(jwt_token, secret):
    header, payload, signature = jwt_token.split('.')
    decoded_payload = json.loads(base64UrlDecode(payload + '==').decode('utf-8'))
    if 'exp' in decoded_payload:
        return decoded_payload['exp'] > time.time()
    return False

jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDE4MjJ9.eyJhYmMiOiJiYWNjZGVmIiwidmFsaW9uIjoibGFzdGluZyJ9"
secret = "my_secret_key"

is_expired = verify_expiration(jwt_token, secret)
print(is_expired)

如何处理被篡改的JWT

JWT的签名机制可以确保JWT在传输过程中不会被篡改。如果收到的JWT签名不正确,那么可以判断JWT已经被篡改。

示例:

import json
import time
import base64
import hmac
import hashlib

def verify_signature(header, payload, signature, secret):
    hash_input = f"{header}.{payload}".encode('utf-8')
    expected_signature = hmac.new(secret.encode('utf-8'), hash_input, hashlib.sha256).digest()
    return base64UrlDecode(signature + '==') == expected_signature

def verify_jwt(jwt_token, secret):
    header, payload, signature = jwt_token.split('.')
    return verify_signature(header, payload, signature, secret)

jwt_token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDE4MjJ9.eyJhYmMiOiJiYWNjZGVmIiwidmFsaW9uIjoibGFzdGluZyJ9"
secret = "my_secret_key"

is_valid_jwt = verify_jwt(jwt_token, secret)
print(is_valid_jwt)

如何安全地存储JWT

JWT可以存储在浏览器的localStorage或sessionStorage中,也可以存储在本地Cookie中,但需要注意存储位置的安全性。

  • localStorage: 存储在浏览器的本地存储中,可以持久化存储,但存在被恶意脚本访问的风险。
  • sessionStorage: 存储在浏览器的会话存储中,只在当前窗口或标签页中有效,但同样存在被恶意脚本访问的风险。
  • Cookie: 存储在服务器端,可以通过设置HttpOnly标志防止被JavaScript访问,但需要确保HTTPS协议的使用以防止中间人攻击。

示例:

// 存储到localStorage
localStorage.setItem('token', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDE4MjJ9.eyJhYmMiOiJiYWNjZGVmIiwidmFsaW9uIjoibGFzdGluZyJ9');

// 从localStorage获取token
const token = localStorage.getItem('token');
console.log(token);
// 设置HTTP-only cookie
document.cookie = "token=" + encodeURIComponent(jwtToken) + "; path=/; HttpOnly";
6. JWT的实践应用

在Web应用中的使用

在Web应用中,可以使用JWT进行用户身份验证。当用户登录后,服务器生成一个JWT并返回给客户端,客户端将JWT存储起来并在每次请求时发送给服务器。

示例(后端):

import json
import time
import base64
import hmac
import hashlib

def generate_jwt(sub='1234567890', name='John Doe', secret="my_secret_key"):
    header = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
    payload = f"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDE4MjJ9"
    signature = hmac.new(secret.encode('utf-8'), f"{header}.{payload}".encode('utf-8'), hashlib.sha256).hexdigest()
    return f"{header}.{payload}.{base64.urlsafe_b64encode(signature.encode('utf-8')).rstrip(b'=').decode('utf-8')}"

def handle_login_request(username, password):
    if validate_credentials(username, password):
        return generate_jwt()
    else:
        return "Invalid credentials"

def validate_credentials(username, password):
    # 实际应用中,需要从数据库中验证用户名和密码
    return username == "john" and password == "doe"

jwt_token = handle_login_request("john", "doe")
print(jwt_token)

示例(前端):

import axios from 'axios';

const loginRequest = async (username, password) => {
    const response = await axios.post('/login', { username, password });
    localStorage.setItem('token', response.data.token);
    return response.data.token;
}

const fetchProtectedResource = async (token) => {
    const response = await axios.get('/protected', {
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });
    console.log(response.data);
}

loginRequest("john", "doe").then(token => {
    fetchProtectedResource(token);
});

在移动应用中的使用

在移动应用中,可以将JWT存储在应用的SharedPreferences、UserDefaults或其他持久化存储中。每次请求时,从存储中读取JWT并发送给服务器。

示例(Android):

import android.content.Context;
import android.content.SharedPreferences;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity {

    private static final String PREFS_NAME = "MyPrefs";
    private static final String TOKEN_KEY = "token";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
        String token = prefs.getString(TOKEN_KEY, null);

        if (token == null) {
            String jwtToken = loginRequest("john", "doe");
            prefs.edit().putString(TOKEN_KEY, jwtToken).apply();
        } else {
            fetchProtectedResource(token);
        }
    }

    private String loginRequest(String username, String password) {
        OkHttpClient client = new OkHttpClient.Builder().build();
        RequestBody body = RequestBody.create(JSON, "{\"username\":\"" + username + "\",\"password\":\"" + password + "\"}");
        Request request = new Request.Builder()
                .url("http://example.com/login")
                .post(body)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                return response.body().string();
            } else {
                throw new IOException("Unexpected code " + response);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    private void fetchProtectedResource(String token) {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();

        Request request = new Request.Builder()
                .url("http://example.com/protected")
                .addHeader("Authorization", "Bearer " + token)
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (response.isSuccessful()) {
                System.out.println(response.body().string());
            } else {
                throw new IOException("Unexpected code " + response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

示例(iOS):

import Foundation

func loginRequest(username: String, password: String) -> String? {
    let url = URL(string: "http://example.com/login")
    var request = URLRequest(url: url!)
    request.httpMethod = "POST"
    request.httpBody = try? JSONEncoder().encode(["username": username, "password": password])
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else { return }
        let token = try! JSONDecoder().decode(String.self, from: data)
        UserDefaults.standard.set(token, forKey: "jwtToken")
    }
    task.resume()
    return nil
}

func fetchProtectedResource() {
    guard let token = UserDefaults.standard.string(forKey: "jwtToken") else { return }
    let url = URL(string: "http://example.com/protected")
    var request = URLRequest(url: url!)
    request.httpMethod = "GET"
    request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        guard let data = data, error == nil else { return }
        print(String(data: data, encoding: .utf8)!)
    }
    task.resume()
}

loginRequest(username: "john", password: "doe")
fetchProtectedResource()

在API接口的认证中的应用

在API接口的认证中,可以使用JWT来进行请求认证。每个请求都需要包含JWT,服务器通过验证JWT来确认请求的有效性。

示例(后端):

from flask import Flask, request, jsonify
import json
import time
import base64
import hmac
import hashlib

app = Flask(__name__)

def generate_jwt(sub='1234567890', name='John Doe', secret="my_secret_key"):
    header = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
    payload = f"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDE4MjJ9"
    signature = hmac.new(secret.encode('utf-8'), f"{header}.{payload}".encode('utf-8'), hashlib.sha256).hexdigest()
    return f"{header}.{payload}.{base64.urlsafe_b64encode(signature.encode('utf-8')).rstrip(b'=').decode('utf-8')}"

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username')
    password = request.json.get('password')
    if validate_credentials(username, password):
        jwt_token = generate_jwt()
        return jsonify({"token": jwt_token})
    else:
        return jsonify({"error": "Invalid credentials"}), 401

@app.route('/protected', methods=['GET'])
def protected_resource():
    authorization = request.headers.get('Authorization')
    if authorization and authorization.startswith('Bearer '):
        token = authorization[len('Bearer '):]
        if verify_jwt(token, "my_secret_key"):
            return jsonify({"message": "Access granted"})
        else:
            return jsonify({"error": "Invalid token"}), 401
    else:
        return jsonify({"error": "Missing Authorization header"}), 401

def validate_credentials(username, password):
    # 实际应用中,需要从数据库中验证用户名和密码
    return username == "john" and password == "doe"

def verify_jwt(jwt_token, secret):
    header, payload, signature = jwt_token.split('.')
    hash_input = f"{header}.{payload}".encode('utf-8')
    expected_signature = hmac.new(secret.encode('utf-8'), hash_input, hashlib.sha256).digest()
    return base64.urlsafe_b64encode(signature.encode('utf-8')).rstrip(b'=').decode('utf-8') == expected_signature

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

示例(前端):

import axios from 'axios';

const loginRequest = async (username, password) => {
    const response = await axios.post('http://localhost:5000/login', { username, password });
    localStorage.setItem('token', response.data.token);
    return response.data.token;
}

const fetchProtectedResource = async (token) => {
    const response = await axios.get('http://localhost:5000/protected', {
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });
    console.log(response.data);
}

loginRequest("john", "doe").then(token => {
    fetchProtectedResource(token);
});

以上是关于JWT的详细介绍,包括其定义、生成、验证、安全存储以及在不同场景下的应用。希望这些内容能够帮助你更好地理解和使用JWT。

点击查看更多内容
TA 点赞

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

评论

作者其他优质文章

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

100积分直接送

付费专栏免费学

大额优惠券免费领

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

举报

0/150
提交
取消