1.文档地址
sanic-jwt
2.开始使用
from sanic_jwt import Initialize
async def authenticate(request):
return dict(user_id='some_id')
app = Sanic(__name__)
Initialize(app, authenticate=authenticate)
3.安装
pip install sanic-jwt
4.初始化
- 概念
- Sanic JWT通过Initialize创建Blueprint,并将一些路由附加到应用程序中
- Sanic JWT是用户认证系统,需要把用户管理系统绑定到Sanic JWT
- 尽量使用Initialize类,避免使用initialize方法,后续可能删除
- Initialize类
- Sanic JWT的入口,可配置运行的参数
- 初始化分为5个部分:
- A.sanin app/sanic blueprint的实例(必须)
- B.handler methods(其中authenticate必须)
- C.runtime configurations
- D.additional Views
- E.component overrides
- 初始化完成后,通过@protected装饰器,保护路由/Blueprint
- 可只保护指定的某个Blueprint
- handler methods详解
- Sanic JWT提供钩子函数,用户传入钩子函数完成相应的功能。大多函数都能使用普通函数/协程,特别注明的除外
- A.authenticate(必须)
- 功能:决定是否有一个有效的用户需要进行身份验证
- 返回类型:
- 包含user_id键的dict
- 包含id和to_dict属性的实例(默认使用实例的user_id查找id,可指定)
- 注意:如果不需要身份验证,应该抛出异常(AuthenticationFailed),而不应返回None,可能引起500异常
- B.store_refresh_token
- 功能:将刷新令牌持久化到磁盘
- 注意:非必需。通过设置refresh_token_enabled=True起用,如果起用但没有实现,将抛出RefreshTokenNotImplemented异常
- C.retrieve_refresh_token
- 功能:从磁盘检索刷新令牌
- 注意:非必需。通过设置refresh_token_enabled=True起用,如果起用但没有实现,将抛出RefreshTokenNotImplemented异常
- D.retrieve_user
- 功能:从应用程序检索用户对象
- 用于返回/auth/me接口/装饰器@inject_user中的用户对象
- 返回类型:
- E.add_scopes_to_payload
- 功能:向访问令牌添加scope。scope给程序提供许可(权限),同java shiro验证框架Permissions的功能
- F.override_scope_validator
- 功能:覆盖默认scope验证
- 注意:本功能必须是一个普通函数,不能使用异步编程
- G.destructure_scopes
- 功能:在scope验证之前调用,可以对scope进行一些预处理
- H.extend_payload
- 功能:在将负载打包到JWT中,可以对负载添加额外的声明。(负载是jwt保存的json信息。jwt分三部分:header头,payload载体,signature签名)
- runtime configurations(运行时配置)详解
- 有多种方法配置Sanic JWT设置。其中之一是初始化时将配置作为关键字参数
- additional Views详解
- 功能:可向身份验证系统添加额外的接口,这些接口并不通过默认的用户认证
- 实现:创建一个基于类的视图,实现处理程序(必须有options response),并映射一个接口名称
- component overrides详解
- 有些组件可以子类化进行控制:
- Authentication
- Configuration
- Responses
5.接口和响应(Endpoints and Responses)
- Endpoints(接口)
- Sanic JWT提供的接口会以/auth为访问路径前缀,可通过url_prefix更改
- Default Endpoints(默认有四个接口),可更改它们的路径:
- path_to_authenticate=’/my_authenticate’
- path_to_retrieve_user=’/my_retrieve_user’
- path_to_verify=’/my_verify’
- path_to_refresh=’/my_refresh’
- Authenticate
- 功能:检查用户是否有效,如果有效并返回true,则sanic jwt会生成访问令牌。访问路径/auth(post请求)
- Verification
- 功能:检查给定的访问令牌是否有效。访问路径/auth/verify(get请求)
- Current User Details
- 功能:检索当前已验证用户的信息。访问路径/auth/me(get请求)
- Refresh Token
- 功能:根据当前令牌获取一个新令牌。访问路径/auth/refresh(post请求)
- Modify Responses
- 默认接口的响应可以通过子类化并连接到适当的方法来扩展,必须返回dict。自定义响应应该在初始化时使用responses_class关键字参数连接到Sanic JWT
- Custom Endpoints
- Exception Handling
- 可以自定义Sanic JWT发生异常时的响应。通过子类化responses类并覆盖exception_respons
- Microservices(微服务)
- 只对单个服务进行身份验证,并在其它服务中使用生成的令牌(auth_mode=True)。每个服务必须具有相同secret
6.Payload(jwt有效载荷)
- Sanic JWT有效载荷三种主要用途:
- passing claims(传递claims,jwt有效载荷JSON的每一条包含键/值对的信息,被称为一个claim)
- passing scope(传递scope)
- passing arbitrary information to the client(将任意信息传递给客户机)
- jwt标准的五项claim
- exp:Expires过期时间,token有效期通常很短,默认开启。可通过verify_exp=False禁用过期时间
- aud:Audience面向的用户,默认关闭。可通过claim_aud='my_client_domain.com’设置
- iat:Issued at签发时间,默认无,可通过claim_iat=True开启
- iss:Issuer签发者,默认无,可通过claim_iss='my_server_domain.com’设置
- nbf:Not before在指定时间之后才启用token,默认无。可通过claim_nbf=True, claim_nbf_delta=(60 * 3)开启并设置启用时间
- jwt自定义的claim
- 功能:在jwt的payload添加额外的claim
- 实现:实现claim的子类,在custom_claims的列表中注册
- Extra Verifications(额外的验证)
- 功能:对负载进行额外的验证
- 实现:通过extra_veriations注册一个方法列表(每个方法返回一个布尔值)
- Payload Handlers(payload处理程序)
- Initialize可用于修改Payload处理程序,如下
- Adding Scopes(添加scope):add_scopes_to_payload
- Extending the payload(扩展payload):extend_payload
- Token signing(签名)
- 以密码方式验证:secret
- 使用非对称算法,须提供公钥和私钥来处理令牌的编码和解码:public_key,public_key
7.保护路由(Protecting Routes)
- 功能:保证只有具有有效访问令牌的用户才能访问指定资源。可以使用@protected装饰器保护应用程序中的路由
- @protected
- 使用
- 默认:@protected()
- 在蓝图上初始化Sanic JWT:
- 将蓝图的实例传递到@protected:@protected(blueprint)
- 从Initialize实例访问装饰器:@sanicjwt.protected()
- Class based views(保护基于类的视图)
app.add_route(ProtectedView.as_view(), '/protected')
- Passing the Token(传递令牌)
- Header Tokens(头部令牌,默认)
- 头部令牌通过在http协议的header中添加Authorization传递
- 组成
- 前缀(the word Bearer),可通过authorization_header_prefix修改
- 访问令牌(the JWT access token),可通过authorization_header修改
- Cookie Tokens(cookie令牌)
- 通过cookie_set=True开启
- 配置:
- cookie_domain 更改与cooke关联的域(默认为")
- cookie_httponly 是否在cookie上设置httponly标志(默认为True)
- cookie_access_token_name 存储cookie用于访问令牌的名称
- cookie_refresh_token_name 存储cookie用于刷新令牌的名称
注意:cookie令牌不要禁用cookie_httponly,禁用后在客户机上运行的任何javascript都可以访问令牌
- Query String Tokens(查询字符串令牌)
- 通过query_string_set=True启用。用于验证websocket认证
- Both Header and Cookie(头部令牌和cookie令牌同时使用)
- 首先采用cookie令牌,如果cookie不存在,则使用头部令牌
- 设置cookie_set=True和cookie_strict=False
- Per view declaration(每个视图单独指定令牌类型)
- @protected(cookie_set=False)
- Advanced Decorators(高级装饰器)
sanicjwt = Initialize(app)
@sanicjwt.protected()
async def protected_route(request):
bp = Blueprint('Users')
@bp.protected()
async def users(request, id):
- 注意:基于实例的装饰器的概念也适用装饰器和@inject_user装饰器
8.访问权限(Scopes)
# **需要user的scope
@scoped('user')
# **需要user和admin的scope
@scoped(['user', 'admin'])
# **需要user或admin的scope
@scoped(['user', 'admin'], False)
# **Blueprint中使用
bp = Blueprint('Users')
Initialize(bp)
@bp.get('/users/<id>')
@scoped(['user', 'admin'], initialized_on=bp)
async def users(request, id):
...
- 参数(Parameters)
- scopes:必须
- 字符串
- 字符串列表
- 返回上述值的同步/异步函数
- requires_all:默认true,可选。true表示需要匹配所有的scope,false表示只匹配其中之一即可
- require_all_actions:默认true,可选。true表示需要匹配所有的action,false表示只匹配其中之一即可
- 处理程序(Handler)
- 通过add_scopes_to_payload添加scope到jwt payload
9.刷新令牌(Refresh Tokens)
- 问题:
- 访问令牌一旦发放在有效期期间不能被撤销
- 访问令牌的寿命很短,每次访问令牌过期,客户端都需重新进行身份验证
- 刷新令牌运行过程
- Refresh Tokens是由服务器存储的令牌。客户端随时可向服务器发送refresh令牌并请求新的访问令牌
- 服务器接受refresh令牌,在存储中查找是否合法,是则生成新的访问令牌
- 刷新令牌和访问令牌一起工作,以提供用户友好且安全的身份验证环境
- (配置)Configuration
- Sanic JWT实现了刷新令牌的创建和传递。和身份验证一样,令牌的存储和检索需要开发人员实现。开发人员可以决定如何持久化令牌,何时停用令牌。
- 使用步骤
- refresh_token_enabled:启用刷新令牌,默认关闭,设置为true开启
- store_refresh_token:存储刷新令牌,可以是同步/异步函数
# **把刷新令牌保存到redis中
# **第一个参数为user_id或者authenticate方法返回的user对象
# **第二个参数为刷新令牌
async def store_refresh_token(user_id, refresh_token, *args, **kwargs):
key = 'refresh_token_{user_id}'
await aredis.set(key, refresh_token)
- retrieve_refresh_token:检索刷新令牌
# **从redis中检索刷新令牌
# **第一个参数为请求对象
# **第二个参数为user_id或者authenticate方法返回的user对象
async def retrieve_refresh_token(request, user_id, *args, **kwargs):
key = f'refresh_token_{user_id}'
return await aredis.get(key)
- 使用刷新令牌(Using the refresh token)
- 需要访问refresh令牌接口,以获得一个新的访问令牌
- 有效期的刷新令牌(Can I have an expirable refresh token?)
- Sanin JWT不支持有效期的刷新令牌
- 原因
- 刷新令牌(utf-8编码,24个字符),不是JWT,没有payload,不受验证约束
- 开发人员决定是否接受refresh令牌
- 想使令牌过期,可在程序中处理
- 实现:重写generate_refresh_token,生成自已的token,并在retrieve_refresh_token中进行验证
10.异常(Exceptions)
- AuthenticationFailed
- MissingAuthorizationHeader
- MissingAuthorizationCookie
- InvalidAuthorizationHeader
- MissingRegisteredClaim
- Unauthorized
11.配置(Configuration)