flask-httpauth

flask-httpauth


1. flask-httpauth

文档:https://flask-httpauth.readthedocs.io/en/latest/

1.1. 安装

pip install flask-httpauth

2. 简介

它有三种认证方式:

  • 基本认证(Basic Authentication)
  • 摘要认证(Digest Authentication)
  • 标志认证(Token Authentication)。

2.1. HTTPBasicAuth

class flask_httpauth.HTTPBasicAuth
This class handles HTTP Basic authentication for Flask routes.

init(scheme=None, realm=None)

If the optional scheme argument is provided, it will be used instead of the standard “Basic” scheme in the WWW-Authenticate response.(不太明白什么意思。)

verify_password(verify_password_callback)
模块还有get_password,hash_password等类似方法,但功能有一些重复,一般使用此方法即可。
它返回True或有效对象即可通过验证。
返回False无法验证失败。

  
 @auth.verify_password  
 def verify_pw(username, password):  
     return call_custom_verify_function(username, password)  

login_required(view_function_callback)
This callback function will be called when authentication is successful. This will typically be a Flask view function. Example:

  
@app.route('/private')  
@auth.login_required  
def private_page():  
    return "Only for authorized people!"  

2.2. HTTPDigestAuth

class flask_httpauth.HTTPDigestAuth

2.3. HTTPTokenAuth

class flask_httpauth.HTTPTokenAuth
同HTTPBasicAuth类似,它也提供”login_required”装饰器来认证视图函数,”error_handler”装饰器来处理错误。区别是,它没有”verify_password”装饰器,相应的,它提供了”verify_token”装饰器来验证令牌。我们来看下代码,为了简化,我们将Token与用户的关系保存在一个字典中:

verify_token(verify_token_callback)
在使用此类做为验证时,必需实现此方法。

  
@auth.verify_token  
def verify_token(token):  
    return User.query.filter_by(token=token).first()  

2.4. MultiAuth

Flask-HTTPAuth扩展还支持几种不同认证的组合,比如上面我们介绍了HTTPBasicAuth和HTTPTokenAuth,我们可以将两者组合在一起,其中任意一个认证通过,即可以访问应用视图。实现起来也很简单,只需将不同的认证实例化为不同的对象,并将其传入MultiAuth对象即可。大体代码如下:

  
auth_basic = flask_httpauth.HTTPBasicAuth()  
auth_token = flask_httpauth.HTTPTokenAuth(scheme='SWT')  
auth_multi = flask_httpauth.MultiAuth(auth_basic, auth_token)  

@blue_restful.route('/1')  
@auth_token.login_required  
def restful_index():  
    return 'restful api1.'  

这里,每个认证都有自己的验证和错误处理函数,不过在视图上,我们使用”@multi_auth.login_required”来实现多重认证

2.5. 错误处理

在之前的例子中,如果未认证成功,服务端会返回401状态码及”Unauthorized Access”文本信息。你可以重写错误处理方法,并用”@auth.error_handler”装饰器来修饰它:

  
from flask import make_response, jsonify  

@auth.error_handler  
def unauthorized():  
    return make_response(jsonify({'error': 'Unauthorized access'}), 401)  

有了上面的”unauthorized()”方法后,如果认证未成功,服务端返回401状态码,并返回JSON信息”{‘error’: ‘Unauthorized access’}”。

3. 实战

新建auth_verify.py

  
from flask import g  
import flask_httpauth  
from itsdangerous import TimedJSONWebSignatureSerializer, BadSignature, SignatureExpired  
from config import Config  

auth_basic = flask_httpauth.HTTPBasicAuth()  
auth_token = flask_httpauth.HTTPTokenAuth(scheme="JWT")  
auth_multi = flask_httpauth.MultiAuth(auth_basic, auth_token)  

TOKENS = {  
    "token1": 't1',  
    "token2": 't2',  
    "token3": "t3"  
}  


@auth_basic.verify_password  
def verify_password(user, password):  
    print("verify_password", user, password)  
    if not password:  
        return False  
    return user  

@auth_token.verify_token  
def verify_token(token):  
    g.user = None  
    token = token_s.loads(token, 'salt')  
    print('verify token:', token)  
    if token['id'] in TOKENS:  
        g.user = TOKENS[token['id']]  
        return True  
    return False  

