前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JWT在Web应用中的安全登录鉴权与单点登录实现

JWT在Web应用中的安全登录鉴权与单点登录实现

原创
作者头像
GeekLiHua
发布2024-08-30 22:24:48
1180
发布2024-08-30 22:24:48
举报
文章被收录于专栏:高并发

JWT在Web应用中的安全登录鉴权与单点登录实现

登录鉴权功能与JWT的好处

JSON Web Tokens(JWT)是一种广泛使用的开放标准(RFC 7519),用于在网络应用环境间传递声明(claim)。它定义了一种紧凑且自包含的方式,用于在各方之间传递安全信息。

JWT的好处包括:

    1. 跨语言和平台

描述: JWT作为一种轻量级的数据格式,可以在不同的编程语言和平台上无缝工作。

代码示例: 假设我们使用Python的pyjwt库来生成和解析JWT。

代码语言:python
代码运行次数:0
复制
import jwt
import datetime

# 密钥
SECRET_KEY = 'YOUR_SECRET_KEY'

# 用户信息
payload = {
    'user_id': 123456,
    'username': 'johndoe',
    'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=30)  # 过期时间
}

# 生成JWT
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print('Generated Token:', token)
    1. 无状态性

描述: 服务端不需要存储用户的会话信息,每个JWT都包含了所需的所有信息。

代码示例: 使用Node.js和jsonwebtoken库来生成无状态的JWT。

代码语言:javascript
复制
const jwt = require('jsonwebtoken');

// 密钥
const secretKey = 'YOUR_SECRET_KEY';

// 用户信息
const payload = {
    userId: 1,
    userName: 'John Doe'
};

// 设置过期时间
const options = {
    expiresIn: '2h' // 2小时后过期
};

// 生成JWT
const token = jwt.sign(payload, secretKey, options);
console.log('Generated Token:', token);
    1. 安全性

描述: JWT通过使用密钥进行签名,确保了数据的完整性和来源的验证。

代码示例: 使用Java的jjwt库来验证JWT的签名。

代码语言:java
复制
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

public class JwtExample {
    public static void main(String[] args) {
        String secretKey = "YOUR_SECRET_KEY";
        String token = Jwts.builder()
                .setSubject("John Doe")
                .setIssuedAt(new Date())
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
        
        System.out.println("Generated Token: " + token);
        
        // 验证JWT
        boolean isTokenValid = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token)
                .getBody()
                .getSubject()
                .equals("John Doe");
        System.out.println("Is Token Valid: " + isTokenValid);
    }
}
4. 易于扩展

描述: JWT允许开发者添加自定义的声明(claims),以扩展其功能。

代码示例: 在Node.js中添加自定义声明。

代码语言:javascript
复制
const jwt = require('jsonwebtoken');

// 密钥
const secretKey = 'YOUR_SECRET_KEY';

// 用户信息和自定义声明
const payload = {
    userId: 1,
    userName: 'John Doe',
    customClaim: 'custom_value'  // 自定义声明
};

// 生成JWT
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
console.log('Generated Token with Custom Claim:', token);
    1. 灵活性

描述: JWT的载荷可以自定义,包含额外的用户信息或应用特定的数据。

代码示例: 使用Python添加额外的用户信息。

代码语言:python
代码运行次数:0
复制
import jwt

# 密钥
SECRET_KEY = 'YOUR_SECRET_KEY'

# 用户信息和额外数据
payload = {
    'user_id': 123456,
    'username': 'johndoe',
    'extra_data': {
        'role': 'admin',
        'department': 'IT'
    }
}

# 生成JWT
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print('Generated Token with Extra Data:', token)

如何防止JWT TOKEN被篡改

以下是一些防止JWT TOKEN被篡改的策略:

1. 使用HTTPS

描述: HTTPS通过SSL/TLS加密传输数据,保护数据不被窃听或篡改。

代码示例: 使用Python的http.serverssl模块创建HTTPS服务器。

代码语言:python
代码运行次数:0
复制
from http.server import HTTPServer, BaseHTTPRequestHandler
import ssl

# 自定义请求处理器
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        # 发送HTTP状态码200
        self.send_response(200)
        self.end_headers()
        # 返回内容
        self.wfile.write(b'Hello, HTTPS world!')

# 创建HTTP服务器对象,监听localhost的443端口
httpd = HTTPServer(('localhost', 443), SimpleHTTPRequestHandler)

