# 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