# 创建token  
token_s = TimedJSONWebSignatureSerializer(Config.SECRET_KEY, Config.TOKEN_EXPIRATION)  

def create_token(id):  
    token = token_s.dumps({'id':id}, 'salt')  
    return token.decode('ascii')  

说明:

  1. auth_token = flask_httpauth.HTTPTokenAuth(scheme="JWT")中的scheme是一个前缀,用于在请求中构建头部参数;
  2. token = token_s.dumps({'id':id}, 'salt') salt是hash的加盐,在loads时也要相应设置,否则解密错误;

视图方法

  
# flask restful  

from flask_restful import Resource, Api, reqparse  
parser = reqparse.RequestParser()  
parser.add_argument('task', type=str, help='task')  
parser.add_argument('pwd', type=str, help='task')  
parser.add_argument("limit", type=int, required=False)  
parser.add_argument("offset", type=int, required=False)  
parser.add_argument("sortby", type=str, required=False)  


TODO_TASKS = { 'todo1': {'task': 'build an API'},  
    'todo2': {'task': '?????'},  
    'todo3': {'task': 'profit!'},  
}  

def dect(f):  
    def _inner(*ar, **kw):  
        print('fffff', f, ar, kw)  
        return f(ar, kw)  
    return _inner  

class FlaskRestful_API(Resource):  
    # 添加认证  
    method_decorators = [auth_token.login_required]  

    def get(self, id=None, name=None):  
        """  
        获取数据  
        :param id:  
        :param name:  
        :return:  
        """  
        args = parser.parse_args()  
        print('ar', args)  
        if not id:  
            return TODO_TASKS  
        else:  
            task = TODO_TASKS.get(str(id), None)  
            return task  

    def put(self, id):  
        args = parser.parse_args()  
        print(args)  
        print(request.form)  
        print('put', request.form)  
        if id in TODO_TASKS:  
            TODO_TASKS[id].update(request.form)  
            return TODO_TASKS[id]  
        else:  
            abort(404)  


    def post(self):  
        args = parser.parse_args()  
        print(args)  
        print(request.form)  
        res_json = jsonify({  
            'method': request.method  
            , 'url': request.url  
            , 'values': request.values  
            , 'headers': dict(request.headers)  
            , 'cookie': request.cookies  
            , 'session': request.host  
            , 'body': request.data.decode()  
            , 'origi': request.remote_addr  
            , "endpoint": request.endpoint  
        })  
        return res_json  

    def delete(self):  
        pass  

# 定义api类并注册视图  
api = Api(blue_restful)  
# 注册视图  
# 注意endpoint,如果不指定便为类名的小写  
api.add_resource(FlaskRestful_API, '/rest/', '/rest/', endpoint="rest_api")  


# 返回token  
from app.auth_verify import create_token  

@blue_restful.route('/login', methods=["POST"])  
def login():  
    """接受参数,返回token"""  
    # print(request.headers)  
    # user = request.json['username']  
    # pssword = request.json['password']  
    token = create_token('token2')  
    return jsonify({'code': 1, 'msg':'成功', 'data': token})  

说明:

  1. login视图用于获取token,使用post是为了加密;

模拟请求
使用requests模拟请求

  
def _test_func():  
    data = {'username':'name', 'password': 'pass'}  
    res = requests.post("http://127.0.0.1:9000/restful/login", headers={"Accept":"application/json"})  
    print('res.json', res.json())  
    print(res.headers)  
    token = res.json()['data']  
    print("token:", token)  

    headers = {"Authorization": "JWT "+ token, "Authenticate":'Bearer realm="owejoiod"'}  
    print(headers)  
    res = requests.get("http://127.0.0.1:9000/restful/rest/todo1", headers=headers)  
    print(res)  
    print(res.headers, res.text)  

说明:

  1. 第一次post是为了获取token;
  2. 请求传递token需要设置headers.Authorization,这里的“JWT ”就是在实例化HTTPTokenAuth(scheme="JWT")时所附加的前缀“JWT ”。
posted @ 2020-06-02 21:10  木林森__𣛧  阅读(902)  评论(0)    收藏  举报