小程序登录流程

思路:首先在小程序端通过wx.login向微信服务器发送请求拿到code,然后把这个code发送到我方服务器,我方服务器拿到前端发来的code之后,携带code, appid,secret,grant_type:'authorization_code这四个参数向微信服务器'https://api.weixin.qq.com/sns/jscode2session,这个接口发送请求拿到openid与session_key,并通过openid进行查询数据库里面这条信息看看有没有手机号,有的话就是老用户,并把这条信息发给前端,如果没有手机号,就说明是新用户,然后后端并把openid发送给前端,还携带一个isnew:true,小程序端按照这个判断获取手机号的按钮是否显示,前端在页面定义一个变量进行存一下这两个参数

接下来是新用户的思路因为是新用户,传过去的isnew:true,所以在小程序界面有两个按钮,一个是用来获取用户信息 wx.getUserProfile,一个是用来获取手机号<button wx:if="{{avatarUrl}}" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">绑定手机号</button>,用来获取用户信息先显示,因为他通过isnew:true判断是否显示,获取用户信息之后,变量存一下,这时候获取手机号是通过用户信息是否存在来控制显示,这时候获取手机号的按钮就会显示,在点击获取手机号按钮的时候,会传一个code,这个code与wx.login不一样,这时候前端调用一个接口,把用户信息与code,openid发给后端,后端接收到这些参数之后,首先通过这个接口获取access_token,需要两个参数还是appid与secret

`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}

获取到access_token之后通过它为参数,并携带第二个code进行获取用户手机号,调用下面这个接口

 const phoneUrl = `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${access_token}`
  const { data } = await axios.post(phoneUrl,{ code })

此时拿到用户手机号.通过这个手机号去数据库查一下数据,要是存在就说明这个手机号已经绑定,要是不存在就把,手机号,openid,用户信息存到数据库里面,存完之后在取一下这条数据返回给前端,前端拿到用户信息,把头像与昵称进行渲染

拉新实现原理:

就是在小程序第二次返回后端的数据加上分享人id,与活动id,然后后端进行入库,通过用户表里面的分享人id,与活动id,进行返现

被灰产薅羊毛的漏斗

被灰产薅羊毛的漏斗就是,把手机号返回给了前端,前端通过调用接口进行入库,这样只要改变手机号,就会创建新用户进行返现,只要把手机号存在后端,在后端进行存库就解决了

后端代码

const Router = require("koa-router");
const user = require('../db/model/userModel')
const {secret} = require("../config/config")
const jsonWebToken = require("jsonwebtoken")
const axios = require('axios')

const router = new Router({prefix:'/user'})

router.post('/wxlogin',async (ctx)=>{
  // 从body获取用户发送的参数
  let {code} = ctx.request.body //这里的code是小程序前端通过wx.login获取到code,传到后端
  // 请求微信的服务器 用code 换取opendid  用户在微信的主键id 
  let url ='https://api.weixin.qq.com/sns/jscode2session'
  // 携带的参数
  let params={
    appid:'wx5e6344cedcbf76a9', //在微信公众平台上的参数
    secret:'6259cadba107f92719c8c3f36d77cade',//在微信公众平台上的参数
    js_code:code,//前端返回来的code
    grant_type:'authorization_code'//授权类型,此处只需填写 authorization_code,写死就行
  }
  let result = await axios.get(url,{params:{...params}})
  let {openid,session_key} = result.data//通过请求可以拿到openid
  //  将wx的openid 和 自己自己的用户系统进行关联 将opendid 和session_key 存入数据库 
  console.log(openid)
  // 根据opendid 获取 用户数据
  let  existRecord = await user.findOne({openid}) //通过openid去数据库查看是否有这条数据
  console.log(existRecord)
  const { phone } = existRecord || {} //通过这条数据去看里面是否有phone,有就是老用户,没有就是新用户
  if(phone){ 
    // 手机号存在是老用户
    let {address,phone,balance,_id,userName, avatarUrl, nickName} = existRecord
    let token = jsonWebToken.sign( {address,phone,balance,_id,userName},secret,{expiresIn:"1d"}) //是老用户的话直接把这些字段生成token返回给前端
    ctx.body ={code:0,msg:'登录成功',token,uid:_id, avatarUrl, nickName, isNew: false}
      //注意这里的isNew前端通过这个参数,来判断获取手机号的两个按钮是否显示,要是老用户是false就不显示
  }else{
    ctx.body ={code:0,msg:'用户不存在',isNew:true, openid} //这里把openid返回给前端存一下,通过openid为参数进行获取用户手机号
  }
})
//下面这个方法,获取手机号的接口是在前台进行接口调用,前台获取到手机号之后,随便修改手机号,调用wxBindPhone存数据库的接口,就会不不停的往数据库插入数据,也就创建多个新用户了,容易让灰产薅羊毛
router.post('/wxGetPhone',async (ctx)=>{
  const appid = 'wx5e6344cedcbf76a9';
  const secret = '6259cadba107f92719c8c3f36d77cade';
  // 从body获取用户发送的参数
  let {code} = ctx.request.body //获取一下前端发来的code,这个code与wx.login返回的code不一样,,这里是获取手机号里面的事件自带的
  // 获取access_token 
  const  tokenurl = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}` 
  const  tokenResult = await axios.get(tokenurl)
  const  {access_token} = tokenResult.data//首先调用微信获取access_token 
  // 根据token 和 code 解密手机号返回给前端
  const phoneUrl = `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${access_token}`
  const result = await axios.post(phoneUrl,{ code }) //通过code去获取手机号返回给前端
  ctx.body ={code:0,msg:'登录成功',data: result.data.phone_info}
 
})

