上一篇文章介紹了使用JWT協議來做token認證的功能,繼續裝逼下去,怎樣實現一個鑒權的模塊?
假設token認證的功能已經完成,那么設定為每次請求頭部中都帶上這個token。server端在每次響應請求時都要做一次token的校驗,包括兩個內容:
- token是否合法,token是否過期、是否被篡改,token內的信息是否有效
- 用戶是否有權限執行此請求
在python/flash上,這個檢驗使用decorator實現,會是一個很靠譜的辦法,代碼簡潔性和可讀性都很不錯。
為認證和鑒權創建數據模型
首先,理清關系,用戶-角色-權限,是多對多對多的關系。通過flask-sqlalchemy給這三者創建多對多模型,會是五張表,其中有兩張表是多對多連接用的實體表,無ORM模型(根據flask的建議)。
然后,為了實現審計功能(一些刪除對象操作之后將造成歷史數據無法對應),用戶-角色-權限都需要添加valid字段。刪除時只設為不可用。
最后,創建一個decorator函數,實現檢查token并審核權限的功能。
目標效果是怎樣的?
在一個api或函數前,添加此decorator,參數包含它所需要的權限名稱。一旦請求執行到此函數,將執行decorator的權限檢查。如果認證或鑒權失敗,直接走異常處理流程,不會執行請求內容。類似這樣:
@auth.PrivilegeAuth(privilegeRequired="userAdmin")
def get(self):
"""
get user list or one user info ...
"""
怎么實現
這需要一個帶參數的decorator類,需要傳參。內部邏輯十分簡單,檢查token--檢查用戶--檢查角色--檢查權限匹配--搞定放行。
ps.抱歉我的英文十分chinglish哈哈。。
auth.py
class PrivilegeAuth(Resource):
"""
This class is used tobe a decoretor for other methods to check the
client's privilege by token.
Your method's 'required privilege' should be set as an argument of this
decoretor. And this 'required privilege' should have been in the
'privilege' table.
If your method's 'required privilege' is one of user's privileges,
user will be allowed to access the method, otherwise not.
ps. user's privilege is checked by his token.
"""
def __init__(self, privilegeRequired):
# argument 'privilegeRequired' is to set up your method's privilege
# name
self.privilegeRequired = privilegeRequired
super(PrivilegeAuth, self).__init__()
def __call__(self, fn):
def wrapped(*args, **kwargs):
try:
rolesReq = Privilege.getFromPrivilegeName(
self.privilegeRequired).roles
except:
msg = 'wrong privilege setting: privilege (' +\
self.privilegeRequired + ') doesnot set in any roles'
app.logger.error(utils.logmsg(msg))
raise utils.InvalidModuleUsage('wrong privilege setting')
# get user's privileges by his token
# if token is in body, then:
# myreqparse = reqparse.RequestParser()
# myreqparse.add_argument('token')
# args = myreqparse.parse_args()
# if token is in headers, then:
token = request.headers.get('token')
if not token:
msg = "you need a token to access"
raise utils.InvalidAPIUsage(msg)
[userId, roleIdList, msg] = User.tokenAuth(token)
if not userId:
msg = msg + " when autherization"
raise utils.InvalidAPIUsage(msg)
else:
currentUser = User.getValidUser(userId=userId)
if not currentUser:
msg = "cannot find user when autherization"
raise utils.InvalidAPIUsage(msg)
# user's privilege auth
for role in currentUser.roles:
for roleReq in rolesReq:
if role.role_id == roleReq.role_id:
return fn(*args, **kwargs)
msg = "Privilege not Allowed."
app.logger.info(utils.logmsg(msg))
raise utils.InvalidAPIUsage(msg)
return wrapped