本文详细介绍了JWT的定义、工作原理、优势及应用场景,包括JWT的基本结构和生成方法,以及头部、负载和签名部分的示例代码。文章还提供了JWT验证和解析的具体步骤,以及如何处理过期和被篡改的JWT。此外,本文展示了JWT在Web应用和移动应用中的实际应用,提供了具体的代码示例。
JWT资料详解:新手入门教程 1. JWT简介什么是JWT
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息。JWT的核心思想是将用户身份信息编码为一个紧凑且自包含的字符串,可以安全地传输并通过验证。JWT由三部分组成:头部、负载和签名。
JWT的工作原理
- 生成JWT:客户端向服务器发送身份验证请求,服务器验证身份后生成一个JWT,并返回给客户端。
- 存储JWT:客户端将JWT存储起来,通常存放在浏览器的localStorage或sessionStorage中。
- 发送JWT:每次访问受保护的资源时,客户端将JWT包含在HTTP请求头中发送给服务器。
- 验证JWT:服务器收到请求后,验证JWT的签名,确认JWT是否有效。
- 响应请求:如果JWT有效,服务器返回请求的数据;如果无效,拒绝请求。
JWT的优势和应用场景
- 安全性高:JWT通过哈希算法对信息进行加密,确保数据在传输过程中的安全性。
- 无状态:服务器不需要存储会话状态,减轻服务器负担。
- 跨语言支持:支持多种语言实现,适用于多种平台。
- 方便传输:JWT是一个紧凑的编码字符串,适合通过URL、POST请求或Cookie传输。
- 应用场景:适用于API认证、单点登录、分布式系统中用户信息的传输等场景。
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。
共同学习,写下你的评论
评论加载中...
作者其他优质文章