⭐个人网站实现微信扫码登录

个人网站实现微信扫码登录

效果图

个人网站实现微信扫码登录

开发布景

为什么想用微信扫码登录呢? 起因是自己开发了一个搜题网站,内容很简略,可是没有登陆,所以游客能够随意运用,当然也不是不让游客访问,仅仅没有登陆的话,不能很好的统计运用的用户,也能减少些一些乱用的用户。

起初,我是想设计成账号密码登录网站的,可是想了下,我自己往常碰到一些需求注册的网站,我往往会直接越过,就不会对这个网站感兴趣了,能让我感兴趣的网站一般都是支持直接扫码登录或许能够以第三方账号直接注册登录,所以能招引更多的用户,有必要要把这个门槛给打下来,进步用户体会!

寻觅计划,以及挑选哪种计划

所以,我开端踏上了百度之旅,通过数次的查阅材料,发现有三种方法实现微信扫码登录

  • 第三方网站(developers.weixin.qq.com/doc/oplatfo…)
  • 大众号
  • 小程序

第三方网站

第三方网站方法是直接恳求api,https://open.weixin.qq.com/connect/qrconnect带上下面的参数 比如:带着参数的链接

个人网站实现微信扫码登录

页面会重定向到一个附带暂时code的地址

个人网站实现微信扫码登录

用户扫码成功后,页面会自动跳转到redirect_uri指定的链接,这样就完结了扫码登录

总之,微信开放渠道的方法应该是最舒服的微信扫码登陆了,可是条件需求交认证300元的认证费用,网站的话还需求提交《微信开放渠道网站信息登记表》,也是审阅最费事的方法,只好先pass啦

个人网站实现微信扫码登录
个人网站实现微信扫码登录

大众号

必要条件:大众号是已认证好的服务号 流程:

个人网站实现微信扫码登录

  • 使用WxLogin获取到code,再用code通过https://api.weixin.qq.com/sns/oauth2/access_token获取access_token;
  • 拿到access_token之后,能够使用生成二维码api(可带着scene值,便是自己界说的uuid之类的,能够用来区别是哪个网页端建议的扫描)生成微信二维码;

个人网站实现微信扫码登录

  • 当用户扫描生成好的二维码时,微信会依据推送事情到服务端(这个需求自己在后台配置),并且会带着好之前的scene值,后台就能够判别是哪个网页端扫了这个二维码,后台进行登录操作,
  • 网页端能够通过拿着scene值轮训后台的接口,查询是否登录成功 或许使用websocket,后台自动告诉你给网页,是否登录成功。

生成二维码文档地址:developers.weixin.qq.com/doc/offiacc…

这种方法仍是对个人开发者要求太高了,个人就不能恳求服务号,认证也要一笔费用,也劝退我了,pass!

小程序

条件:现已上线的小程序(个人/公司) 流程:

  1. 后台调用微信接口https://api.weixin.qq.com/cgi-bin/token获取access_token,access_token只有两个小时,由于有调用频率约束,access_token最好是保存在缓存中;
  2. 用户在网页端点击获取小程序码,前端带着随机数恳求后台,后台使用access_token获取小程序码https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=(其间需求带着一个参数scene需求注意,scene值便是一个随机数,需求在前端就生成好,用来后台区别网页端用),回来小程序码二进制数据;
  3. 前端接收后台回来的二进制数据,(此时前端需求不停地轮训后台接口查询是否登录成功,或许使用websocket,后台自动告诉前端)展示小程序码,用户拿出手机用微信扫码之后,相似下面截图,

个人网站实现微信扫码登录

  1. 用户自动点击登录按钮,登录按钮需求绑定WxLogin事情,获取用户的微信code,再调用后台供给的登录接口进行登录操作,当登陆成功完毕,告诉前端完结登录

获取小程序码官方文档 developers.weixin.qq.com/miniprogram…

最后,整个过程全程免费,对于我来说也是比较简略的,我先说明下,我之前是开发了一个小程序的,所以对我来说是比较便利的,如果我们没有现已上线的小程序,为了一个扫码登录,的确有点费力

原理

原理图

个人网站实现微信扫码登录

开发步骤

这儿我就不细讲了,知道了思路,其实开发便是最简略的事了,这儿贴一些关键的代码

网页端

    // 获取二维码
    getQrcode = () => {
        // 创建websocket衔接,为了后台能自动告诉到前端
        if (this.state.ws == null) {
            this.setState({
                ws: this.createWebSocket(this)
            });
        }
        // 发送恳求 获取二维码
        this.setState({
            qrcodeBase64: ''
        });
        // 带着uuid到后台,后台将uuid和socket对象相关,便利告诉到哪个网页端
        getUnlimitedQrCode({ "uuid": this.state.uuid }).then((res) => {
            if (res == undefined || res.data == undefined || res.data.data == undefined) {
                console.log("获取二维码失利");
                return null;
            }
            this.setState({
                qrcodeBase64: res.data.data
            });
        }).catch((error) => {
            console.error(error);
        });
    }