router.post("/wxBindPhone",async (ctx) => {
  //下面是对薅羊毛的改造,不在让前端进行获取手机号,进行掉接口存数据,这里手机号通过后端获取,然后直接存
  const { nickName, avatarUrl , openid, code } = ctx.request.body
  //如果是新用户,用户有个按钮首先会调用wx.getUserProfile来获取用户的头像与昵称,openid是在login获取并存在前台的
  //下面的跟上面一样
  const appid = 'wx5e6344cedcbf76a9';
  const secret = '6259cadba107f92719c8c3f36d77cade';
  // 获取access_token 
  const  tokenurl = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`
  const  tokenResult = await axios.get(tokenurl)
  const  {access_token} = tokenResult.data
  // 根据token 和 code 解密手机号返回给前端
  const phoneUrl = `https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${access_token}`
  const { data } = await axios.post(phoneUrl,{ code })
  console.log(data)//在这里获取到手机号并没有进行发给前端,而是直接在后端进行存数据库,并不需要前端携带手机号等信息进行调用接口,存储数据,这样就不会呗薅羊毛
  let  phoneNumber = "" //首先给手机号一个初始值
  if(data.errcode) { //如果报错的时候进行返回false
    ctx.body = data
    return false
  } else { //这里拿到手机号
      phoneNumber = data.phone_info.phoneNumber 
  }
  console.log(ctx.request.body)
  let  isExist = await user.findOne({phone: phoneNumber})//通过手机号到数据库查询数据
  if(isExist){
    // 手机号已存在
    ctx.body ={ code:-1,msg:'手机号已绑定' }
  }else{ //数据库没有这个手机号就说明是新用户,就把前端返回来的数据加上手机号存到数据库里面
    let  insertR = await user.insertMany({openid,nickName, avatarUrl, phone: phoneNumber})
    let userInfo = await user.findOne({openid, phone:phoneNumber }) //存完数据之后在取一下这条数据,并生成token返回前端
    let {address,phone,balance,_id,userName} = userInfo
    let token = jsonWebToken.sign( {address,phone,balance,_id,userName},secret,{expiresIn:"1d"})
    ctx.body ={code:0,msg:'登录成功',userInfo, token}
  }
  console.log("xxxxx")
})

小程序代码

login.wxml

<view>
  <button bindtap="login"> 登录 </button>
  <block wx:if="{{isNew}}">
    <view class="model">
      <button wx:if="{{avatarUrl}}" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">绑定手机号</button>
      <button wx:else bindtap="getUserProfile"> 绑定手机号 </button>
    </view>
  </block>
</view>

login.js

const {wxlogin, getPhone, wxBindPhone} = require("./service")
const storage = getApp().storage;
Page({
  openid: null,
  phone: null,
  data: {
    isNew: false,
  },
  getUserProfile() {
    wx.getUserProfile({
      desc: 'desc',
      success: (res) => {
        const {
          avatarUrl,
          nickName
        } = res.userInfo
        this.setData({
          avatarUrl,
          nickName
        })
      }
    })
  },
  login() {
    wx.login({
      success: async (res) => {
        const { openid, isNew, nickName, token,avatarUrl } = await wxlogin({ code: res.code }, true) || {}
        this.openid = openid
        if(isNew) {
          this.setData({isNew})
        } else {
          storage.set("token",token)
          storage.set("userInfo",{ nickName, avatarUrl})
          wx.reLaunch({
            url: '/pages/my/my',
          })
        }
      }
    })
  },
  // 获取手机号
  async getPhoneNumber(e) {
    // const result = await getPhone({ code: e.detail.code })
    const payload = {
      openid: this.openid,
      code: e.detail.code,
      nickName: this.data.nickName,
      avatarUrl: this.data.avatarUrl,
      // 邀请人id
      // 活动id
    }
    const res = await wxBindPhone(payload)
    const { token, userInfo } = res || {}

    storage.set("token",token)
    storage.set("userInfo", userInfo)
    this.setData({ isNew: false })
  },

})

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值