😄
第一篇博客,熟悉一下10分钟速成的markdown笔记写法,把加密和token验证的具体逻辑记录一下,当作强化记忆和学习
1、设计思路
- 用户注册:查重数据库里的用户名,密码以非明文的形式记入数据库(加密)
- 用户登录:检索该用户名,解密对应的密码,与明文进行对比,生成用于通行的token
- Api请求:正常使用,一旦token过期,用户收到重新登录的提示
token都有一个过期时间,从用户层面来看,这个到期时间是看不到的,各个网站或者APP的设计也各不相同,我记得小黑盒的网页端只有几十分钟时间,网易BUFF可以选择15天。
很多业务会选择长短token的双token机制,长token设置超过一天的较长时间,短token过期后,让长token进行验证,通过后直接发放新的短token(而不是为短token续期),让用户可以长期使用。
长token往往设置几天甚至一个月的时间,可像微信等大量软件几乎不需要重新登录,利用更底层的,与设备绑定的设备信任可以跳出token的有效期,创造一种“永不登录”的体验。不过像微信内置的小程序根据开发者不同的需求,还是以token验证机制为主。
最简单的token过期机制,以30分钟为例,流程大概如下图:

2、代码拆解
2.1 加密验证
|
|
用户设置的密码不能以明文的方式存入数据库中,一般要经过加密,bcrypt提供了一种不可逆的密码哈希,核心是通过salt = bcrypt.gensalt(rounds=12)这种生成随机盐的方式,与版本标识、哈希值等进行再组合而生成的最终密文,由于盐的随机生成性,可以保证相同明文可以生成不同的密文。
问题:如果生成的密文不同,那verify_password是怎么让用户输入的密码与数据库的哈希值进行比对的?
实际上verify()函数并不是对用户输入的密码进行再加密后与数据库密文进行比对,而是提取了数据库中保存的密码密文中的盐值,再与输入密码的明文进行bcrypt加密,这里的盐值不再具有随机性,因此加密出的密文是相同的,达到了一个安全性与验证性兼顾的效果。
2.2 生成token
|
|
用户在成功登陆之后都会生成一段token,在token的有效时间内不需要重新登录就能访问所有API。expire是当前国际时间加上token有效时长而确定的一个最终到期时间,token的生成根据开发者的需要会到用户名、到期时间等变量,这也是我们要传入一个data:dirt字典类型的原因,同时用于token生成的to_encode也要采用copy()的形式,因为频繁更改原始数据会降低安全性。
问题:一段token最终如何生成,又如何保证安全性?
用JWT生成的token由三个部分组成

jwt.encode(to_encode,SECRET_KEY,algorithm=ALGORITHM)这里的三个参数,ALGORITHM决定了我们所用的加密算法,to_encode是从用户那里收集的信息通过Base64编码放进token,要注意Base64不是加密,任何人都可以解码,所以不能收集密码等敏感信息用于token生成。
而确保安全性的SECRET_KEY是由手动设置的一串密钥,密钥与头部和Payload进行加密生成Signature(签名),只要他人拿不到这串密钥,就无法生成正确的签名,这就是JWT用于保证安全性的方式
2.3 token验证
|
|
|
|
具体怎么实现token的过期验证,需要把这两段代码结合起来看,首先decode_token是尝试一个获取用户名的函数,正如JWT的三个部分所呈现的那样,Payload是用户名和过期时间通过Base64转换过去的,可以被我们解码后获取。而jwt.decode会做三件事:
- 验证密钥是否正确
- 验证token是否被篡改
- 检查exp字段(也就是过期时间)是否过期
如果token过期,抛出JWTError的异常,返回None。在用户每次请求API的时候,都会传入get_current_user的参数,要求返回用户名,如果是None,说明token已经过期,要求用户重新登录。
2.4 全部代码
|
|
虽然只用了一点基础功能,感觉Markdown写出来还是足够好看的。网页排版里要首行缩进还要改CSS的格式,干脆就不缩进了。