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_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

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認證原理: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))

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
相關文章
相關標籤/搜索