网站利用扫码关注公众号实现一键登录(上)
网站利用扫码关注公众号实现一键登录
最近接了一个单子,其中有个需求是需要扫码关注公众号实现网站登陆,由于篇幅过长,可能需要分成两篇跟大家分享,今天先分享微信公众号部分的对接,下一篇再分享关注后如何实现登陆。
我这里项目是用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条评论