手把手实现第三方社交登录方式微信登录

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 手把手实现第三方社交登录方式微信登录

为什么要使用第三方社交登录吗?

  • 社交登录
  • 含义:访问某个网站的用户可以通过社交媒体账号登录
  • 优点
  • 可以利用社会化媒体的影响增加网站和用户粘性,提高留存
  • 借助分享、裂变等方式对网站进行推广,提高知名度 AARRR中的留存和传播
  • 同时增加网站的流量,使用量,提高网站产品的转换率
  • 类型
  • 腾信微信
  • 腾讯QQ
  • 阿里支付宝
  • 微博
  • .....
  • 微信登录
  • 场景二维码登录
  • Oauth2.0授权登录

特别注意: 订阅号不能实现微信登录服务,服务号才可以实现,但是微信认证也要300大洋,我的钱,呜呜呜。

微信登录业务逻辑梳理-系统时序图

  • 时序图
  • 介绍微信登录从接入微信服务器node后端开发整个链路
  • 整个链路比较复杂,当前先宏观上了解整个流程,但是我会详细地讲解每个流程逻辑
  • 如果有部分细节不理解没关系,在微信登录实战开发时结合时序图都会一一掌握
  • 时序图的组成元素
  • 对象
  • 扮演的角色,大矩形表示
  • 生命线
  • 对象存活 的事件对象下的虚线
  • 控制焦点
  • 在这个时间段执行响应的操作,小矩形标识
  • 消息
  • 发送消息:实线箭头
  • 返回消息:虚线箭头
  • 处理消息:自身调用,处理数据

image.png

深入理解微信服务器的接口授权接入

  • 自身的node服务器,并且提供一个给微信服务器回调的接口
  • 按照微信的要求,验证请求消息是来自微信服务器
  • 公众号的全局唯一接口调用凭据 access_token 的获取
  • 微信公众账号测试号申请系统
  • 微信提供测试的appID和appsecret
  • 回调接口的接入配置测试
  • 要将自己本地服务的地址映射成外网可访问的域名
  • 通过内网穿透工具:

使用花生壳,生成的外网地址固定,不用频繁更换

场景二维码的接入:developers.weixin.qq.com/doc/offiacc…

  • 通过access_token获取ticket
{
  "expire_seconds": 604800,
  "action_name": "QR_SCENE", 
  "action_info": {
    "scene": {"scene_id": 123}
  }
} 

拼接二维码url

  • 微信提供的部分url拼接上获取的ticket,将此url和ticket返回客户端
https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET
  • 扫码之后的操作看时序图流程
  • 微信回调node服务器接口发送的XML格式消息
  • 返回给微信服务器的消息
  • image.png

微信服务器回调本地接口【对称加密】接入验证

微信服务器回调本地接口接入验证

  • 花生壳配置本地地址

image.png

测试访问

https://474y3966p9.goho.co/api/wx_login/v1/callback

image.png

接收微信服务器发送的参数对称加密验证

// sha1加密安装
yarn add sha1@1.1.1
const SecretTool = require('../utils/SecretTool')
const WxLoginService = {
  wechat_insert: (signature, timestamp, nonce, echostr) => {
    // 服务器的token
    let token = 'testxdclass'
    // 将token、timestamp、nonce三个参数进行字典序排序,拼接成一个字符串,进行sha1加密
    let str = SecretTool.sha1([token, timestamp, nonce].sort().join(''))
    // 获得加密后的字符串可与signature对比,验证标识该请求来源于微信服务器
    if (str === signature) {
      // 确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效
      return echostr
    }
  },
}
module.exports = WxLoginService
  • 微信公众平台配置
  • 基本配置
  • image.png
