flask 之JWT认证实现

目录

1、JWT

1.1、JWT概述

1.2、token的生成

1.3、token校验

1.4、flask项目中实现JWT认证

1、JWT

1.1、JWT概述

JWT(JSON Web Token)是一种用于身份验证和授权的开放标准。它由三部分组成,分别是头部、负载和签名。

头部(Header)是一个 JSON 对象,描述了使用的算法和类型。它通常包含两个字段:算法(alg)和类型(typ)。

负载(Payload)也是一个 JSON 对象,用于存储用户的相关信息。它可以包含一些预定义的字段,如过期时间(exp)、发布时间(iat)等,也可以包含一些自定义字段。

签名(Signature)使用指定的算法对头部和负载进行加密,生成一段字符串。这个字符串可以用于验证数据的完整性。

1、传统的token认证流程

  1. 用户登录:用户输入用户名和密码,向服务器发送登录请求。
  2. 生成token:服务器验证用户名和密码的正确性,如果验证通过,则生成一个随机字符串作为token,并将这个token与用户ID等关键信息关联起来(例如,将token和用户ID作为键值对存储在Redis等缓存中)。
  3. 返回token:服务器将生成的token返回给客户端,客户端将token保存在cookie或localStorage中。
  4. 携带token:当客户端需要访问需要认证的接口时,会在请求头中携带这个token。
  5. 验证token:服务器在接收到请求后,会从请求头中提取token,并根据token在缓存中查找对应的用户信息。如果找到了对应的用户信息,则说明用户已经登录且token有效;否则,说明用户未登录或token已过期。

2、传统token认证流程和JWT的对比

  1. 存储方式:传统token方式需要将token保存在服务器端(如Redis),而JWT则将token的所有信息都包含在token本身中,无需在服务器端保存。
  2. 安全性:JWT通过签名机制来确保数据的完整性和防止篡改,而传统token方式则更依赖于服务器端的验证。
  3. 应用场景:JWT更适用于分布式系统或跨域请求的场景,而传统token方式则更适用于传统的Web应用。

1.2、token的生成

jwt的生成token格式如下,即:由 . 连接的三段字符串组成。

eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9.eyJ1c2VybmFtZSI6ImxqIiwiZXhwIjoxNzE2NzkyODI4fQ.WrT1XY8CC1vFo8tPpt0ZrDjje5ooD8hTOvIW42Z2WEo

1、生成规则如下:

  • token组成:将三段字符串通过 . 拼接起来就生成了jwt的token。
  • 第一段HEADER部分,固定包含算法和token类型,对此json进行base64url加密,这就是token的第一段。
headers = {
    'typ': 'jwt',
    'alg': 'HS256'
}
  • 第二段PAYLOAD部分,包含一些数据,对此PAYLOAD进行base64url加密,这就是token的第二段
payload = {
    'user_id':1,
    'username':'张三', 
    'role':'admin',
    'exp': "XXX"
    ...........
}
  • 第三段SIGNATURE部分,把前两段的base密文通过.拼接起来,然后对其进行HS256加密,再然后对hs256密文进行base64url加密,最终得到token的第三段。
base64url(
    HMACSHA256(
      base64UrlEncode(headers) + "." + base64UrlEncode(payload),
      secret # 秘钥加盐
    )
)

注意:base64url加密是先做base64加密,然后再把 - 替换 + _ 替换 / 。

2、代码实现:

  • 使用的库是:pyjwt模块。
  • 安装库:

pip install pyjwt

代码实现:

import jwt
import datetime

# 秘钥加盐
SALT = 'adasfasfgthfafklsaj?lkasld;)(hjasgbsvbfjh='

def create_token():
    # header
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    # payload
    payload = {
        'user_id': 1,  # 自定义用户ID
        'username': '张三',  # 自定义用户名
        'role': "admin", # 自定义角色
        'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=2)  # 过期时间(2个小时)
    }
    result = jwt.encode(headers=headers, payload=payload, key=SALT, algorithm=["HS256"])
    return result

if __name__ == '__main__':
    token = create_token()
    print(token)

1.3、token校验

获取token之后,会按照以下步骤(主要是进行超时和合法性校验)进行校验:

  • 将token分割成 header_segment、payload_segment、crypto_segment 三部分
  • 对第一部分header_segment进行base64url解密,得到header
  • 对第二部分payload_segment进行base64url解密,得到payload
  • 对第三部分crypto_segment进行base64url解密,得到signature后进行合法性校验。
    • 拼接前两段密文,即:signing_input
    • 从第一段明文中获取加密算法,默认:HS256
    • 使用 算法+盐 对signing_input 进行加密,将得到的结果和signature密文进行比较。

代码实现:

import jwt

# 秘钥加盐
SALT = 'adasfasfgthfafklsaj?lkasld;)(hjasgbsvbfjh='

def get_payload(token):
    try:
        # 校验token合法性和是否超时
        verified_payload = jwt.decode(jwt=token, key=SALT, verify=True, algorithms=["HS256"])
        return verified_payload
    except jwt.ExpiredSignatureError:
        print("token已过期")
    except jwt.DecodeError:
        print('token认证失败')
    except jwt.InvalidTokenError:
        print('非法的token')

if __name__ == '__main__':
    token = "eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Ilx1NWYyMFx1NGUwOSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxNzQwMDIwMn0.iXqTAykKdA8ln0WMCsdhcc3Je0l4qOvYoxctkjEmhHs"
    payload = get_payload(token)
    print(payload)

1.4、flask项目中实现JWT认证

项目的目录结构如下:

1、app.py文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import base64
from flask import Flask, request, jsonify, g
from utils.jwt_auth import create_token, check_token

app = Flask(__name__)

