第 82 天:Python Web 开发之 JWT 简介

简介: 第 82 天:Python Web 开发之 JWT 简介

在之前的课程中,介绍过 Flask-Login 框架,它是基于 Session 和 Cookie 技术来实现用户授权和验证的,不过 Session 有很多的局限性,这一节介绍一种基于 token 的验证方式 —— JWT (JSON Web Token),除了对 JWT 的概念讲解之外,还有在 Flask 中简单实践


session 的局限性


基于 Session 的验证过程大体是:服务器端有一个 Session 词典,当用户验证登录后,在词典中为该用户创建一个 Session 对象,在响应( response )中返回一个 Session id,当用户下次请求时,携带 Session id,服务器从 Session 词典中可以恢复出 Session 对象,以完成用户的验证,在用 Session id 从恢复出认证实体。


从 Session 验证过程可以看出一些局限性:


  • 服务器横向扩展很困难:因为 Session 只能存活在一个服务实例中,将用户请求引导到其他服务器,将丢掉用户的登录状态



  • 携带信息量少,恢复会话信息比较耗时:Session 认证后,客户端得到 Session ID, 服务器无法从 Session ID 中得到更多信息,需要从数据库、文件系统或缓存中取得用户信息,比较耗时


  • 没有统一标准:Session 由各个服务器框架自己实现,没有统一标准,存在应用扩展困难的问题,特别加密方式,五花八门,有很大的安全隐患


token 简介


为了解决 Session 的问题,有了 token 的验证方式。


token 可以理解成票据,或者凭证,当用户得到服务器的认证后,由服务器颁发,在之后的请求时携带,免去频繁登录。


token 不同于 Session 的地方:


  • 可以独立于具体的服务器框架生成和校验



  • 可以携带更多的信息,避免对持久层的查询操作


  • 基于标准的算法可以由不同的节点完成验证



为了利用好 token 的验证机制,IEIT (互联网工程任务组),制定了基于 JSON 数据结构的网络认证方式 JWA(JSON Web Algorithms),还针对不同应用场景提出了具体协议,如 JWS、JWE、JWK 等,他们可以统称为 JWT,即 Javascript Web Token。


理解 JWA


JWA 的全称是 JSON Web Algorithms


JSON 是 Javascript 的语言的文本对象表示法,是一种独立语言环境的数据结构表示,可以用网络数据传输,在前面 RESTful 章节中,对 API 调用的返回数据格式就是 JSON。

Algorithms 本义是算法的意思,这里特指加密算法,也就是用 JSON 表示的数据,经过加密后在在服务器端和客户段之间传输。


有了数据结构和加密算法的基础,根据不同的应用场景,定义出了具体实现:


  • JWS(JSON Web Signature)对数据进行签名的,用于防止数据被篡改,传输不敏感数据的情况


  • JWE(JSON Web Encryption)对数据做了加密的,用于传输敏感数据,具有更好的安全性


  • JWK(JSON Web Key)是通过密钥对数据进行加密的方法,规定了相应的加密算法

JWT(JSON Web Token)上面 JWS、JWE 和 JWK 的总称。



JWT 简介


JWT Wiki 上的定义是:


JSON Web Token is an Internet standard for creating JSON-based access tokens that assert some number of claims.




大致意思是,JWT 是用基于 JSON 数据结构的生成包含了一些权限声明的网络访问凭证的网络标准


数据结构


JWT 由 HeaderPayloadSignature,三部分组成,像这样的形式:


eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRobGliIiwic3ViIjoiMTIzIiwibmFtZSI6ImJvYiJ9.cBo6e7Uss5__16mlqZECjHJSKJDdyisevDP5cUGvJms

换行符只是为了展示用,实际 token 中不包括换行符


Header

用于指定采用的加密算法,以及 JWT 采用的形式类型,例如:


{    "alg" : "HS256",    "typ" : "JWT"}
  • alg 指定前面所用的算法,默认为 HmacSHA256 简写为 HS256,还有 HS384、RS256 等


  • typ 是指令牌的类型,JWT 令牌的类型为 JWT


Payload

用于携带一些信息,例如用户名,过期时间 等等,例如:


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


JWT 标准定义了 7 个字段:

字段 说明
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

这些字段有实现这自由选取,也可以加入其他自定义字段


Signature


首先,需要指定一个密钥(secret)。密钥很重要,需要严格保密

然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名:



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


