權限管理的基礎就是驗證字段(用戶名or郵箱...)以及密碼,
因此首先須要考慮驗證字段和密碼的存儲。(這裏使用flask-sqlalchemy做爲ORM)html
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)
我通常將用戶權限設置爲16進制的值, 而用戶角色則是用戶權限(16進制的值)的異或(|)運算,
好比我設置如下2個權限:sql
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()
如今只需在User裏建立一個指向用戶角色的外鍵便可flask
calss User(db.Model): ...... role_id = db.Column(db.Integer, db.ForeignKey('role.id')) ......
應用在具體場景, 使用相關擴展就不可避免了安全
對於flask登陸,使用flask-login是較爲簡單的辦法,
可是我的感受flask-login
不是很靈活(下面的擴展也是,
畢竟擴展的目的是通用)。<br/>
這裏就不具體說明如何集成flask-login了,主要說如何使用flask-login<br/>服務器
使用flask-login提供的login_user
, current_user
session
@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
app
@login_required @auth.route('/logout/') def logout(): """用戶登出""" logout_user() return redirect("重定向路由")
flask-httpauth 能夠幫助咱們處理一些基本的權限管理,
同flask-login同樣, flask-httpauth 也提供了一個login_required裝飾器,
經過auth對象調用便可控制用戶名, 密碼的登陸權限控制。函數
這裏比較複雜的地方就是token的權限管理了, 由於此處沒有拓展可用(擴展是爲了通用性, 可是token不是強制使用的),
都是本身踩坑弄出來的, 寫在博客裏作一個紀錄。ui
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})
token 通常使用在API的場景中, 此時客戶端和服務器端是分離的,
數據信息經過API進行傳遞, 他人很容易就能夠攔截API並獲取驗證頭部的authorization字段,
從而獲取用戶名和密碼(通常是base64進行了加密, 可是很容易被破解), 這樣是極不安全的。<br/>
之因此須要驗證, 其實就是爲了標識這個用戶是誰, 而id就是一個最好的標識,
因此經過用戶的用戶名和密碼請求token(這樣用戶名和密碼不會頻繁被髮送),
用獲取的token放在API頭部進行傳遞, 這樣即便被攔截,
也不會獲取用戶的敏感信息(用戶名, 密碼), 即便token被他人使用,
也會由於token的壽命而使破壞性大大下降。
token需放在Authorization頭部, 採用以下形式發送:
"Basic 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'])
這裏主要是區別普通用戶和管理員用戶, 由於有些特定的操做是隻有管理員能夠進行的。<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)
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
而後就可使用admin_required裝飾器進行權限管理了,
這裏有個坑提一下就是flask-httpauth會本身添加Basic,
因此使用該擴展的地方就沒必要考慮token格式的問題了。
flask權限管理比較煩的地方就是如何使不一樣的權限管理擴展一塊兒工做,以及使用了擴展之後對於定製本身的東西該如何寫,這時候仍是最好去看使用相應擴展的源碼, 而後以此爲基礎, 展開本身的工做吧!