# 通过url传递token
# 每一个请求前都进行token的验证
# @app.before_request
# def jwt_query_params_auth():
#     if request.path == '/login/':
#         return
#     token = request.args.get('token')
#     result = check_token(token)
#     if not result['status']:
#         return jsonify(result)
#     g.user_info = result['info']


# 通过Authorization请求头传递token


@app.before_request
def jwt_authorization_auth():
    if request.path == '/login/':
        return
    # 在实际项目中Authorization的组成一般是: Bearer toekn
    authorization = request.headers.get('Authorization', '')
    token = ""
    # 对head进行解密
    if authorization:
        # 获取token信息
        token = authorization.split(" ")[1]
        # 获取token中的header加密部分
        encoded_header = token.split(".")[0]
        # 注意:我们需要替换字符并确保字符串长度是 4 的倍数
        padded = encoded_header + '=' * (-len(encoded_header) % 4)
        # 对header进行base64url解密
        header = base64.urlsafe_b64decode(padded.replace('-', '+').replace('_', '/')).decode('utf-8')
        print(header)

    result = check_token(token)
    if not result['status']:
        return jsonify(result)
    g.user_info = result['info']



@app.route('/login/', methods=['POST'])
def login():
    user = request.form.get('username')
    pwd = request.form.get('password')
    print(user)
    # 检测用户和密码是否正确,此处可以在数据进行校验。
    if user == '张三' and pwd == '123456':
        # 用户名和密码正确,给用户生成token并返回
        token = create_token({'user_id':1, 'username':'张三', 'role':'admin'})
        return jsonify({'status': True, 'token': token})
    return jsonify({'status': False, 'error': '用户名或密码错误!'})


@app.route('/info/')
def order():
    print(g.user_info)
    return jsonify(g.user_info)


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

2、jwt_auth.py文件

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import jwt
import datetime

# 秘钥加盐
SALT = 'adasfasfgthfafklsaj?lkasld;)(hjasgbsvbfjh='

def create_token(payload, timeout=60):
    """
    :function: 创建token
    :param payload:  例如:{'user_id':1,'username':'张三', 'role':'admin'}用户信息
    :param timeout: token的过期时间,默认60分钟
    :return:
    """
    headers = {
        'typ': 'jwt',
        'alg': 'HS256'
    }
    payload['exp'] = datetime.datetime.utcnow() + datetime.timedelta(minutes=timeout)
    result = jwt.encode(headers=headers, payload=payload, key=SALT, algorithm="HS256")
    return result


def check_token(token):
    """
    :function: 检验token的合法性和是否过期
    :param token: 用户token信息
    :return: 返回检验结果
    """
    result = {'status': False, 'info': None, 'msg': None}
    try:
        print(token)
        verified_payload = jwt.decode(jwt=token, key=SALT, verify=True, algorithms=["HS256"])
        result['status'] = True
        result['info'] = verified_payload
    except jwt.ExpiredSignatureError:
        result['msg'] = 'token已失效'
    except jwt.DecodeError:
        result['msg'] = 'token认证失败'
    except jwt.InvalidTokenError:
        result['msg'] = '非法的token'
    return result
JWT (JSON Web Token) 是一种用于身份认证的标准。它是一种基于 JSON 的轻量级的身份验证和授权机制,通过对用户进行加密签名生成一个安全的 token,用于在双方之间传递信息。 下面是一个使用 JWT 进行认证的示例代码: 首先在 Flask 中安装 PyJWT: ``` pip install PyJWT ``` 然后在 Flask 中创建一个 auth.py 文件,用于处理用户认证: ```python import jwt from flask import request, jsonify, current_app def generate_token(user_id): payload = { 'user_id': user_id, } token = jwt.encode(payload, current_app.config['SECRET_KEY'], algorithm='HS256') return token.decode('utf-8') def authenticate(): auth_header = request.headers.get('Authorization') if not auth_header: return jsonify({'message': 'Authorization header is missing'}), 401 try: token = auth_header.split(" ")[1] payload = jwt.decode(token, current_app.config['SECRET_KEY'], algorithms=['HS256']) user_id = payload['user_id'] except jwt.ExpiredSignatureError: return jsonify({'message': 'Token has expired'}), 401 except jwt.InvalidTokenError: return jsonify({'message': 'Invalid token'}), 401 return user_id ``` 在上面的代码中,我们定义了两个函数: - `generate_token(user_id)`:用于生成一个 JWT token,其中包含 user_id 的信息。 - `authenticate()`:用于验证请求中的 JWT token,并返回 user_id 的信息。 我们可以在 Flask 中使用这些函数来进行身份认证: ```python from flask import Flask, jsonify from auth import generate_token, authenticate app = Flask(__name__) app.config['SECRET_KEY'] = 'your_secret_key' @app.route('/login', methods=['POST']) def login(): # 这里省略了用户登录的逻辑 user_id = get_user_id_from_database(username, password) token = generate_token(user_id) return jsonify({'token': token}) @app.route('/protected', methods=['GET']) def protected(): user_id = authenticate() # 根据 user_id 查询用户信息并返回 return jsonify({'user_id': user_id}) ``` 在上面的代码中,我们实现了两个路由: - `/login`:用于用户登录,验证用户名和密码后生成一个 JWT token。在实际应用中,我们可能需要使用更加安全的加密方式来存储用户的密码,例如 bcrypt。 - `/protected`:用于获取受保护的资源,需要在请求头中包含 JWT token。 如果用户在 `/protected` 路由中没有提供有效的 JWT token 或者 token 已经过期,将返回 401 错误码。如果 token 验证通过,则会返回用户的信息。 需要注意的是,在实际应用中,我们需要使用更加安全的方式来存储 SECRET_KEY,例如环境变量或者密钥管理工具。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值