后台

// 获取不约束的小程序码接口
func (h *handler) GetUnlimitedQRCode() core.HandlerFunc {
	return func(c core.Context) {
		param := new(RequestQrCodeParam)
		c.ShouldBindJSON(&param)
        // 获取accessToekn
		accesstokenInfo, err := h.getAccessToken()
		if err != nil {
			c.AbortWithError(core.Error(
				http.StatusBadRequest,
				500,
				"获取微信access_token失利").WithError(err),
			)
			return
		}
		qrCodeBytes := postUnlimitedQRCode(accesstokenInfo, param.Uuid)
		// c.ResponseWriter().Write(qrCodeBytes)
		var q = QrcodeRes{Data: qrCodeBytes, Errcode: 0, Errmsg: "success"}
		c.Payload(q)
	}
}
// websocket衔接回调函数
var (
	err           error
	server        socket.Server
	SocketManager = make(map[string]*websocket.Conn) // 存储uuid和socket对象的关系
)
func (h *handler) Connect() core.HandlerFunc {
	return func(ctx core.Context) {
		server, err = socket.New(h.logger, h.db, h.cache, ctx.ResponseWriter(), ctx.Request(), nil)
		if err != nil {
			return
		}
		// 保存uuid到map,后续后台自动回调前台,需求依据uuid找到对应的websocket衔接
		uuid := ctx.Request().URL.Query().Get("uuid")
		log.Println(uuid)
		conn, err := server.GetConn()
		if err != nil {
			return
		}
		SocketManager[uuid] = conn
		go server.OnMessage()
	}
}
// 登录接口
func (h *handler) Wx_web_login() core.HandlerFunc {
	return func(c core.Context) {
		param := new(RequestWebLoginParam)
		c.ShouldBindJSON(&param)
		// 登录操作
		ticketRes, err := registerAndExportToken(h, c, &WxLoginParams{Code: param.Code})
		if err != nil {
			c.AbortWithError(core.Error(
				400,
				500,
				"微信登陆失利").WithError(err),
			)
		}
		log.Print(ticketRes)
		// 登陆成功后,自动调用websocket的接口,回来token
		res := new(webmessage.WebSocketResponse).Success(webmessage.LOGIN_SUCCESS_TYPE, ticketRes)
        // 依据uuid找到对应socket
        socket := webmessage.SocketManager[param.Uuid]
		socket.WriteJSON(res)
		c.Payload(res)
	}
}

小程序端

    // 登录按钮绑定的登录事情
    handleLogin = () => {
        // 处理登录逻辑
        console.log('登录按钮被点击')
        // 获取code
        Taro.login({
            success: (res) => {
                console.log(res)
                if (res.code != '') {
                    // 恳求登录操作
                    webWxLogin({ "code": res.code, "uuid": this.state.scene }).then((res) => {
                        console.log(res.data)
                        if (res.data.code == 200 && res.data.type == 20000) {
                            // 登录成功
                            Taro.showToast({
                                title: '登录成功',
                                icon: 'success'
                            })
                            // 跳转主页
                            Taro.switchTab({
                                url: '/pages/home/index'
                            })
                        } else {
                            Taro.showToast({
                                title: '登录失利',
                                icon: 'none'
                            })
                        }
                    }).catch((err) => {
                        console.log(err)
                        Taro.showToast({
                            title: '登录失利',
                            icon: 'none'
                        })
                    })
                } else {
                    Taro.showToast({
                        title: '登录失利',
                        icon: 'none'
                    })
                }
            },
            fail: (err) => {
                console.log(err)
                Taro.showToast({
                    title: '登录失利',
                    icon: 'none'
                })
            }
        })
    }
    // 这段代码是用GitHub Copilot 写的,真的是把一切状况都写出来了

总结

这次扫码登录前前后后花了我五天的时刻(下班后弄得),其间也碰了不少坑,前端二维码不显现、后台不回来二进制数据、nginx署理的https域名网站websocket也要做特殊处理,真的是在里面爬了很久,虽然这个手段不是很正规,可是过程能学到一些知识,实现了自己想要的效果,仍是挺满足的,谢谢看到这儿,如果哪里有说的有问题,欢迎指正

这儿打个广告,欢迎我们体会懒熊搜题网站 tiku.toolkit.show

本文由mdnice多渠道发布