即先将 headerpayload 分别做 base64url 编码, 然后用 . 将他们连接成一个字符串,用加密算法,使用密钥 secret, 得到的加密结果就算签名


Base64URL 编码字符集是 Base64 字符集的子集

= 被省略、+ 替换成 -/ 替换成_

因为 token 可能通过 URL 进行传输,而=+/ 在 URL 中有特殊含义


验证


当客户端发送请求时将 token 送到服务器端,可以用和签名同样的方式,重新计算一次签名,如果和客户端送过来的签名一致,说明 token 没有被篡改,如果不一致,说明 token 已被篡改,不安全了。


由此可见,用于做签名的密钥 secret 很重要,一旦泄漏,将无法鉴别 token 的真伪


JWT 应用


关于 Python 的 JWT 实现不止一个,不同的库,不同的实现方式层出不穷,今天要讲解的是 Python 的 Authlib 库,它是一个大而全的 Python Web 验证库支持多种 Python 框架


Authlib 的 JWT


Authlib 是构建 OAuth 和 OpenID 安全连接服务器的终极 Python 库,包括了 JWS, JWE, JWK, JWA, JWT


Authlib 功能强大而丰富,今天我们只了解他的 JWT 部分,之后在介绍基于第三方认证的 OAuth 技术时还会进一步讲解


安装

使用 pip 安装


pip install Authlib

如果一切正常,可以导入 Authlib 模板,例如,引入 jwt :


>>> from authlib.jose import jwt>>>


小试牛刀


JWT 是服务器端的机制,所以可以在命令行中做测试


生成 token


>>> from authlib.jose import jwt>>> header = {'alg': 'HS256'}>>> payload = {'iss': 'Authlib', 'sub': '123', 'name': 'bob'}>>> secret = '123abc.'>>> token = jwt.encode(header, payload, secret)>>> print(token)b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRobGliIiwic3ViIjoiMTIzIiwibmFtZSI6ImJvYiJ9.cBo6e7Uss5__16mlqZECjHJSKJDdyisevDP5cUGvJms'
  • 导入 jwt 模块
  • 定义 header,并且设置签名算法为 HS256
  • 定义 payload,作为传输信息
  • 定义 secret,注意这里只是方便演示,实际项目中最好是随机生成,并妥善保存
  • 使用 jwt 的 encode 方法,生成 token,encode 方法一次性实现了所有关于 JWT 协议的定义
  • 打印出 token,可见,被 . 分隔为三部分,前两部分是 headerpayload的 Base64Url 编码,最后一部分是 签名


解码 token

接上面的环境:


>>> claims = jwt.decode(token, secret)>>> print(claims){'iss': 'Authlib', 'sub': '123', 'name': 'bob'}>>> print(claims.header){'alg': 'HS256', 'typ': 'JWT'}>>> claims.validate()>>>
  • 用 jwt 模块的 decode 方法,利用 secrettoken 进行解码,如果签名正确,就会得到解码内容,解码对象是 authlib.jose.JWTClaims 类的实例
  • 打印出解码内容,可以看到和生成 token 时的 payload 内容一致
  • 打印出 header,可以看到 typJWT,即使用默认值
  • validate 方法用于检验 token 的有效性,比如:是否过期、主题是否一致,是否没到生效时间等等,也可以针对每种情况单独做验证,例如validate_exp 可用检验是否过期


虽然 JWT 理论很繁琐,但 Authlib 库提供了简洁的方法,让开发应用变得更高效


与客户端交互


JWT 之所有流行,有个重要原因是可以支持多种客户端,例如 浏览器和 app,JWT 标准规定,一般情况下,客户端需要将 token 放在 Http 请求的 Header 中的 Authorization 字段中,举个例子:


GET /resource HTTP/1.1     Host: server.example.com     Authorization: Bearer mF_9.B5f-4.1JqM


  • 用 GET 方式请求 /resource ,在 Header 中添加了 Authorization 字段
  • 不能直接将 token 作为 Authorization的值,必须有类型声明,这里是Bearer


Bearer 表示这个 token 是认证服务器生成的,用来做身份识别的,除此之外,IEIT 还定义了其他 认证类型,如 Bisic, Digest,可以简单理解成 Bearer 就是 JWT 的认证类型


除了通过 Http Header 类携带 token 之外,还可以通过 POST 请求主体,以及 URL 中的 querystring 来向服务器发送 token,这两种情况下,需要使用 access_token 字段来表示 token


