flask權限管理

Wilfredo227 8年前發布 | 25K 次閱讀 Flask 安全相關

來自: https://segmentfault.com/a/1190000004406179

基本的flask權限管理

1. 驗證字段與密碼的存儲

權限管理的基礎就是驗證字段(用戶名or郵箱...)以及密碼,所以首先需要考慮驗證字段和密碼的存儲。(這里使用flask-sqlalchemy作為ORM)

model:User

class User(db.Model): """用戶類""" id = db.Column(db.Integer, primary_key=True)

# 用戶名字符串存儲即可
username = db.Column(db.String(164))
# 密碼一定要注意
# 密碼不允許在數據庫中明文存儲
password_hash = db.Column(db.String(164))
......

# 所以需要對用戶傳入的明文密碼進行加密
@property
def password(self):
    """
    password屬性函數
    不允許直接讀取原始值
    """
    return "密碼不是可讀形式!"

@password.setter
def password(self, password):
    """
    設置密碼hash值
    """
    self.password_hash = werkzeug.security.generate_password_hash(password)

def verify_password(self, password):
    """
    將用戶輸入的密碼明文與數據庫比對
    """
    return werkzeug.security.check_password_hash(password)</pre> 

2. 用戶權限與角色的設置

我一般將用戶權限設置為16進制的值, 而用戶角色則是用戶權限(16進制的值)的 異或(|) 運算,

比如我設置以下2個權限:

class Permission:
    """
    權限表
    """
    COMMENT = 0x01  # 評論
    MODERATE_COMMENT = 0x02  # 移除評論

那么可設置如下角色, 并建立和User的 多對一 關系:

class Role(db.Model):
    """
    用戶角色
    """
    id = db.Column(db.Integer, primary_key=True)

# 該用戶角色名稱
name = db.Column(db.String(164))
# 該用戶角色是否為默認
default = db.Column(db.Boolean, default=False, index=True)
# 該用戶角色對應的權限
permissions = db.Column(db.Integer)
# 該用戶角色和用戶的關系
# 角色為該用戶角色的所有用戶
users = db.relationship('User', backref='role', lazy='dynamic')

@staticmethod
def insert_roles():
    """
    創建用戶角色
    """
    roles = {
        # 定義了兩個用戶角色(User, Admin)
        'User': (Permission.COMMENT, True),
        'Admin': (Permission.COMMENT |
                  Permission.MODERATE_COMMENT, False)
    }
    for r in roles:
        role = Role.query.filter_by(name=r).first()
        if role is None:
            # 如果用戶角色沒有創建: 創建用戶角色
            role = Role(name=r)
        role.permissions = roles[r][0]
        role.default = roles[r][1]
        db.session.add(role)
        db.session.commit()</pre> 

現在只需在User里創建一個指向用戶角色的外鍵即可

calss User(db.Model):
    ......
    role_id = db.Column(db.Integer, db.ForeignKey('role.id'))
    ......

flask權限在具體場景中的應用

應用在具體場景, 使用相關擴展就不可避免了

1. 處理登錄(flask-login)

對于flask登錄,使用flask-login是較為簡單的辦法,

但是個人感覺 flask-login 不是很靈活(下面的擴展也是,

畢竟擴展的目的是通用)。<br/>

這里就不具體說明 如何集成flask-login了 ,主要說如何使用flask-login<br/>

用戶登錄

使用flask-login提供的 login_user , current_user

@auth.route('/login/', methods=["POST", "GET"])
def login():
    """用戶登錄"""
    next = get_redirect_target()
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is not None and user.verify_password:
            login_user(user)
            return redirect_back('default redirect back url', id=current_user.id)
        flash("用戶名或密碼錯誤!")
    return render_template("login.html", form=form, next=next)

用戶登出

使用flask-login提供的 logout_user

@login_required
@auth.route('/logout/')
def logout():
    """用戶登出"""
    logout_user()
    return redirect("重定向路由")

