flask token认证

在前后端分离的项目中,我们现在多半会使用token认证机制实现登录权限验证。

token通常会给一个过期时间,这样即使token泄露了,危害期也只是在有效时间内,超过这个有效时间,token过期了,就需要重新生成一个新的token。

如何生成token呢?

1、创建用户数据库,本文会使用flask-SQLAlchemy(ORM)去管理数据库:

  首先创建一个用户模型:包括了用户昵称,账号(邮箱或者电话号码等),密码及拥有的权限

 1 class User(Base):
 2     id = Column(Integer, primary_key=True)
 3     nickname = Column(String(30), nullable=False)
 4     account = Column(String(30), nullable=False)
 5     _password = Column("password", String(100), nullable=False)
 6     auth = Column(SmallInteger, default=1)
 7
 8     @property
 9     def password(self):
10         return self._password
11
12     @password.setter
13     def password(self, row):
14         self._password = generate_password_hash(row)
15
16     @staticmethod
17     def register_by_email(nickname, account, password):
18         with db.auto_commit():
19             user = User()
20             user.nickname = nickname
21             user.account = account
22             user.password = password
23             db.session.add(user)
24
25     @staticmethod
26     def checkUser(email, password):
27         # 验证用户名是否存在
28         user = User.query.filter_by(account=email).first_or_404()
29         res = user.checkPassword(password)
30         if not res:
31             raise AuthFailed()
32         scope = "adminScope" if user.auth=="2" else "scope"
33         return {"uid":user.id, "scope":scope}
34
35     def checkPassword(self, raw):
36         if not self._password:
37             return False
38         # check_password_hash将raw加密后和_password比较
39         p = generate_password_hash(raw)
40         print(p==self._password)
41         return check_password_hash(self._password, raw)
42
43     def delete(self):
44         self.status = "0"

  由于安全原因,数据库的密码是一定不能明文保存的,所以此处将用户名进行了加密

  本文使用的werkzeug.security下面的generate_password_hash()对密码进行的加密,我们定义了password.setter方法,当在设置密码时,会调用generate_password_hash(password)加密密码,并将其赋值给_password

  当验证密码时,会调用werkzeug.security下面的check_password_hash(hashpwd, raw) 对用户传递过来的密码和加密后的密码进行比对,如果正确返回True

2、注册

  当前端传递过来用户名,密码时进行注册时,我们需要对用户名和密码进行如下基本验证

    1)非空性及长度等基本校验

    2)用户名是否已经存在

  邮箱注册form:

class EmailRegisterForm(RegisterForm):
    nickname = StringField(validators=[DataRequired(), length(3,30)])
    account = StringField(validators=[DataRequired(message="account can not be blank"), length(
        min=3, max=32, message="account length wrong"), Email(message="format wrong")])
    password = StringField(validators=[DataRequired()])

    def validate_account(self, value):
        user = User.query.filter_by(account=value.data).first()
        if user:
            raise ParamsError(msg = "用户已存在")

当验证成功后,会调用我们在User模型下面定义的register_by_email() 方法进行注册。

@api.router("/register", methods=["POST"])
def register():
    data = request.json
    form = RegisterForm(data=data).validate_for_api()
    promise = {
        ClientType.REGISTER_EMAIL:_register_by_email,
        ClientType.REGISTER_MOBILE:_register_by_mobile()
    }
    promise[form.type.data]()
    return Success()

def _register_by_email():
    form = EmailRegisterForm(data=request.json).validate_for_api()
    nickname = form.nickname.data
    account = form.account.data
    password = form.password.data
    User.register_by_email(nickname, account,password)

现在我们使用postman发送一条注册请求

如果用户已经存在,会返回400

3、登录,生成token

  生成token的方式有很多种,如产生一个固定长度的随机字符串,和用户名密码及过期时间一起存储在数据库中,这样token就是一个普通的字符串,可以方便的和其他字符串验证比较并可以检查是否过期

  比较复杂一点的做法就是,不要将token存储在数据库,而是使用数字签名作为token,这样做的好处是经过用户数字签名的token是可以防止篡改的。

  flask使用与数字签名类似的方法去实现加密的token,我们可以直接使用itsdangerous库去实现。

  生成token,需要用到itsdangerous下面的TimedJSONWebSignatureSerializer

  首先我们实例化一个Serializer,并将我们的秘钥SECRET_KEY和过期时间作为参数,返回一个TimedJSONWebSignatureSerializer类型对象

  然后调用TimedJSONWebSignatureSerializer对象的dumps方法,将我们想要写入到token中的信息以字典形式传递进去即可。

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

