Browse Source

增加扫码登录

liuyu 5 năm trước cách đây
mục cha
commit
72203ded87
5 tập tin đã thay đổi với 82 bổ sung17 xóa
  1. 1 1
      app.js
  2. 14 0
      app/controller/login.js
  3. 4 4
      app/controller/weixin.js
  4. 1 0
      app/router.js
  5. 62 12
      app/service/login.js

+ 1 - 1
app.js

@@ -8,7 +8,7 @@ class AppBootHook {
     // 应用已经启动完毕
     const ctx = await this.app.createAnonymousContext();
     // 企业入驻申请消息接收事件
-    await ctx.service.rabbitmq.receiveQueueMsg('user_bind');
+    // await ctx.service.rabbitmq.receiveQueueMsg('user_bind');
   }
 
   async serverDidReady() {

+ 14 - 0
app/controller/login.js

@@ -20,6 +20,20 @@ class LoginController extends Controller {
     const res = await this.service.wxlogin(this.ctx.request.body);
     this.ctx.ok({ data: res });
   }
+
+  // POST 生成二维码
+  async qrcode() {
+    const res = await this.service.createQrcode();
+    this.ctx.ok({ data: res });
+  }
+
+  // POST 检查二维码
+  async check() {
+    const { qrcode } = this.ctx.requestparam;
+    const res = await this.service.checkQrcode(qrcode);
+    this.ctx.ok(res);
+  }
+
 }
 
 module.exports = LoginController;

+ 4 - 4
app/controller/weixin.js

@@ -25,7 +25,7 @@ class WeixinController extends Controller {
   //       store - 默认,认证结果写入sessionStore,然后重定向回请求页面(要求请求页面和认证服务在同一域名下)
   //       token - url带上token参数重定向到原始地址
   async auth() {
-    const { redirect_uri, code, test, type, response_type = 'store', uid } = this.ctx.query;
+    const { redirect_uri, code, test, type, response_type = 'store', uid, qrcode } = this.ctx.query;
     if (test) {
       return await this.authTest();
     }
@@ -43,7 +43,7 @@ class WeixinController extends Controller {
     // TODO: 保存原始请求地址
     const state = uuid();
     const key = `visit:auth:state:${state}`;
-    const val = JSON.stringify({ redirect_uri, type, uid });
+    const val = JSON.stringify({ redirect_uri, type, uid, qrcode });
     await this.app.redis.set(key, val, 'EX', 600);
 
     // TODO: 生成回调地址
@@ -75,7 +75,7 @@ class WeixinController extends Controller {
     if (openid) {
       const key = `visit:auth:state:${state}`;
       const val = await this.app.redis.get(key);
-      const { redirect_uri, type, uid } = JSON.parse(val);
+      const { redirect_uri, type, uid, qrcode } = JSON.parse(val);
       console.log('redirect_uri-->' + redirect_uri);
       if (type === '0') {
         // 通过openid取得用户信息
@@ -104,7 +104,7 @@ class WeixinController extends Controller {
         // TODO: 重定性页面
         this.ctx.redirect(to_uri);
       } else if (type === '2') {
-        const to_uri = urljoin(redirect_uri, `?openid=${openid}&type=${type}`);
+        const to_uri = urljoin(redirect_uri, `?openid=${openid}&type=${type}&qrcode=&{qrcode}`);
         // TODO: 重定性页面
         this.ctx.redirect(to_uri);
       }

+ 1 - 0
app/router.js

@@ -148,6 +148,7 @@ module.exports = app => {
   // pc端登录
   router.post('/api/train/login', controller.login.login);// 登录
   // 微信端登录
+  router.get('/api/train/qrcode', controller.login.qrcode);// 登录
   router.post('/api/train/wxlogin', controller.login.wxlogin);// 登录
 
   // 评分表设置路由

+ 62 - 12
app/service/login.js

@@ -6,6 +6,7 @@ const { ObjectId } = require('mongoose').Types;
 const { CrudService } = require('naf-framework-mongoose/lib/service');
 const { BusinessError, ErrorCode } = require('naf-core').Error;
 const jwt = require('jsonwebtoken');
+const uuid = require('uuid');
 
 class LoginService extends CrudService {
   constructor(ctx) {
@@ -66,27 +67,76 @@ class LoginService extends CrudService {
   }
 
   async wxlogin(data) {
-    const { openid } = data;
+    const { qrcode, openid } = data;
+    assert(qrcode, 'qrcode不能为空');
     assert(openid, 'openid不能为空');
+    const key = `naf:qrcode:login:${qrcode}`;
+    const status = await this.app.redis.get(key);
+    if (!status) {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
+    }
+    if (status !== 'pending') {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码状态无效');
+    }
     const res = await this.uModel.findOne({ openid });
     if (!res) {
       throw new BusinessError(ErrorCode.USER_NOT_EXIST);
     }
     const result = await this.createJwt(res);
+    // TODO: 修改二维码状态,登录凭证保存到redis
+    await this.app.redis.set(key, `scaned:${result}`, 'EX', 600);
+    // TODO: 发布扫码成功消息
     const { mq } = this.ctx;
+    const ex = 'qrcode.login';
     if (mq) {
-      const msg = res.name + '微信登录成功';
-      const parm = {
-        durable: true,
-        headers: {
-          token: result,
-        } };
-      console.log(parm);
-      console.log(msg);
-      await mq.topic('wx_login', res.id, msg, parm);
-    } else {
-      this.ctx.logger.error('!!!!!!没有配置MQ插件!!!!!!');
+      await mq.topic(ex, qrcode, 'scaned', { durable: true });
+    }
+  }
+
+  /**
+   * 创建二维码
+   * 随机生成二维码,并保存在Redis中,状态初始为pending
+   * 状态描述:
+   * pending - 等待扫码
+   * consumed - 使用二维码登录完成
+   * ${jwt.token} - Jwt登录凭证
+   */
+  async createQrcode() {
+    const qrcode = uuid();
+    const key = `naf:qrcode:login:${qrcode}`;
+    await this.app.redis.set(key, 'pending', 'EX', 600);
+    return qrcode;
+  }
+
+  // 使用二维码换取登录凭证
+  async qrcodeLogin(qrcode) {
+    assert(qrcode, 'qrcode不能为空');
+    const key = `naf:qrcode:login:${qrcode}`;
+    const val = await this.app.redis.get(key);
+    if (!val) {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
+    }
+    const [ status, token ] = val.split(':', 2);
+    if (status !== 'scaned' || !token) {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码状态无效');
+    }
+
+    // TODO: 修改二维码状态
+    await this.app.redis.set(key, 'consumed', 'EX', 600);
+
+    return { token };
+  }
+
+  // 检查二维码状态
+  async checkQrcode(qrcode) {
+    assert(qrcode, 'qrcode不能为空');
+    const key = `naf:qrcode:login:${qrcode}`;
+    const val = await this.app.redis.get(key);
+    if (!val) {
+      throw new BusinessError(ErrorCode.SERVICE_FAULT, '二维码已过期');
     }
+    const [ status ] = val.split(':', 2);
+    return { status };
   }
 }
 module.exports = LoginService;