权限管理的基础就是验证字段(用户名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权限管理比较烦的地方就是如何使不一样的权限管理扩展一块儿工做,以及使用了扩展之后对于定制本身的东西该如何写,这时候仍是最好去看使用相应扩展的源码, 而后以此为基础, 展开本身的工做吧!