zoukankan      html  css  js  c++  java
  • 服务端编程——登录阶段(含JsonWebToken使用)

    登录阶段

    用户要传入的信息

    • 首先肯定要传入account和secret 也就是账号和密码,但在小程序里登录时不止有一种方式,比如可以直接从微信进去 这样就不需要密码了,因此密码要设置为可选填项

      • 添加对token的api以及TokenValidator校验器
    • 其次还要传入type,也就是进入途径,由于js没有枚举器,所以在api/v1/lib下新建文件enum.js来模拟枚举

      • 其实里面就是一个对象LoginType,其中有各种进入方式(type)对应的值
      • 设置一个isLoginType函数来判断当前的type是否属于里面的值 也就是是否合法;这里要遍历的是对象中的key值,还不太方便... 但可以将函数也定义在enum.js中,并将其放到LoginType对象里,之后都通过对象来调用,就能直接用this拿到所有key值:
    function isThisType (val) {
      for (let key in this) {
        if (this[key] === val) {
          return true
        }
      }
      return false
    }
    
    const loginType = {
      USER_MINI_PROGRAM: 100,
      USER_EMAIL: 101,
      USER_MOBILE: 102,
      ADMIN_EMAIL: 200,
      isThisType
    }
    
    module.exports = {
      loginType
    }
    

    验证阶段

    用户发送完相应请求后,要做以下几件事情:

    • 检验各项信息是否都按照要求填写,格式符不符合规范:设置一个TokenValidator校验器专门校验登录时的信息
      • 暂时只包括 account、secret、type(进入方式,刚刚说了验证方法)
    • 根据当前进入方式来选择不同的处理函数:用switch...case语句,调用不同的函数
      • 如:当前是用邮箱登录的 即type===LoginType.USER_EMAIL,则调用emailLogin函数(同步的),这个函数写在发送api的token.js文件中(当前文件)
      • emailLogin中调用Model中定义的方法verifyEmailPassword来验证账号密码是否正确:为什么要在Model中定义而不是直接卸载emailLogin中呢,因为这个方法是验证账号密码的,也就是要操控数据库中的数据,因此还是写sequelize中的Model中比较好(注意也要是同步的)
      • verifyEmailPassword中先看数据库中有没有输入的账号(User.findOne()),若不存在直接throw一个NotFound的error,存在的话就看当前输入的密码和数据库中相应密码是否匹配。这里要注意,数据库中保存的是加密的密码 而输入的是明文的,因此要用bcrypt的compareSync方法来判断密码是否匹配正确,不正确就抛出一个AuthorFailed的error
    • 注意以上所有函数要加上async和await让其同步,且返回值基本都是Promise,要得到它的确切返回值一定要在回调函数(.then())中拿,或者是在前面加上await把promise中的值计算出来
    // /app/api/v1/token.js
    router.post('/', async (ctx, next) => {
      const v = await new TokenValidator().validate(ctx)
      const type = v.get('body.type')
      const account = v.get('body.account')
      const secret = v.get('body.secret')
      switch (type) {
        case loginType.USER_EMAIL:
          await emailLogin(account, secret)
          breakb
        case loginType.USER_MINI_PROGRAM:
          break
        default:
          throw new global.errs.ParameterException('没有相应的处理函数')
      }
    })
    
    async function emailLogin (account, secret) {
      const res = await User.verifyEmailPassword(account, secret)
    }
    
    // /modules/user.js
    class User extends Model {
        static async verifyEmailPassword (email, secret) {
          // 数据库查询是个异步操作 也要加await
          const res = await User.findOne({
            where: {
              email
            }
          })
          if (!res) {
            throw new global.errs.NotFound('账号不存在')
          }
          const correct = bcrypt.compareSync(secret, res.password)
          if (!correct) {
            throw new global.errs.AuthorFailed('密码不正确')
          }
          return res
        }
    }
    

    jwt——Json Web Token

    就是一种便于客户端与服务器通信的令牌,详见

    项目中使用

    • koa项目中引入basic-auth包,在/middlewares/auth.js中写一个Auth类用来校验jwt令牌

    • 引入base-auth中的basicAuth函数,将当前http请求对象传入即可得到token

      • 在koa项目中,中间件中的上下文ctx:ctx.request是被koa封装处理过后的请求对象,而ctx.req才是原生的http请求对象
    • 在要验证的地方,将这个Auth类导入,直接将刚刚写的函数当作中间件导入,注意顺序哦,一定要在执行操作之前就将其导入,这样才能起到截断的作用(在router函数中可以导入多个中间件)

    • Auth类中的校验函数里通过当前发送的http请求获取到token后,大致分为三步

      • 先看token和token.name(内容)是否存在,若不存在直接抛出Forbidden的error
      • 再看token的值是否合法——通过jsonwebtoken中的verify方法,传入两个参数 一个是当前获取到的token,另一个是配置文件中自己写好的secretKey
      • 第二步中的verify方法若错误则会抛出异常,所以要用try...catch语句来调用,其中catch中要查看当前error的名字来区分到底是 token过期 还是token不合法
    • token通过校验成功之后,会返回一个对象,内容就是在生成token的时候自己传进去的第一个参数(想要token携带的信息),可以将其保存到ctx中,在后续中间件都可以使用到

      get m () {
        return async (ctx, next) => {
          const errMsg = 'token不合法'
          // ctx.request === koa 对请求封装了
          // ctx.req === 原生的http请求
          const userToken = basicAuth(ctx.req)
          if (!userToken || !userToken.name) {
            throw new global.errs.Forbidden(errMsg)
          }
          try {
            var decode = jwt.verify(userToken.name, global.config.security.secretKey)
          } catch (error) {
            if (error.name === 'TokenExpiredError') {
              throw new global.errs.Forbidden('token已过期')
            }
            throw new global.errs.Forbidden(errMsg)
          }
          ctx.auth = {
            uid: decode.uid,
            scope: decode.scope
          }
          await next()
        }
      }
    
  • 相关阅读:
    使用 jQuery Uploader 显示文件上传进度
    Uploadify插件上传图片并且插入到FCKeditor
    Flex与.NET互操作(一):基于Socket的网络连接
    JQuery上传插件Uploadify使用详解
    让自定义登录程序像 Membership 一样
    Stream 和 Byte[] 互操作
    AspNet控件开发(1)入门介绍
    Visual Studio 2008 集成SP1补丁
    asp.net 解决下载文件–中文名乱码问题
    不错的一个JS框架
  • 原文地址:https://www.cnblogs.com/TRY0929/p/13855748.html
Copyright © 2011-2022 走看看