2. 處理HTTPBasicAuth(flask-httpauth)

flask-httpauth 可以幫助我們處理一些基本的權限管理,

同flask-login一樣, flask-httpauth 也提供了一個 login_required 裝飾器,

通過auth對象調用即可控制 用戶名, 密碼 的登錄權限控制。

token權限管理

這里比較復雜的地方就是token的權限管理了, 因為此處沒有拓展可用(擴展是為了通用性, 但是token不是強制使用的),都是自己踩坑弄出來的, 寫在博客里做一個紀錄。

什么是token

token是用戶信息(一般是用戶id,

具有唯一標識作用)的標識。對用戶id進行簽名加密(我一般使用itsdangerous模塊),

例如:

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

此函數為User類的成員函數

def generate_auth_token(self): s = Serializer( current_app.config['SECRET_KEY'], # 密鑰很重要 expiration # Token的壽命 )

# 用獲取的簽名加密用戶id信息
return s.dumps({'id': self.id})</pre> 

為什么需要token

token 一般使用在API的場景中, 此時 客戶端和服務器端是分離的 ,

數據信息通過API進行傳遞, 他人很容易就可以攔截API并獲取驗證頭部的 authorization 字段,

從而獲取用戶名和密碼(一般是base64進行了加密, 但是很容易被破解), 這樣是極不安全的。<br/>

之所以需要驗證, 其實就是為了標識這個用戶是誰, 而id就是一個最好的標識,

所以通過用戶的 用戶名和密碼請求token (這樣用戶名和密碼不會頻繁被發送),

用獲取的token放在API頭部進行傳遞, 這樣即使被攔截,

也不會獲取用戶的敏感信息(用戶名, 密碼), 即使token被他人使用,

也會因為token的壽命而使破壞性大大降低。

token的使用: 發送token

token需放在 Authorization 頭部, 采用如下形式發送:

"Basic token"

token的使用: 解析token

依然采用 itsdangerous 模塊, 這里 密鑰 就很重要了,

只有與加密相同的密鑰才可以解析token。

# User類的一個靜態成員函數
@staticmethod
def verify_auth_token(token):

# 獲取簽名
s = Serializer(current_app.config["SECRET_KEY"])
try:
    data = s.loads(token)  # 用簽名解析token
except:
    return None
# 返回該token標識的用戶
return User.query.filter_by(id=data['id'])</pre> 
基于token的權限管理

這里主要是區別普通用戶和管理員用戶, 因為有些特定的操作是只有管理員可以進行的。<br/>

1.在User類里添加管理員判斷函數 <br/>

def can(self, permission):

# 判斷用戶是否具備某權限
return self.role is not None and (self.role.permissions & permissions) = permissions

def is_adminstractor(self):

# 判斷用戶是否具備管理員權限
return self.can(Permission.COMMENT | Permission.MODERATE_COMMENT)</pre> 

2.創建 管理員權限判斷裝飾器 <br/>

from functools import wraps
from flask import request, g

def admin_required(f): @wraps(f) def decorator(args, **kwargs): token_header = request.headers.get('authorization') token = token_header[6:] # 去掉格式中的Basic if token: g.current_user = User.verify_auth_token(token) if g.current_user.is_adminstractor(): return f(args, **kwargs) else: abort(403) return decorator</pre>

然后就可以使用 admin_required 裝飾器進行權限管理了,

這里有個坑提一下就是 flask-httpauth 會自己添加Basic,

所以使用該擴展的地方就不必考慮token格式的問題了。

</div>

總結

flask權限管理比較煩的地方就是如何使不同的權限管理擴展一起工作,

以及使用了擴展以后對于定制自己的東西該如何寫,

這時候還是最好去看使用相應擴展的源碼, 然后以此為基礎, 展開自己的工作吧!

 本文由用戶 Wilfredo227 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!