JWT 标准建议使用 Header 方式,除非 Header 无法使用时才考虑其他方式


Flask JWT


Authlib 主要的用途在打造一个 OAuth 应用,对于单独做 JWT 的实践有些麻烦,因此我们用 flask-jwt 框架,做 JWT 的实践。


flask-jwt 和之前讲述的 flask-login 用法很像,是基于 JWT 的认证的框架,提供和很多方便实践的特性


安装 flask-jwt


pip install Flask-JWT


创建应用


为了简单,将所有代码放在 app.py 中:



from flask import Flaskfrom flask_jwt import JWT, jwt_required, current_identityfrom werkzeug.security import safe_str_cmp
# User 类,用于模拟用户实体class User(object):    def __init__(self, id, username, password):        self.id = id        self.username = username        self.password = password
    def __str__(self):        return "User(id='%s')" % self.id
# User 实体集合,用于模拟用户对象的缓存users = [    User(1, 'user1', 'abcxyz'),    User(2, 'user2', 'abcxyz'),]
username_table = {u.username: u for u in users}userid_table = {u.id: u for u in users}
# 获取认证的回调函数,从 request 中得到登录凭证,返回凭证所代表的 用户实体def authenticate(username, password):    user = username_table.get(username, None)    if user and safe_str_cmp(user.password.encode('utf-8'), password.encode('utf-8')):        return user
# 通过 token 获得认证主体的回调函数def identity(payload):    user_id = payload['identity']    return userid_table.get(user_id, None)
app = Flask(__name__)app.debug = Trueapp.config['SECRET_KEY'] = 'super-secret'
jwt = JWT(app, authenticate, identity)  # 用 JWT 初始化应用
@app.route('/protected', methods= ["GET", "POST"])  # 定义一个 endpoint@jwt_required()  # 声明需要 token 才能访问def protected():    return '%s' % current_identity  # 验证通过返回 认证主体
if __name__ == '__main__':    app.run()

运行:



$ python app.py * Serving Flask app "app" (lazy loading) * Environment: production   WARNING: This is a development server. Do not use it in a production deployment.   Use a production WSGI server instead. * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 566-326-511


获取 access_token


flask-jwt 默认的获取 token 的路由是/auth,请求方式是 POST,用 JSON 传送用户名密码给服务器,例如:



