Typescript express 新手教程 S5 express应用极限最简注册登录 教程
太长不看
- 创建一张user 表
- 登录和注册也是两个路由,因为他们提供的功能都属于一个类型,比如叫做验证类,统一放到一个控制器里
- 需要用到 bcrypt这个库,他提供了密码的蛤希,包括了加盐(加入随机字符串,让同样的密码的蛤希结果也不完全相同,阻止rainbow table attack),当然还有compare,用于比对(比如客户端或者攻击者)提交的密码和hash后的密码是否一致
- salt是随机生成的字符串,会加入到hash后的结果中
- hash后的结果的格式固定,所以不需要单独保存salt
- 需要用到 jsonwebtoken这个库,他提供了发token和验证token的功能
- 自定义中间件 验证 客户端信息
- 还要有退出登录
蛤希密码
- 这里的蛤希算法 能保证一致性,也就是同一个字符串 蛤希变换之后结果唯一
- 为了避免 多个用户 使用同一个密码导致的蛤希后结果一致的情况,增加一个salt
- 可以在每次蛤希过程中随机向原始password中插入字符串,来确保蛤希结果的大概率不一致性
- 用于混淆hash结果的salt会存储在hash结果中
通俗的解释就是,
hash(str) 可以保证对不同的str结果唯一
hash(str + random salt)可以保证对相同的str结果不相同
salt 会存储在hash结果里
$2b$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
比如上面来自bcrypt 文档的介绍 ,
- $2b指使用了 bcrypt的某个算法
- $10指 saltRound是 2^10
- $nOUIs5kJ7naTuTFkBy1veu是salt
- 剩余部分是蛤希结果
真正的脏活由高手来接手 bcrypt
npm i bcrypt @types/bcrypt
上面两条应该能让你安装 bcrypt包和对应的type定义
const passwordInPlainText = '12345678';
const hashedPassword = await bcrypt.hash(passwordInPlaintext, 10);
const doPasswordsMatch = await bcrypt.compare(passwordInPlaintext, hashedPassword);
console.log(doPasswordsMatch); // true
- hash和比较过程都必须使用异步形式,否则会导致堵塞
- 所以使用bcrypt 的方法就是 先hash,存到数据库,在需要的时候(验证)取出来hash后的结果并进行比较
路由 注册和登录
- 要注意createUserDto和 logginDto不一致,不应该使用前者 +skipMissingProperty(class-validator提供的功能,简写为SMP)
- 虽然理论上,没有提供正确的email不会在数据库中查到对应数据,但是我认为这可能导致未知的错误发生,还是应该明确logginDto内容。
- 举例说明,假设攻击者传入密码为空的登录信息,那么使用 "SMP",验证中间件是会通过验证,假设后续的控制器逻辑中没有验证密码,就是致命错误
- 之前没有涉及trim掉空格的逻辑,这里在 dto验证的时候加上了对应的transform 装饰器
- 所以注册和登录的路由也要使用不同的dto来进行验证
用户登录后的 token发放
- 假设应用的功能A需要用户登录后才能使用,jwt可以帮助完成这个任务
- 当用户登录成功,发送一个jwt签发的token给用户
- 用户使用A功能的时候,在header里发送token给服务器
- 服务器进行校验,如果校验成功,就说明用户有权使用这个功能
- 注意,发放和校验token要使用到秘钥,这个秘钥不能暴露,需要保存良好,一般通过process.env注入到应用中
用户使用某功能的鉴权
- 上面登录成功后,服务器发放给客户端的token可以设置在header中一并发还客户端
- 这时,对应某个 功能,就要specifically 使用 authMiddlewar ,其中的逻辑,就是借用jwt.verify来校验token是否是有效内容
- 在token中 不能放置任何敏感的信息,建议只放一条_id。
退出登录
- Set-Cookie 中 Authorization= 可以清空token内容
- MaxAge=0 可以立刻清空客户端中的
这条 cookie
- 但是这里还是有点小问题,我想你一定知道如何解决(笑)
【转】JAVA中Cookie MaxAge属性及其使用