通过 Flask-JWT REST API 使用 Flask-Security 角色

Posted

技术标签:

【中文标题】通过 Flask-JWT REST API 使用 Flask-Security 角色【英文标题】:Using Flask-Security Roles with Flask-JWT REST API 【发布时间】:2016-02-09 09:24:33 【问题描述】:

我正在构建一个基于 Flask 的 REST API 并使用 Flask-JWT 来处理 JWT 身份验证。我还想使用 Flask-Security 的内置角色管理。但是,Flask-Security 的 @roles_required() 装饰器假定我在失败时显示 Flask 视图。

这是我的令牌端点(按我的意愿工作):

$ http POST localhost:5000/auth/token username='test' password='test'
HTTP/1.0 200 OK
Content-Length: 192
Content-Type: application/json
Date: Sun, 08 Nov 2015 17:45:46 GMT
Server: Werkzeug/0.10.4 Python/3.5.0


    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ"

这是对没有任何角色要求的资源的成功响应(仅使用@jwt_required)这也可以按我的意愿工作:

$http GET localhost:5000/protected Authorization:'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ'
HTTP/1.0 200 OK
Content-Length: 25
Content-Type: text/html; charset=utf-8
Date: Sun, 08 Nov 2015 17:46:24 GMT
Server: Werkzeug/0.10.4 Python/3.5.0

<models.User[email=test]>

当我对具有所需角色的资源(例如本例中的管理员)执行相同操作时,似乎假设我有一个要显示的页面,例如 /login,但我没有,因为它是一个无头 REST API:

$ http GET localhost:5000/admin Authorization:'JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE0NDcwMDQ3NDYsIm5iZiI6MTQ0NzAwNDc0NiwiZXhwIjoxNDQ3MDA1MDQ2LCJpZGVudGl0eSI6MX0.RFIeaLuvJNM9fDjFYFQ7sh_WaDVU-_aM7e46tVJzlBQ'
HTTP/1.0 302 FOUND
Content-Length: 209
Content-Type: text/html; charset=utf-8
Date: Sun, 08 Nov 2015 17:46:43 GMT
Location: http://localhost:5000/
Server: Werkzeug/0.10.4 Python/3.5.0
Set-Cookie: session=eyJfZmxhc2hlcyI6W3siIHQiOlsiZXJyb3IiLCJZb3UgZG8gbm90IGhhdmUgcGVybWlzc2lvbiB0byB2aWV3IHRoaXMgcmVzb3VyY2UuIl19XX0.CSEcAw.pjwXLeSWUsORXR-OU5AfFvq6ESg; HttpOnly; Path=/

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to target URL: <a href="/">/</a>.  If not click the link.

我知道 Flask-Security 在幕后使用 Flask-Principal 进行角色管理(@roles_required 等),它与数据存储区的 RoleMixin 和 UserMixin 相关联,这非常好。但是,如果没有办法让 Flask-Security 在不使用我的 JWT 标头的情况下只允许资源通过,那么最好的办法是构建我自己的装饰器,它使用 Flask-Principal 来管理角色。

有人有这方面的经验吗?这个想法是整个前端可以并且将以我们需要的任何语言构建,这意味着它可能不是 Flask-Security 似乎正在做的 Flask 的模板/视图。

感谢任何人提供的任何见解!

【问题讨论】:

你有没有得到任何地方? @MikeDavlantes 我们最终没有机会这样做,因为我不得不搬到一家新公司,道歉:/ 【参考方案1】:

您可能希望使用 HTTP 状态代码 403 来响应,而不是重定向。

您最好的选择确实是创建自己的装饰器来管理角色,并完全放弃使用Flask-Security

Flask-Securityhas mentioned 的作者认为,有更好的方法来保护 API,而且由于库没有维护,因此更有意义。

Flask-JWTFlask-JWT-Extended 是完成此任务的最佳人选。前者需要更多样板才能让事情顺利进行。有一个 stale PR 建议使用 API 来支持角色,如果您决定使用 Flask-JWT,您可以使用它来创建自己的装饰器。

Flask-JWT-Extended 文档建议了一个更简单的解决方案,可能适合您的情况。您应该按照文档的custom decorators 部分获取完整示例,但简而言之,这里是装饰器:

from functools import wraps

from flask import jsonify
from flask_jwt_extended import (
    verify_jwt_in_request, get_jwt_claims
)

def admin_required(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        verify_jwt_in_request()
        claims = get_jwt_claims()
        if claims['roles'] != 'admin':
            return jsonify(msg='Admins only!'), 403
        else:
            return fn(*args, **kwargs)
    return wrapper

此代码在 JWT 中查找 roles 声明,如果不是 admin,则返回 403 响应。

【讨论】:

以上是关于通过 Flask-JWT REST API 使用 Flask-Security 角色的主要内容,如果未能解决你的问题,请参考以下文章

TypeError:使用 Flask-JWT 时需要字符串或字节格式的密钥

通过 PayPal REST API 示例打折?

通过 PayPal REST API 进行银行支付

使用 WordPress REST API 通过 AJAX 更改密码

Magento 使用 OAuth 通过 REST API 到 NodeJS

使用 cUrl 通过其 REST API 配置 Keycloak