$ curl -X POST -H "Content-Type: application/json" localhost:5000/auth -d '{"username":"user1","password":"abcxyz"}'{  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.  eyJleHAiOjE...<省略>...VudGl0eSI6MX0.  M-shnDPAVdu...<省略>...LaH1EMIbrWjPto"}

如果登录凭证正确,则返回 access_token,可以看到被 . 分隔成三部分,即 JWT 的结构


使用 access_token


flask-jwt 默认通过 Header 传送 token,为了和 OAuth 生成的 JWT 做区分,默认使用JWT 作为 token 的类型,例如,用上面生成的 JWT 请求 /protected


curl -H "Authorization: jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE...<省略>...VudGl0eSI6MX0.M-shnDPAVdu...<省略>...LaH1EMIbrWjPto" localhost:5000/protectedUser(id='1')


如果 token 有效,则返回 token 对应的认证实体,这个例子中打印出了 user 实体


总结


本节课程讲解了基于 token 验证的 JWT,使用 Authlib 库对 JWT 做了实践练习,期望能帮助您更好的理解 JWT,最后通过 flask-jwt 模块,实践了 JWT 的验证方式,和使用方式。在后续的课程中还会对目前流行的第三方认证框架 OAuth 做介绍,敬请期待。


参考

示例代码:Python-100-days

目录
相关文章
|
1月前
|
算法 Java Go
【GoGin】(1)上手Go Gin 基于Go语言开发的Web框架,本文介绍了各种路由的配置信息;包含各场景下请求参数的基本传入接收
gin 框架中采用的路优酷是基于httprouter做的是一个高性能的 HTTP 请求路由器,适用于 Go 语言。它的设计目标是提供高效的路由匹配和低内存占用,特别适合需要高性能和简单路由的应用场景。
168 4
|
3月前
|
存储 监控 算法
淘宝买家秀 API开发实录Python(2025)
本文讲述了作者在电商开发领域,尤其是对接淘宝买家秀 API 接口过程中所经历的挑战与收获。从申请接入、签名验证、频率限制到数据处理和实时监控,作者分享了多个实战经验与代码示例,帮助开发者更高效地获取和处理买家秀数据,提升开发效率。
|
2月前
|
设计模式 人工智能 API
AI智能体开发实战:17种核心架构模式详解与Python代码实现
本文系统解析17种智能体架构设计模式,涵盖多智能体协作、思维树、反思优化与工具调用等核心范式,结合LangChain与LangGraph实现代码工作流,并通过真实案例验证效果,助力构建高效AI系统。
377 7
|
5月前
|
缓存 JavaScript 前端开发
鸿蒙5开发宝藏案例分享---Web开发优化案例分享
本文深入解读鸿蒙官方文档中的 `ArkWeb` 性能优化技巧,从预启动进程到预渲染,涵盖预下载、预连接、预取POST等八大优化策略。通过代码示例详解如何提升Web页面加载速度,助你打造流畅的HarmonyOS应用体验。内容实用,按需选用,让H5页面快到飞起!
|
5月前
|
JavaScript 前端开发 API
鸿蒙5开发宝藏案例分享---Web加载时延优化解析
本文深入解析了鸿蒙开发中Web加载完成时延的优化技巧,结合官方案例与实际代码,助你提升性能。核心内容包括:使用DevEco Profiler和DevTools定位瓶颈、四大优化方向(资源合并、接口预取、图片懒加载、任务拆解)及高频手段总结。同时提供性能优化黄金准则,如首屏资源控制在300KB内、关键接口响应≤200ms等,帮助开发者实现丝般流畅体验。
|
前端开发 JavaScript Shell
鸿蒙5开发宝藏案例分享---Web页面内点击响应时延分析
本文为鸿蒙开发者整理了Web性能优化的实战案例解析,结合官方文档深度扩展。内容涵盖点击响应时延核心指标(≤100ms)、性能分析工具链(如DevTools时间线、ArkUI Trace抓取)以及高频优化场景,包括递归函数优化、网络请求阻塞解决方案和setTimeout滥用问题等。同时提供进阶技巧,如首帧加速、透明动画陷阱规避及Web组件初始化加速,并通过优化前后Trace对比展示成果。最后总结了快速定位问题的方法与开发建议,助力开发者提升Web应用性能。
|
5月前
|
JSON 开发框架 自然语言处理
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(三)
本文主要介绍了应用开发中的三大核心内容:生命周期管理、资源限定与访问以及多语言支持。在生命周期部分,详细说明了应用和页面的生命周期函数及其触发时机,帮助开发者更好地掌控应用状态变化。资源限定与访问章节,则聚焦于资源限定词的定义、命名规则及匹配逻辑,并阐述了如何通过 `$r` 引用 JS 模块内的资源。最后,多语言支持部分讲解了如何通过 JSON 文件定义多语言资源,使用 `$t` 和 `$tc` 方法实现简单格式化与单复数格式化,为全球化应用提供便利。
241 104
|
5月前
|
JavaScript 前端开发 API
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(二)
本文介绍了HarmonyOS应用开发中的HML、CSS和JS语法。HML作为标记语言,支持数据绑定、事件处理、列表渲染等功能;CSS用于样式定义,涵盖尺寸单位、样式导入、选择器及伪类等特性;JS实现业务逻辑,包括ES6语法支持、对象属性、数据方法及事件处理。通过具体代码示例,详细解析了页面构建与交互的实现方式,为开发者提供全面的技术指导。
257 104
|
5月前
|
开发框架 编解码 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(三) -> 兼容JS的类Web开发(一)
该文档详细介绍了一个兼容JS的类Web开发范式的方舟开发框架,涵盖概述、文件组织、js标签配置及app.js等内容。框架采用HML、CSS、JavaScript三段式开发方式,支持单向数据绑定,适合中小型应用开发。文件组织部分说明了目录结构、访问规则和媒体文件格式;js标签配置包括实例名称、页面路由和窗口样式信息;app.js则描述了应用生命周期与对象管理。整体内容旨在帮助开发者快速构建基于方舟框架的应用程序。
252 102
|
3月前
|
算法 程序员 API
电商程序猿开发实录:淘宝商品python(2)
本文分享了开发者在对接淘宝商品详情API过程中的真实经历,涵盖权限申请、签名验证、限流控制、数据解析及消息订阅等关键环节,提供了实用的Python代码示例,帮助开发者高效调用API,提升系统稳定性与数据处理能力。

推荐镜像

更多