def generate_auth_token(uid, type, scope, expiration=7200):
    serializer = Serializer(current_app.config["SECRET_KEY"], expires_in=expiration)
    token = serializer.dumps({"uid":uid, "type":type.value, "scope":scope})
    return token

  当前端传递用户名,密码到服务端时,服务端校验用户存在并且密码正确时候,就会调用generate_auth_token函数,生成token

  值得注意的一点是,这里生成的token是二进制的,所以我们在返回给前端时,需要将二进制解码token.decode("ascii")

  后面用户在访问需要登录才能访问的的接口时,就不需要再登录,只需要将token传递过来即可。

  1)验证用户名是否存在,此方法作为静态方法放在User模型下

    @staticmethod
    def checkUser(email, password):
        user = User.query.filter_by(account=email).first_or_404()
        res = user.checkPassword(password)
        if not res:
            raise AuthFailed()
        scope = "adminScope" if user.auth=="2" else "scope"
        return {"uid":user.id, "scope":scope}

  2)校验密码是否匹配  

    def checkPassword(self, raw):
        if not self._password:
            return False
        # check_password_hash将raw加密后和_password比较
        return check_password_hash(self._password, raw)

  3)校验通过后,调用generate_auth_token方法生成token

@api.router("/", methods=["POST"])
def get_token():
    data = request.json
    form = EmailLoginForm(data=data).validate_for_api()
    type = form.type.data
    promise = {
        ClientType.REGISTER_EMAIL:User.checkUser
    }
    identify = promise[ClientType(type)](form.account.data, form.password.data)
    expiration = current_app.config["EXPIRATION"]
    token = generate_auth_token(identify["uid"], type,identify["scope"], expiration)
    r = {
        "token":token.decode("ascii")
    }
    return jsonify(r)

4、token认证

  如用户想要获取用户信息,这个是要登录后才能访问的接口,我们可以使用一个装饰器 @auth.login_required 保护,即表示只有正常登录的用户才可以访问

  这个装饰器用到了flask_httpauth库下面的HTTPBasicAuth

  auth = HTTPBasicAuth

  HTTP Basic Authentication 协议没有具体要求必须使用用户名密码进行验证,HTTP头可以使用两个字段去传输认证信息,对于token,我们只需要将token作为用户名传递过去即可,密码字段可以不填

  @auth.verify_password将作为@auth.login_required的中校验密码的回调函数被调用。

  我们前面生成token的时候,用到了我们自定义了SECRET_KEY加密,同样解密也需要使用我们的秘钥SECRET_KEY,加密调用的是serializer.dumps(),解密对应的需要使用serializer.loads()

  调用serializer.loads(token)时,如果捕捉到下面两个错误:

    BadSignature:签名错误,签名可能被篡改

    SignatureExpired:签名已过期

  表示验证token失败,直接抛出自定义异常,如果没有捕捉到错误,表示,验证通过。可以从中取得前面加密的用户信息,并将信息保存在g变量中,留做他用。

  这里的g变量和request一样,都是代理模式的实现,而且是线程隔离的,所以也不用担心多个请求线程导致数据错乱。

@auth.verify_password
def check_authorization(token, pwd):
    user_info = check_auth_token(token)
    if not user_info:
        return False
    else:
        g.user = user_info
        return True

def check_auth_token(token):
    serialzer = Serializer(current_app.config["SECRET_KEY"])
    try:
        s = serialzer.loads(token)
    except BadSignature:
        raise AuthFailed(msg="token is invalid", error_code=1004)
    except SignatureExpired:
        raise AuthFailed(msg="token is expired", error_code=1004)
    uid = s["uid"]
    type = s["type"]
    scope = s["scope"]
    return user(uid, type, scope)

原文地址:https://www.cnblogs.com/fiona-zhong/p/10254961.html

时间: 2024-10-10 18:13:46

flask token认证的相关文章