# 使用ssl模块包装socket以支持HTTPS
httpd.socket = ssl.wrap_socket(
    httpd.socket,
    keyfile='path/to/your/private.key',  # 私钥文件路径
    certfile='path/to/your/certificate.crt',  # 证书文件路径
    server_side=True  # 表示服务器端
)

# 启动服务器
print("Starting HTTPS server on https://localhost:443")
httpd.serve_forever()

2. 签名

描述: 使用强密钥对JWT进行签名,确保JWT的安全性。

代码示例: 使用Python的pyjwt库生成签名的JWT。

代码语言:python
代码运行次数:0
复制
import jwt
import datetime

# 定义密钥
SECRET_KEY = 'YOUR_SECRET_KEY'

# 定义JWT的负载内容
payload = {
    'user_id': 123456,
    'username': 'johndoe',
    # 设置过期时间为当前时间后1小时
    'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}

# 使用HS256算法对payload进行编码(生成JWT)
token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
print('Signed JWT:', token)

3. 密钥管理

描述: 定期更换密钥并安全存储,避免泄露。

代码示例: 使用环境变量来存储密钥。

代码语言:python
代码运行次数:0
复制
import os
import jwt

# 从环境变量中获取密钥
SECRET_KEY = os.getenv('JWT_SECRET_KEY')

# 使用SECRET_KEY生成和验证JWT的代码与前面类似
# ...

4. 算法选择

描述: 选择更强的算法,如RSA或ECDSA。

代码示例: 使用pyjwt库和RSA算法生成JWT。

代码语言:python
代码运行次数:0
复制
from jwt import JWT, PyJWK, PyJWKSet

# 假设你已经有了一个RSA私钥和公钥
private_key = """-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----"""

public_key = """-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----"""

# 从公钥创建JWKSet对象
jwk_set = PyJWKSet.from_pem(public_key)
jwk = jwk_set.keys[0]  # 获取公钥对象

# 定义JWT的负载内容
payload = {'user_id': 123456, 'username': 'johndoe'}

# 使用RS256算法对payload进行编码(生成JWT)
token = JWT().encode(payload, private_key, algorithm='RS256')
print('JWT with RS256:', token)

5. 验证算法

描述: 确保客户端和服务器端都验证JWT的签名算法。

代码示例: 使用pyjwt库验证JWT的签名算法。

代码语言:python
代码运行次数:0
复制
# 假设token是使用RS256算法签名的
try:
    # 解码JWT,验证签名
    decoded_token = jwt.decode(token, key=jwk, algorithms=["RS256"])
    print('JWT Algorithm Valid:', True)
except jwt.PyJWTError:
    print('JWT Algorithm Valid:', False)

6. 访问控制

描述: 确保只有授权的应用和服务可以访问和验证JWT。

代码示例: 使用Flask框架设置JWT访问控制。

代码语言:python
代码运行次数:0
复制
from flask import Flask, request, jsonify
from functools import wraps
import jwt

app = Flask(__name__)
# 定义密钥
SECRET_KEY = 'YOUR_SECRET_KEY'

# 创建装饰器,用于保护需要token验证的路由
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.args.get('token')  # 从请求中获取token
        if not token:
            return jsonify({'message': 'Token is missing!'}), 403

        # 验证token
        try:
            data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
        except:
            return jsonify({'message': 'Token is invalid!'}), 403

        return f(*args, **kwargs)
    return decorated

# 创建一个需要token验证的路由
@app.route('/protected')
@token_required
def protected():
    return jsonify({'message': 'This is a protected endpoint'})

if __name__ == '__main__':
    # 运行Flask应用,使用adhoc创建自签名证书
    app.run(ssl_context='adhoc')

JWT TOKEN如何实现单点登录(SSO)

单点登录(SSO)是一种允许用户使用单一凭证在多个相关但独立的系统间访问的机制。JWT可以有效地实现SSO,以下是其实现过程:

JWT TOKEN实现单点登录(SSO)的Python代码和案例

1. 身份验证

描述: 用户首次登录时,系统验证身份并生成JWT。

代码示例:

代码语言:python
代码运行次数:0
复制
import jwt
import datetime

# 用户登录验证函数
def authenticate_user(username, password):
    # 这里应有验证逻辑,例如查询数据库
    # 假设用户名和密码正确
    return True

# 生成JWT的函数
def generate_jwt(user_id):
    try:
        payload = {
            'user_id': user_id,
            'exp': datetime.datetime.utcnow() + datetime.timedelta(days=1)  # JWT有效期1天
        }
        # 使用HS256算法和密钥生成JWT
        jwt_token = jwt.encode(payload, 'YOUR_SECRET_KEY', algorithm='HS256')
        return jwt_token
    except Exception as e:
        print("Error generating JWT:", e)
        return None

2. 存储会话

描述: 将JWT存储在用户的浏览器中,通常通过HTTP Only Cookie。

代码示例: 使用Flask设置HTTP Only Cookie。

代码语言:python
代码运行次数:0
复制
from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/login')
def login():
    username = request.args.get('username')
    password = request.args.get('password')
    
    if authenticate_user(username, password):
        user_id = 1  # 假设的用户ID
        jwt_token = generate_jwt(user_id)
        resp = make_response(jwt_token)  # 创建响应对象
        resp.set_cookie('jwt_token', jwt_token, httponly=True)  # 设置HTTP Only Cookie
        return resp
    else:
        return 'Authentication failed', 401

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

3. 访问控制

描述: 用户携带JWT访问其他系统,服务端验证JWT有效性。

代码示例: 使用Flask验证JWT。

代码语言:python
代码运行次数:0
复制
# 装饰器,用于保护需要JWT验证的路由
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.cookies.get('jwt_token')  # 从Cookie中获取JWT
        if not token:
            return 'Token is missing', 403

        try:
            # 解码JWT,验证有效性
            payload = jwt.decode(token, 'YOUR_SECRET_KEY', algorithms=['HS256'])
        except jwt.ExpiredSignatureError:
            return 'Token has expired', 403
        except jwt.InvalidTokenError:
            return 'Invalid token', 403

        return f(*args, **kwargs)
    return decorated

@app.route('/protected')
@token_required
def protected_resource():
    return 'You have accessed a protected resource!'

4. 刷新机制

描述: JWT设置过期时间,并提供刷新机制。

代码示例: 使用Flask实现刷新令牌。

代码语言:python
代码运行次数:0
复制
# 刷新令牌的函数
def refresh_token():
    # 假设从数据库或会话中获取用户信息
    user_id = 1  # 假设的用户ID
    return generate_jwt(user_id)

@app.route('/refresh')
def refresh():
    token = request.args.get('token')
    if token:
        try:
            # 验证传入的token是否有效
            payload = jwt.decode(token, 'YOUR_SECRET_KEY', algorithms=['HS256'])
            return refresh_token()  # 返回新的JWT
        except jwt.InvalidTokenError:
            return 'Invalid token', 403
    return 'No token provided', 400

登录时如何把之前的设备挤掉

在单点登录系统中,当用户在新设备上登录时,可能需要将之前的设备上的会话挤掉,以确保安全性。以下是一些实现策略:

1. 会话管理

详细策略: 建立一个中心化的会话存储,可以是一个数据库或分布式缓存系统,用于跟踪每个用户的活跃会话及其设备标识。每当用户登录时,系统检查该用户的现有会话并根据需要更新或创建新会话。

代码示例:

代码语言:python
代码运行次数:0
复制
# 假设使用Redis作为会话存储
import redis

r = redis.Redis()

def create_or_update_session(user_id, device_id, session_token):
    # 存储会话,key为用户ID和设备ID的组合
    r.set(f"session:{user_id}:{device_id}", session_token, ex=3600)  # 设置1小时过期

def get_session(user_id, device_id):
    # 根据用户ID和设备ID获取会话
    return r.get(f"session:{user_id}:{device_id}")

def invalidate_all_sessions_except(user_id, current_device_id, session_token):
    # 遍历用户所有会话,除了当前设备外,其他全部使无效
    current_session = f"session:{user_id}:{current_device_id}"
    sessions = r.scan(match=f"session:{user_id}:*", count=100)  # 获取所有会话
    for session_key in sessions[1]:
        if session_key != current_session:
            r.delete(session_key)  # 删除旧会话

2. 刷新令牌

详细策略: 为每个用户会话生成一个唯一的刷新令牌,存储在安全的地方(如服务器端数据库)。当用户从新设备登录时,使旧设备的刷新令牌失效。

代码示例:

代码语言:python
代码运行次数:0
复制
def refresh_session(user_id, new_refresh_token):
    # 更新用户的刷新令牌
    r.set(f"refresh_token:{user_id}", new_refresh_token, ex=7200)  # 设置2小时过期

def revoke_old_refresh_token(user_id, new_refresh_token):
    # 使旧刷新令牌无效
    old_refresh_token = r.get(f"refresh_token:{user_id}")
    if old_refresh_token and old_refresh_token != new_refresh_token:
        r.delete(f"refresh_token:{user_id}")  # 删除旧刷新令牌

3. 令牌黑名单

详细策略: 实现一个黑名单系统,用于存储被撤销的令牌。在验证JWT时,首先检查令牌是否在黑名单中。

代码示例:

代码语言:python
代码运行次数:0
复制
def add_to_blacklist(jwt_token):
    # 将JWT添加到黑名单
    r.sadd("blacklist", jwt_token)

def check_token_in_blacklist(jwt_token):
    # 检查JWT是否在黑名单中
    return r.sismember("blacklist", jwt_token)

4. 强制重新认证

详细策略: 当检测到用户从新设备登录时,要求用户完成多因素认证或发送一次性密码到用户的已验证邮箱或手机。

代码示例:

代码语言:python
代码运行次数:0
复制
def force_reauthentication(user_id):
    # 发送一次性密码到用户的邮箱或手机
    # 这里只是一个示例,实际实现需要集成邮件或短信服务
    send_otp_to_user(user_id)  # 假设这是发送OTP的函数

5. 通知机制

详细策略: 当用户的会话被挤掉时,通过电子邮件、短信或应用内通知等方式,及时通知用户。

代码示例:

代码语言:python
代码运行次数:0
复制
def notify_user(user_id, message):
    # 发送通知给用户
    # 这里只是一个示例,实际实现需要集成邮件或短信服务
    send_email_to_user(user_id, message)  # 假设这是发送邮件的函数

在您的文章最后,关于JWK(JSON Web Key)和JWKS(JSON Web Key Set)的补充可以这样写:

JWK和JWKS简介

除了JWT本身,JWK和JWKS也是在处理JWT时经常使用的概念,它们为JWT的安全性和灵活性提供了额外的支持。

JWK(JSON Web Key) 是一种JSON数据结构,用于表示公钥或私钥。JWK的格式允许在网络应用间安全地传输和存储密钥信息,而不需要直接暴露密钥的原始格式。

JWKS(JSON Web Key Set) 是一个JWK的集合,通常用于存储多个密钥,并且可以动态地添加、更新或删除密钥。JWKS常用于需要使用多个密钥进行签名或验证的场景,例如在多租户应用中。

使用JWK和JWKS的好处
  • 密钥管理:JWKS提供了一种集中管理密钥的方式,使得密钥的更新和轮换更加容易。
  • 动态密钥使用:在需要使用不同密钥签署或验证JWT的情况下,JWKS可以动态地选择适当的密钥。
  • 安全性:通过JWKS,可以在不暴露原始密钥的情况下,安全地传输和使用密钥。

以下是使用Python pyjwt 库和 cryptography 库操作JWK和JWKS的示例:

代码语言:python
代码运行次数:0
复制
from jwt import jwk_from_pem
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives import serialization

# 从PEM格式的私钥文件加载私钥
with open('private.pem', 'rb') as key_file:
    private_key = load_pem_private_key(
        key_file.read(),
        password=None,
        backend=serialization.NoBackend()
    )

# 将私钥转换为JWK
jwk = jwk_from_pem(private_key)

# 打印JWK信息
print('JWK:', jwk)

# 假设有多个JWK,可以创建JWKS
jwks = {
    'keys': [jwk]  # JWKS包含一个或多个JWK
}

# 打印JWKS信息
print('JWKS:', jwks)

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JWT在Web应用中的安全登录鉴权与单点登录实现
    • 登录鉴权功能与JWT的好处
      • 如何防止JWT TOKEN被篡改
        • 1. 使用HTTPS
        • 2. 签名
        • 3. 密钥管理
        • 4. 算法选择
        • 5. 验证算法
        • 6. 访问控制
        • JWT TOKEN如何实现单点登录(SSO)
        • JWT TOKEN实现单点登录(SSO)的Python代码和案例
        • 1. 身份验证
        • 2. 存储会话
        • 3. 访问控制
        • 4. 刷新机制
      • 登录时如何把之前的设备挤掉
        • 1. 会话管理
        • 2. 刷新令牌
        • 3. 令牌黑名单
        • 4. 强制重新认证
        • 5. 通知机制
        • JWK和JWKS简介
    相关产品与服务
    云服务器
    云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档