网站利用扫码关注公众号实现一键登录(上)

网站利用扫码关注公众号实现一键登录
最近接了一个单子,其中有个需求是需要扫码关注公众号实现网站登陆,由于篇幅过长,可能需要分成两篇跟大家分享,今天先分享微信公众号部分的对接,下一篇再分享关注后如何实现登陆。
我这里项目是用go语言开发,这里就按照go语言进行演示,大家可以参考后根据自己实际情况进行开发。
一、对接微信公众号accessToken
//缓存key名称 const wxPublicAccessTokenKey = "WxPublicAccessToken" // 获取公众号accessToken func WxPublicGetAccessToken(appId, secret string) (int, string) { //判断是否存在缓存 keyExit, _ := model.Exist(wxPublicAccessTokenKey) if keyExit != false { //提取数据 accessTokenString := "" _, accessToken := model.StringGet(wxPublicAccessTokenKey, accessTokenString) //返回 return 200, accessToken } else { //请求url apiUrl := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + secret //设置请求方式 req, _ := http.NewRequest("GET", apiUrl, nil) req.Header.Add("cache-control", "no-cache") //执行请求 res, _ := http.DefaultClient.Do(req) //关闭链接 defer res.Body.Close() //提取数据 result, err := io.ReadAll(res.Body) if err != nil { return 404, "结构解析异常" } //处理json var resultList interface{} errResult := json.Unmarshal(result, &resultList) if errResult != nil { return 404, "结构转化失败" } //提取数据 resultDataList, _ := resultList.(map[string]interface{}) //判断是否存在错误信息 errMsg, errMsgErr := resultDataList["errmsg"].(string) if errMsgErr { return 404, errMsg } //提取参数 accessToken := resultDataList["access_token"].(string) expiresIn := resultDataList["expires_in"].(float64) //设置过期时间 expiresInOutTime := int(expiresIn) - 60*20 //设置缓存 err = model.StringSet(wxPublicAccessTokenKey, accessToken, expiresInOutTime) if err != nil { return 404, "设置缓存失败" } //返回 return 200, accessToken } }
这里我是将accessToken放在redis缓存里面,优先查询是否存在缓存,如果不存在则再调取接口进行获取,然后再次存放在缓存里,这里可以避免过度调用接口,redis部分我就不贴出来了,仅供大家参考。
二、生成微信公众号二维码
// 获取公众号临时二维码 func WxPublicCreateQrCode(accessToken, sceneStr string) (int, string) { //请求地址 qrCodeUrl := "https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=" + accessToken //组合动作参数 var actionInfoParams = map[string]interface{}{ "scene": map[string]interface{}{ "scene_str": sceneStr, }, } //组合参数 var param = map[string]interface{}{ "expire_seconds": 300, "action_name": "QR_STR_SCENE", "action_info": actionInfoParams, } jsonStr, _ := json.Marshal(param) //转化数据 postData := bytes.NewBuffer(jsonStr) //发起请求 response, err := http.Post(qrCodeUrl, "application/json;charset=utf-8", postData) if err != nil { return 404, "请求失败" } //关闭链接 defer response.Body.Close() result, err := io.ReadAll(response.Body) if err != nil { return 404, "结构解析异常" } //处理json var resultList interface{} errResult := json.Unmarshal(result, &resultList) if errResult != nil { return 404, "结构转化失败" } //提取数据 resultDataList, _ := resultList.(map[string]interface{}) //判断是否存在错误信息 errMsg, errMsgErr := resultDataList["errmsg"].(string) if errMsgErr { return 404, errMsg } //获取ticket ticket := resultDataList["ticket"].(string) //返回 return 200, ticket }
这里我们调取公众号临时二维码接口生成二维码,我们需要生成一个唯一的ID值生成公众号场景值,这样在用户扫码进入公众号的时候,会将该值携带请求到我们设置回调地址,方便我们区分。
三、处理公众号事件回调
我们还需要开发一个回调接口来处理公众号回调事件,这里我们需要接收公众号用户扫码跟关注事件处理,然后将上一步生成的唯一ID跟公众号用户ID进行绑定,这里我们需要处理GET跟POST请求,GET请求只会在确认回调地址的时候触发,公众号事件请求则是通过POST请求进行触发,代码如下
回调地址确认接口
/** 微信公众号回调处理【GET:首次确认】 */ func (c *WxLoginController) GetPublicCallBack() string { //获取用户参数 signature := params.FormValueDefault(c.Ctx, "signature", "") //密钥 timestamp := params.FormValueDefault(c.Ctx, "timestamp", "") //时间戳 nonce := params.FormValueDefault(c.Ctx, "nonce", "") //随机数 echostr := params.FormValueDefault(c.Ctx, "echostr", "") //随机字符串 //获取配置 publicToken := "公众号回调地址设置的token" //将token、timestamp、nonce三个参数进行字典序排序 var tempArray = []string{publicToken, timestamp, nonce} sort.Strings(tempArray) //将三个参数字符串拼接成一个字符串进行sha1加密 var sha1String string for _, v := range tempArray { sha1String += v } h := sha1.New() h.Write([]byte(sha1String)) sha1String = hex.EncodeToString(h.Sum([]byte(""))) //获得加密后的字符串可与signature对比 if sha1String == signature { //输出解密成功 return echostr } else { //返回 return "false" } }
这里我们验证成功后,直接将echostr输出即可完成验证,token需要跟公众号回调地址配置上面一致
事件回调处理接口
我们还需要开发一个接收POST请求的接口,用于处理事件
// 定义一个结构体用于解析微信传来的消息 type WxCallbackMessage struct { ToUserName string `xml:"ToUserName"` FromUserName string `xml:"FromUserName"` CreateTime int `xml:"CreateTime"` MsgType string `xml:"MsgType"` Event string `xml:"Event"` EventKey string `xml:"EventKey"` Ticket string `xml:"Ticket"` } /** 微信公众号回调处理【POST:事件推送】 */ func (c *WxLoginController) PostPublicCallBack() string { //获取用户参数 signature := params.FormValueDefault(c.Ctx, "signature", "") //密钥 timestamp := params.FormValueDefault(c.Ctx, "timestamp", "") //时间戳 nonce := params.FormValueDefault(c.Ctx, "nonce", "") //随机数 //获取配置 publicToken := "微信公众号后台配置的Token" //对参数进行字典序排序并进行加密验证 var tempArray = []string{publicToken, timestamp, nonce} sort.Strings(tempArray) var sha1String string for _, v := range tempArray { sha1String += v } // 使用 SHA1 对字符串进行加密 h := sha1.New() h.Write([]byte(sha1String)) sha1String = hex.EncodeToString(h.Sum(nil)) // 验证签名 if sha1String == signature { // 微信消息的处理:解析传递过来的 XML 消息 var message WxCallbackMessage if err := xml.NewDecoder(c.Ctx.Request().Body).Decode(&message); err != nil { return "Failed to parse message" } println("事件", message.Event) println("动作", message.EventKey) //获取基本信息 fromUserName := message.FromUserName //用户openId eventKey := message.EventKey //额外参数 eventEvent := message.Event //事件 // 处理关注事件 if eventEvent == "subscribe" { //判断获取令牌 if len(eventKey) > 0 && strings.Contains(eventKey, "qrscene") { loginToken := strings.Replace(eventKey, "qrscene_", "", -1) if loginToken != "" { //创建一条公众号+令牌登录信息 ........创建一条公众号openId、sceneStr、状态关联记录 } } } // 处理扫码进入公众号 if eventEvent == "SCAN" { //判断获取令牌 if len(eventKey) > 0 && len(eventKey) == 32 { loginToken := eventKey if loginToken != "" { //创建一条公众号+令牌登录信息 ........创建一条公众号openId、sceneStr、状态关联记录 } } } // 处理取消关注事件 if eventEvent == "unsubscribe" { } // 默认返回消息 return "Success" } // 不是微信的请求,返回错误提示 return "false" }
上面我们需要处理用户关注事件以及用户扫描进入公众号事件,在接收到用户openId以及sceneStr值之后,我们需要创建一条记录,用来关联两者并且还需要有一个状态值,如果扫码关注之后,将状态值更改为有效,用户端在登陆之后,则改成失效状态。
四、生成微信公众号二维码接口
完成基本开发之后,我们再开发一个生成二维码接口返回给前端并且需要返回一个唯一ID给前端,前端通过该唯一ID查询用户是否已经扫码登陆
/** 微信公众号生成二维码 */ func (c *WxLoginController) PostPublicGetQrCode() *web.JsonResult { // 生成唯一编码 uuidStr := uuid.New().String() sceneStr := uuidStr[:32] //生成公众号关注二维码 apiCode, apiResult := WechatService.WechatGetPublicQrCode(sceneStr) if apiCode != 200 { return web.JsonErrorMsg(apiResult) } //组合生成连接 qrCode := "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + apiResult //返回数据 return web.NewEmptyRspBuilder().Put("qrCode", qrCode).Put("sceneStr", sceneStr).JsonResult() }
这个接口我们会生成一个二维码地址以及对应的唯一ID返回给前端。
我们便完成了对微信公众号部分的对接,下一篇跟大家分享用户关注后如何通知前端,完成一键登录。
0条评论