zoukankan      html  css  js  c++  java
  • Python Flask-HTTPAuth

    Flask-HTTPAuth

    1. Basic authentication example

    --

    # coding=utf-8
    from flask import Flask
    from flask_httpauth import HTTPBasicAuth
    
    app = Flask(__name__)
    auth = HTTPBasicAuth()
    
    users = {
        "john": "hello",
        "susan": "bye"
    }
    
    @auth.get_password
    def get_pw(username):
        if username in users:
            return users.get(username)
        return None
    
    @app.route('/')
    @auth.login_required  
    def index():
        return "Hello, %s!" % auth.username()
    
    if __name__ == '__main__':
        app.run()
    

    请求方法需要登录,添加注解@auth.login_required

    登录的默认验证行为:get_password(username) == password.

    Test:

    $ curl -i http://localhost:5000
    HTTP/1.0 401 UNAUTHORIZED
    Content-Type: text/html; charset=utf-8
    Content-Length: 19
    WWW-Authenticate: Basic realm="Authentication Required"
    Server: Werkzeug/0.12.2 Python/2.7.13
    Date: Thu, 26 Oct 2017 06:11:34 GMT
    
    Unauthorized Access
    
    $ curl -u "john:hello" -i http://localhost:5000
    HTTP/1.0 200 OK
    Content-Type: text/html; charset=utf-8
    Content-Length: 12
    Server: Werkzeug/0.12.2 Python/2.7.13
    Date: Thu, 26 Oct 2017 06:12:33 GMT
    
    Hello, john!
    

    授权失败处理

    from flask import make_response, jsonify
    
    # 授权失败
    @auth.error_handler
    def unauthorized():
    	return make_response(jsonify({'error': 'Unauthorized access'}), 403)
    

    Test:

    $ curl -i http://localhost:5000
    HTTP/1.0 403 FORBIDDEN
    Content-Type: application/json
    WWW-Authenticate: Basic realm="Authentication Required"
    Content-Length: 37
    Server: Werkzeug/0.12.2 Python/2.7.13
    Date: Thu, 26 Oct 2017 06:26:20 GMT
    
    {
      "error": "Unauthorized access"
    }
    

    密码加密处理

    from hashlib import md5
    
    users = {
    	"john": md5("hello").hexdigest(),
    	"susan": "bye"
    }
    
    @auth.hash_password
    def hash_pw(password):
    	return md5(password).hexdigest()
    

    如果加密处理需要username,则使用两个参数的函数

    @auth.hash_password
    def hash_pw(username, password):
    	return md5(username + password).hexdigest()
    

    如果添加了加密函数,则验证行为是:get_password(username) == hash_password(password)

    当然也可以使用verify_password来替代验证行为。

    # 验证密码
    @auth.verify_password
    def verify_pw(username, password):
    	if username in users:
    		if password == users.get(username) or md5(password).hexdigest() == users.get(username):
    			return True
    	return False
    

    class flask_httpauth.HTTPBasicAuth

    1. __init__ 构造函数
    2. get_password 获取密码
    3. hash_password 加密密码,可以是password一个参数,也可以username,password两个参数
    4. verify_password 验证密码,可以自定义验证逻辑
    5. error_handler 错误处理,如果验证失败,则执行
    6. login_required 登录授权,添加该注解后,则需要登录才能访问
    7. username 用户名

    2. Digest authentication example

    --

    from flask import Flask
    from flask_httpauth import HTTPDigestAuth
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'secret key here'
    auth = HTTPDigestAuth()
    
    users = {
        "john": "hello",
        "susan": "bye"
    }
    
    @auth.get_password
    def get_pw(username):
        if username in users:
            return users.get(username)
        return None
    
    @app.route('/')
    @auth.login_required
    def index():
        return "Hello, %s!" % auth.username()
    
    if __name__ == '__main__':
        app.run()
    

    Digest认证原理:

    Digest Authentication在基本身份验证上面扩展了安全性. 服务器为每一连接生成一个唯一的随机数, 客户端对用这个随机数对密码进行MD5加密. 然后发送到服务器. 服务器端也用此随机数对密码加密, 然后和客户端传送过来的加密数据进行比较.

    过程:客户端请求资源->服务器返回认证标示->客户端发送认证信息->服务器查验认证,

    如果成功则继续资源传送,否则直接断开连接。

    nonce & opaque

    @auth.generate_nonce
    def generate_nonce():
    	"""Return the nonce value to use for this client."""
    	pass
    
    
    @auth.generate_opaque
    def generate_opaque():
    	"""Return the opaque value to use for this client."""
    	pass
    
    
    @auth.verify_nonce
    def verify_nonce(nonce):
    	"""Verify that the nonce value sent by the client is correct."""
    	pass
    
    
    @auth.verify_opaque
    def verify_opaque(opaque):
    	"""Verify that the opaque value sent by the client is correct."""
    	pass
    

    realm:授权域,至少应该包含主机名

    nonce:服务端产生的随机数,用于增加摘要生成的复杂性,从而增加破解密码的难度,防范“中间人”与“恶意服务器”等攻击类型,这是相对于不使用该指令而言的;另外,nonce本身可用于防止重放攻击,用于实现服务端对客户端的认证。RFC 2617 建议采用这个随机数计算公式:nonce = BASE64(time-stamp MD5(time-stamp “:” ETag “:” private-key)),服务端可以决定这种nonce时间有效性,ETag(URL对应的资源Entity Tag,在CGI编程中通常需要自行生成ETag和鉴别,可用于鉴别URL对应的资源是否改变,区分不同语言、Session、Cookie等)可以防止对已更新资源版本(未更新无效,故需要设定nonce有效期)的重放请求,private-key为服务端私有key

    opaque:这是一个不透明的数据字符串,在盘问中发送给客户端,客户端会将这个数据字符串再发送回服务端器。如果需要在服务端和客户端之间维护一些状态,用nonce来维护状态数据是一种更容易也更安全的实现方式

    默认生成与认证

    def _generate_random():
        return md5(str(self.random.random()).encode('utf-8')).hexdigest()
    
    def default_generate_nonce():
        session["auth_nonce"] = _generate_random()
        return session["auth_nonce"]
    
    def default_verify_nonce(nonce):
        return nonce == session.get("auth_nonce")
    
    def default_generate_opaque():
        session["auth_opaque"] = _generate_random()
        return session["auth_opaque"]
    
    def default_verify_opaque(opaque):
        return opaque == session.get("auth_opaque")
        
    def generate_ha1(self, username, password):
        a1 = username + ":" + self.realm + ":" + password
        a1 = a1.encode('utf-8')
        return md5(a1).hexdigest()
    

    算法:

    1. 算法的一般性表示
    	H(data) = MD5(data)
    	KD(secret, data) = H(concat(secret, ":", data))
    	
    2. 与安全信息相关的数据用A1表示,则
    	a) 采用MD5算法:
    	    A1=(user):(realm):(password)
    	b) 采用MD5-sess算法:
    	    A1=H((user):(realm):(password)):nonce:cnonce
    
    3. 与安全信息无关的数据用A2表示,则
    	a) QoP为auth或未定义:
    	    A2=(request-method):(uri-directive-value)
    	b) QoP为auth-int:
    	    A2=(request-method):(uri-directive-value):H((entity-body))
    	    
    4. 摘要值用response表示,则
    	a) 若qop没有定义:
    	    response
    	    = KD(H(A1),<nonce>:H(A2))
    	    = H(H(A1),<nonce>:H(A2))
    	b) 若qop为auth或auth-int:
    	    response
    	    = KD(H(A1),<nonce>:<nc>:<cnonce>:<qop>:H(A2))
    	    = H(H(A1),<nonce>:<nc>:<cnonce>:<qop>:H(A2))
    

    class flask_httpauth.HTTPDigestAuth

    1. __init__ 构造函数
    2. generate_ha1 构造函数中use_ha1_pw为True时,生成HA1密码
    3. generate_nonce
    4. verify_nonce
    5. generate_opaque
    6. verify_opaque
    7. get_password 获取密码
    8. error_handler 错误处理,如果验证失败,则执行
    9. login_required 登录授权,添加该注解后,则需要登录才能访问
    10. username 用户名

    3. Token Authentication Scheme Example

    --

    from flask import Flask, g
    from flask_httpauth import HTTPTokenAuth
    
    app = Flask(__name__)
    auth = HTTPTokenAuth(scheme='Token')
    
    tokens = {
        "secret-token-1": "john",
        "secret-token-2": "susan"
    }
    
    @auth.verify_token
    def verify_token(token):
        if token in tokens:
            g.current_user = tokens[token]
            return True
        return False
    
    @app.route('/')
    @auth.login_required
    def index():
        return "Hello, %s!" % g.current_user
    
    if __name__ == '__main__':
        app.run()
    

    TEST

    curl -X GET -H "Authorization: token secret-token-2" http://localhost:5000
    

    class flask_httpauth.HTTPTokenAuth¶

    1. __init__ 构造函数
    2. verify_token 验证Token,可以自定义验证逻辑
    3. error_handler 错误处理,如果验证失败,则执行
    4. login_required 登录授权,添加该注解后,则需要验证过Token才能访问

    4. Using Multiple Authentication Schemes

    --

    # coding=utf-8
    
    import base64
    import unittest
    from flask import Flask
    from flask_httpauth import HTTPBasicAuth, HTTPTokenAuth, MultiAuth
    
    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'my secret'
    
    basic_auth = HTTPBasicAuth()
    token_auth = HTTPTokenAuth('MyToken')
    multi_auth = MultiAuth(basic_auth, token_auth)
    
    
    @basic_auth.verify_password
    def verify_password(username, password):
    	return username == 'john' and password == 'hello'
    
    
    @token_auth.verify_token
    def verify_token(token):
    	return token == 'this-is-the-token'
    
    
    @token_auth.error_handler
    def error_handler():
    	return 'error', 401, {'WWW-Authenticate': 'MyToken realm="Foo"'}
    
    
    @app.route('/')
    @multi_auth.login_required
    def index():
    	return 'index'
    
    if __name__ == '__main__':
    	app.run()
    

    多重认证,只需要使用其中的一种认证方式来通过认证。

    TEST:

    $ curl -u john:hello http://localhost:5000
    index
    
    $ curl -H "Authorization: MyToken this-is-the-token" http://localhost:5000
    index
    
  • 相关阅读:
    mysql u root p 等这些常用的参数
    关于类对象大小的 sizeof 计算问题
    sql like用法
    链表队列
    Zend Studio安装教程
    #define和inline 的区别
    ERROR 1045 (28000): Access denied for user root@localhost (using password: NO)
    Ubuntu 10.04下LAMP环境的配置与使用教程
    win7保护视力的电脑设置
    sql between and 用法
  • 原文地址:https://www.cnblogs.com/zhuhc/p/7738227.html
Copyright © 2011-2022 走看看