const appId = 'wx5beac15ca207c40c';
const appSecret = '8189e5f14346ccaa3bd5f6909f31a362';
const accessTokenPc = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`;
const getAccess_token = () => {
  return axios({
    method: 'get',
    url: accessTokenPc,
  });
};

请求微信服务器获取 ticket

const ticketReq = (token) => {
  return axios({
    method: 'post',
    url: `https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=${token}`,
    data: {
      expire_seconds: 60 * 2, // 二维码有效时间
      action_name: 'QR_SCENE',
      action_info: { scene: { scene_id: 123 } },
    },
  });
};

获取二维码方法封装

const qrUrl = 'https://mp.weixin.qq.com/cgi-bin/showqrcode';
const wechatLogin = {
  // 获取微信登录二维码
  getQR: async () => {
    let token = (await getAccess_token()).data.access_token;
    console.log((await getAccess_token()))
    console.log('token:', token)
    let ticket = (await ticketReq(token)).data.ticket;
    return { qrcodeUrl: `${qrUrl}?ticket=${ticket}`, ticket: ticket };
  }
}

将 ticket作为key值,{ isScan : false } 转成 json 存入Redis缓存

let key = `wechat:ticket:${ticket}`
redisConfig.set(key, JSON.stringify({ isScan: 'no' }), 120)
  • 返回二维码url+ticket

掌握流数据+xml数据处理接受微信服务器请求参数

  • 创建用户扫码时微信回调接口的开发 POST请求
// controller 控制层
wechat_message: (req, res) => {
  let handleRes = WxLoginService.wechat_message(req)
  res.send(handleRes)
}
// service 数据层
wechat_message: (req, res) => {
  return '成功'
}

处理微信回调的参数

  • 封装数据处理工具类
/**
 * @param {*} getXMLStr 以流的形式接收微信服务器发过来的数据
 * @param {*} getJsData 通过工具解析xml数据转换成对象
 * @param {*} getObjData 优化数据
 */
var { parseString } = require('xml2js');
// 微信XML数据类型处理
class WxDataTool {
  static getXMLStr(req) {
    return new Promise((resolve, reject) => {
      let data = '';
      req.on('data', (msg) => {
        data += msg.toString();
      });
      req.on('end', () => {
        resolve(data);
      });
    });
  }
  static getJsData(data) {
    return new Promise((resolve, reject) => {
      parseString(data, (err, result) => {
        if (err) {
          reject('error');
        } else {
          resolve(result);
        }
      });
    });
  }
  static getObjData(obj) {
    let tempObj = {};
    if (obj && typeof obj === 'object') {
      // 循环对象,提取数据
      for (let key in obj) {
        let value = obj[key];
        if (value && value.length > 0) {
          tempObj[key] = value[0];
        }
      }
      return tempObj;
    } else {
      return tempObj;
    }
  }
};
module.exports = WxDataTool

根据微信回调参数生成权限token+redis缓存状态+xml数据返回

  • 根据openid进行注册/登录
// 根据openid判断是否注册
let openidRes = await DB.Account.findAll({ where: { openid }, raw: true })
// 随机获取头像、用户名
let head_img = RandomTool.randomAvatar()
let username = RandomTool.randomName()
// 无注册则增加个用户数据
let user = null
if (openidRes.length === 0) {
  let resData = await DB.Account.create({ username, head_img, openid })
  user = { head_img, username, id: resData.toJSON().id }
} else {
  user = {
    head_img: openidRes.head_img,
    username: openidRes.username,
    id: openidRes.id
  }
}

生成token

let token = SecretTool.jwtSign(user, '168h')

更新redis缓存中的扫码状态+token

let key = `wechat:ticket:${ticket}`
const existsKey = await redisConfig.exists(key)
if (existsKey) {
  redisConfig.set(key, JSON.stringify({ isScan: 'yes', token }), 120)
}

返回微信xml格式数据

let content = ''
if (msgObj.MsgType == 'event') {
  // 扫码
  if (msgObj.Event == 'SCAN') {
    content = '欢迎回来,讲师微信:xdclass6'
  } else if (msgObj.Event == 'subscribe') {
    // 订阅
    content = '感谢关注,讲师微信:xdclass6'
  }
  // 根据来时的信息格式,重组返回。(注意中间不能有空格)
  let msgStr = `<xml>
    <ToUserName><![CDATA[${lastData.FromUserName}]]></ToUserName>
    <FromUserName><![CDATA[${lastData.ToUserName}]]></FromUserName>
    <CreateTime>${Date.now()}</CreateTime>
    <MsgType><![CDATA[text]]></MsgType>
    <Content><![CDATA[${content}]]></Content>
  </xml>`
  return msgStr
}

客户端怎么知道用户何时扫码?开发轮询接口

image.png创建轮询接口

http://127.0.0.1:8081/api/wx_login/v1/check_scan
请求方法:get
请求参数:ticket

根据ticket参数查询redis缓存

let { ticket } = req.query
let key = `wechat:ticket:${ticket}`
let redisData = JSON.parse(await redisConfig.get(key))

判断是否存在对应key

if (redisData && redisData.isScan == 'yes') {
  let token = redisData.token
  return JsonData.buildSuccessAndData(`Bearer ${token}`)
} else {
  return JsonData.buildResult(CodeEnum.WECHAT_WAIT_SCAN)
}

源代码:

里面都有我每次的commit记录,想要源码的同学可以看下面的链接~

[前端] github.com/mohaixiao/c…

[后端] github.com/mohaixiao/c…

目录
相关文章
|
10天前
|
安全 NoSQL API
拼多多:通过微信支付API实现社交裂变付款的技术解析
基于微信JSAPI构建社交裂变支付系统,用户发起拼单后生成预订单与分享链接,好友代付后通过回调更新订单并触发奖励。集成微信支付、异步处理、签名验签与Redis关系绑定,提升支付成功率与裂变系数,实现高效安全的闭环支付。
135 0
|
2月前
|
移动开发 小程序 安全
微信API社交裂变工具,老带新流量成本归零!
在数字化营销时代,利用微信API构建社交裂变工具,可实现“老带新”的病毒式传播,大幅降低获客成本。本文详解如何通过微信API实现零成本流量增长,解析裂变机制与技术实现。
96 0
|
7月前
|
存储 移动开发 小程序
校园圈子系统小程序(圈子论坛、私信聊天、资料共享、二手交易、兼职,跑腿)开源码开发/微信公众号、小程序、H5多端账号同步/搭建多城市的综合社交生活平台
基于开源技术栈构建的校园圈子系统小程序,整合社交与生活服务功能,涵盖兴趣圈子、私信聊天、资料共享、二手交易、兼职跑腿等六大核心模块。通过多端账号同步(微信公众号/小程序/H5),实现数据实时交互,满足学生群体的多元化需求。项目精准锚定校园市场,以“社交+服务”双轮驱动,提供一站式解决方案,支持快速部署与多校区运营,同时具备广告、佣金、会员等多元变现能力,是打造校园生态的理想工具。
609 2
校园圈子系统小程序(圈子论坛、私信聊天、资料共享、二手交易、兼职,跑腿)开源码开发/微信公众号、小程序、H5多端账号同步/搭建多城市的综合社交生活平台
|
8月前
|
存储 小程序 前端开发
微信小程序与Java后端实现微信授权登录功能
微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验
2438 12
|
9月前
|
存储 缓存 关系型数据库
社交软件红包技术解密(六):微信红包系统的存储层架构演进实践
微信红包本质是小额资金在用户帐户流转,有发、抢、拆三大步骤。在这个过程中对事务有高要求,所以订单最终要基于传统的RDBMS,这方面是它的强项,最终订单的存储使用互联网行业最通用的MySQL数据库。支持事务、成熟稳定,我们的团队在MySQL上有长期技术积累。但是传统数据库的扩展性有局限,需要通过架构解决。
192 18
|
9月前
|
存储 缓存 监控
社交软件红包技术解密(四):微信红包系统是如何应对高并发的
本文将为读者介绍微信百亿级别红包背后的高并发设计实践,内容包括微信红包系统的技术难点、解决高并发问题通常使用的方案,以及微信红包系统的所采用高并发解决方案。
259 13
|
9月前
|
存储 监控 容灾
社交软件红包技术解密(五):微信红包系统是如何实现高可用性的
本次分享介绍了微信红包后台系统的高可用实践经验,主要包括后台的 set 化设计、异步化设计、订单异地存储设计、存储层容灾设计与平行扩缩容等。听众可以了解到微信红包后台架构的设计细节,共同探讨高可用设计实践上遇到的问题与解决方案。
253 5
|
11月前
|
小程序 前端开发 算法
|
11月前
|
存储 监控 算法
社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节
本文将由微信团队工程师张文瑞分享微信春节摇一摇红包技术背后的方方面面,希望能给同行们带来启发。
220 1
|
12月前
|
移动开发 前端开发 Android开发
开发指南059-App实现微信扫描登录
App是用uniapp开发的,打包为apk,上传到安卓平板中使用

热门文章

最新文章