微信token认证源码分享(c#版)

在开发时遇到一个问题: 上线后提交申请微信提示"您的服务器没有正确响应token验证...",我查看日志发现根本就没有接收到来自微信的参数. 后来我又记录了微信请求方式和请求的字符串,想看看微信服务器到底有没有给我的服务器响应请求.结果是有的.并且通过了. 代码就添加了Request.HttpMethod和Request.QueryString没变,但不晓得怎么回事. /// <summary> 按照api说明对signature进行校验,校验成功返回参数echostr &

034.认证方式 | Token认证 及 AK/SK认证

认证方式 关于认证: https://www.cnblogs.com/badboyh2o/p/11068779.html https://www.cnblogs.com/badboyh2o/p/11069470.html Token认证 用途:一般用于登录,token包含授权信息. 原理:任何请求,都附带token:服务端根据token判断请求是否合法. 缺点:如果报文在中途被劫持,那么token就泄露了,这时(token有效期内)黑客就可以构造任意的请求了. AK/SK的认证 用途:一般用于后

token认证

token认证 文章出处:https://segmentfault.com/a/1190000014527692 一.CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造.攻击者盗用你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全. 二.CSRF攻击原理 三.防御CSRF的策略:token认证 1.token验证

【django后端分离】Django Rest Framework之认证系统之redis数据库的token认证(token过期时间)

1:登录视图 redis_cli.py文件: import redis Pool= redis.ConnectionPool(host='localhost',port=6379,decode_responses=True) 登录视图文件:import redisfrom utils.redis_cli import Pool    # 创建redis连接池 class UserLogin(APIView): """ 用户登陆认证: 登录成功更新token值,并且返回给前端,

ASP.NET Core Token认证

翻译:Token Authentication in ASP.NET Core 令牌认证(Token Authentication)已经成为单页应用(SPA)和移动应用事实上的标准.即使是传统的B/S应用也能利用其优点.优点很明白:极少的服务端数据管理.可扩展性.可以使用单独的认证服务器和应用服务器分离. 如果你对令牌(token)不是太了解,可以看这篇文章( overview of token authentication and JWTs) 令牌认证在asp.net core中集成.其中包括

java微信二次第三方开发,token认证,消息事件接收,图文素材库,自定义菜单等功能

基于之前的文章SSM配置的项目:http://www.cnblogs.com/mangyang/p/5168291.html 来进行微信第三方开发, 微信二次开发,官方还是网上有很多介绍了,这里就不在进行讲述了 直接上干货. 首先 与微信对接,服务器配置,需要80端口和443端口开放的服务器,这里推荐 使用 python 的pagekite,一款反向代理的工具,具体安装百度搜,提供下配置放方法:http://jingyan.baidu.com/article/0eb457e52ca0af03f0

Restful下的token认证方案

Restful讲究一个无状态的特性(stateless),这就不能把一些例如登陆后的认证信息写进cookie的传统方式, 目前探索的是采用token的方式来进行权限的识别. 刚开始研究token的时候,很容易查到比较流行的JWT(JSON Web Token)的很多资料,目前有RFC的规范(尽管还只是个草案). 简单来说JWT规定了可以自定义的CLAIM区域,并且可以加密(要在头部指明加密方法),如下图所示 (本图来自于https://jwt.io) 由于采用的非对称加密的方式,所以一般情况下信

Aspnet前后端分离项目手记(二)关于token认证

在前后端分离的项目中,首先我们要解决的问题就是身份认证 以往的时候,我们使用cookie+session,或者只用cookie来保持会话. 一,先来复习一下cookie和session 首先我们来复习一下在aspnet中cookie和session的关系,做一个简单试验 这是一个普通的view没有任何处理 可以看到,没有任何东西(cookie),然后当我们写入一个session之后 \ 会发现多了一个名为ASP.NET_SessionId的cookie.我们都知道在aspnet中,session

Aspnet Mvc 前后端分离项目手记(二)关于token认证

在前后端分离的项目中,首先我们要解决的问题就是身份认证 以往的时候,我们使用cookie+session,或者只用cookie来保持会话. 一,先来复习一下cookie和session 首先我们来复习一下在aspnet中cookie和session的关系,做一个简单试验 这是一个普通的view没有任何处理 可以看到,没有任何东西(cookie),然后当我们写入一个session之后 会发现多了一个名为ASP.NET_SessionId的cookie.我们都知道在aspnet中,session是保