# 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_requiredhtml
登陸的默認驗證行爲:get_password(username) == password.python
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:json
$ 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" }
密碼加密處理flask
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)bash
固然也能夠使用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
- __init__ 構造函數
- get_password 獲取密碼
- hash_password 加密密碼,能夠是password一個參數,也能夠username,password兩個參數
- verify_password 驗證密碼,能夠自定義驗證邏輯
- error_handler 錯誤處理,若是驗證失敗,則執行
- login_required 登陸受權,添加該註解後,則須要登陸才能訪問
- username 用戶名
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認證原理:session
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))
- __init__ 構造函數
- generate_ha1 構造函數中use_ha1_pw爲True時,生成HA1密碼
- generate_nonce
- verify_nonce
- generate_opaque
- verify_opaque
- get_password 獲取密碼
- error_handler 錯誤處理,若是驗證失敗,則執行
- login_required 登陸受權,添加該註解後,則須要登陸才能訪問
- username 用戶名
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
- __init__ 構造函數
- verify_token 驗證Token,能夠自定義驗證邏輯
- error_handler 錯誤處理,若是驗證失敗,則執行
- login_required 登陸受權,添加該註解後,則須要驗證過Token才能訪問
# 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