flask權限管理
來自: https://segmentfault.com/a/1190000004406179
基本的flask權限管理
1. 驗證字段與密碼的存儲
權限管理的基礎就是驗證字段(用戶名or郵箱...)以及密碼,所以首先需要考慮驗證字段和密碼的存儲。(這里使用flask-sqlalchemy作為ORM)
model:Userclass 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, gdef 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權限管理比較煩的地方就是如何使不同的權限管理擴展一起工作,
以及使用了擴展以后對于定制自己的東西該如何寫,
這時候還是最好去看使用相應擴展的源碼, 然后以此為基礎, 展開自己的工作吧!