GoFrame框架应用之登录接口开发(微信扫码、手机号、邮箱)

前置准备

用GoFrame框架进行登录接口的开发,提供以下几种登录方式:

  • 手机号+验证码
  • 邮箱+验证码
  • 微信公众平台扫码登录

在本次开发过程中用到的Goframe框架的工程目录如下:

/
├── api
├── internal
│   ├── cmd
│   ├── controller
│   ├── dao
│   ├── logic
│   ├── model
│   |   ├── do
│   │   └── entity
│   └── service
├── manifest
├── resource
├── utility
├── go.mod
└── main.go 

开发前需提前安装好gofrmae相应的环境,一般使用gf run main.go命令来运行程序

开发规范

路由注册

路由注册主要是在internal/cmd/cmd.go文件中进行,GoFrame框架初始化时就提供了cmd文件的相应模板。

这里我使用了goframe框架v2版本的路由规范以及路由绑定对象的方式进行路由注册。

每个路由对绑定对应的对象的方法NEWxxx(),该对象在控制器controller中进行声明、定义和实现。
在这里插入图片描述
下面拿出手机号登录的路由对象以作示例,下面部分代码在internal/controller/login/login.go文件中
在这里插入图片描述
对象声明与实现在上图模板中,下面的两个方法对应的就是该对象的两个路由。
在终端可以看到对应方法实现的路由/login/phone/sendsmscode/login/phone/tel-sms-login。如果我们方法名命名为驼峰命名法,即TelPhoneNum,除首字符外有大写字母,则会自动将路由中的大写转换为小写并加上-,如tel-phone-num
在这里插入图片描述

服务定义与封装

goframe框架为了降低模块之间的耦合性,分了servicelogic两个目录来进行业务的相关开发。一般controller层调用的是service层封装好的方法,而service层下接logic层的服务实现逻辑,所有关于业务处理相关的代码都放在logic层。
service层模板如下图:
在这里插入图片描述
可以看到业务处理逻辑都在ILogin接口中,其它代码为相应的初始化部分,为goframe框架的规范。相应代码在internal/service/login.go文件中。

logic层模板如下图:
在这里插入图片描述
logic层则是对应service层进行一些初始化,首先需要定义一个结构体,并有相应的初始化函数,紧接着便是对应service层中的方法的实现。

手机号+验证码登录

手机号+验证码的登录方式,我们只需要明确一下如何保存验证码和验证验证码即可,我们这里使用Redis数据库,用手机号+验证码的键值对进行保存。同时我们需要给对应手机号发送验证码。手机号需要进行相应的鉴别,确保手机号是有效的。

UniSMS调用发送短信

实现手机号登录需要调用第三方的短信验证平台,这里选用的是UniSMS。具体调用步骤查看对应文档
在这里插入图片描述
在接入SDK之后需要进行签名认证,如果是个人使用要用个人姓名进行申请,否则通过不了。
在这里插入图片描述
相应发送信息代码如下:

//以下为调用unisms平台的SDK
	client := unisms.NewClient("RQ21CBsYy41DkG8hfrHEZtFRiT4mLWkPo2npHZFQpv8agmL7o", "your access key secret") // 若使用简易验签模式仅传入第一个参数即可
	// 构建信息
	message := unisms.BuildMessage()
	message.SetTo("%s", Phonenumber)
	message.SetSignature("吴奇墉")                                          //签名
	message.SetTemplateId("pub_verif_ttl3")                              //短信模板
	message.SetTemplateData(map[string]string{"code": verificationCode}) //验证码
	_ = client
	//发送短信
	res, err := client.Send(message)
	_ = res
	if err != nil {
		g.Log().Error(ctx, "发送验证码失败:", err)
		return err
	}

验证码生成与验证

在发送信息接口生成验证码并进行存放进缓存中,设置过期时间为15分钟

// 生成随机数
	verificationCode := fmt.Sprintf("%06d", rand.Intn(1000000))

	//验证码保存在Redis缓存中
	expiration := 15 * time.Minute
	_, err := g.Redis().Do(ctx, "SETEX", Phonenumber, int64(expiration.Seconds()), verificationCode)
	if err != nil {
		g.Log().Error(ctx, "验证码Redis操作错误:", err)
		return err
	}

在验证登录接口取出验证码并进行验证

//从redis获取验证码
	v, err := g.Redis().Do(ctx, "GET", Phonenumber)
	//打印日志
	g.Log().Info(ctx, "Redis中的验证码为:", v)

	if err != nil {
		g.Log().Error(ctx, "读取Redis中验证码失败:", err)
		return "", err
	}
	//验证验证码是否匹配
	if v.String() != smscode {
		err = errors.New("smscode don't match")
		g.Log().Error(ctx, "验证验证码失败:", err)
		return "", err
	}

用户信息放入mysql数据库

err = dao.User.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.User.Ctx(ctx).Data(do.User{
			Phonenumber: Phonenumber,
		}).Insert()
		return err
	})
	if err != nil {
		return "", err
	}

邮箱号+验证码登录

邮箱号+验证码的登录方式跟手机号+验证码类似,只是将发送短信步骤改为发送邮件。对于邮箱号需要进行格式验证,确保邮箱有效。

获取授权码

这里以QQ邮箱为例
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
将生成的授权码进行复制

gomail库发送邮件并验证

"gopkg.in/gomail.v2"为本次发送邮件使用的库。
官方文档:https://pkg.go.dev/gopkg.in/gomail.v2
发送邮件的内容需要我们自行编辑
在发送邮件接口:

//发送邮件
	m := gomail.NewMessage()
	m.SetHeader("From", "发送人")
	m.SetHeader("To", "接收人")
	m.SetHeader("Subject", "标题")
	m.SetBody("text/html", "正文")

	d := gomail.NewDialer("smtp.qq.com", 465, "xxx@qq.com", "授权码")
	err = d.DialAndSend(m)
	if err != nil {
		g.Log().Info(ctx, "发送邮件失败:", err)
		return false, err
	}

验证码存入Redis

//验证码保存在Redis缓存中
	expiration := 15 * time.Minute
	_, err := g.Redis().Do(ctx, "SETEX", emailinput.Emailnumber, int64(expiration.Seconds()), smscode)
	if err != nil {
		g.Log().Error(ctx, "验证码Redis操作错误:", err)
		return false, err
	}

发送后就能收到邮件
在这里插入图片描述

验证登录接口,从Redis取出验证码进行比对

//从redis获取验证码
	v, err := g.Redis().Do(ctx, "GET", emaillogininput.Emailnumber)
	//打印日志
	g.Log().Info(ctx, "Redis中的验证码为:", v)

	if err != nil {
		g.Log().Error(ctx, "读取Redis中验证码失败:", err)
		return "", err
	}

微信公众平台测试号获取登录二维码+前端轮询+扫码登录

微信扫码登录有两种方式,一种是通过微信公众平台进行开发,另一种是通过微信开放平台进行开发。这里用微信公众平台进行开发,微信公众平台能够申请到测试号。

微信公众平台开发步骤

1、测试号申请
2、配置开发者身份
3、使用appidappsecret获取访问凭证access_token
4、使用access_token获取二维码ticket
5、使用ticket获取二维码链接
6、编写html文件显示二维码并进行轮询
7、处理用户扫码消息推送
8、发送ticket到检查登录状态接口,查询该ticket下是否有对应的openid

测试号申请

申请链接:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
扫码通过微信账号登录即可

配置开发者身份

配置开发者身份可以参考微信公众平台的开发文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
需要准备一个域名进行身份验证,如果没有域名的可以购买一个云服务器,也可以看看阿里云服务器的免费试用。

在接口配置信息中填入接收参数,Token可以自定义,URL中填写域名+端口号+接口地址(图中localhost只是演示,需替换为真实域名或服务器公网ip)
在这里插入图片描述
文档中提示:

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

参数详情
signature微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp时间戳
nonce随机数
echostr随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
另请注意,微信公众号接口必须以http://或https://开头,分别支持80端口和443端口。

基于文档,编写代码对微信服务器发来的消息进行签名验证

r := g.RequestFromCtx(ctx)
	signature := r.URL.Query().Get("signature")
	timestamp := r.URL.Query().Get("timestamp")
	nonce := r.URL.Query().Get("nonce")
	echostr := r.URL.Query().Get("echostr")
	sign := &model.Signature{
		Signature: signature,
		Timestamp: timestamp,
		Nonce:     nonce,
		Echostr:   echostr,
	}
	g.Log().Debug(ctx, "签名内的内容:", sign)
	//签名验证逻辑
	bool1, err := checksign.CheckSignature(ctx, sign)
	if err != nil {
		g.Log().Error(ctx, err)
	}

CheckSignature函数:

r := g.RequestFromCtx(ctx)
	token := model.TOKEN
	tmpArr := []string{token, sign.Timestamp, sign.Nonce}
	sort.Strings(tmpArr)
	tmpStr := ""
	for _, str := range tmpArr {
		tmpStr += str
	}
	hash := sha1.New()
	hash.Write([]byte(tmpStr))
	tmpSum := hash.Sum(nil)
	tmpStr = fmt.Sprintf("%x", tmpSum)
	r.Response.WriteString(sign.Echostr)
	return tmpStr == sign.Signature, nil

获取access_token

我们使用测试号中申请的appidappsecret获取access_token,获取access_token的相关步骤参考官方链接https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html

获取access_token的方式是通过GET的方式从微信服务器进行获取,调用的链接为:

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

GET请求携带参数如下:

参数是否必须说明
grant_type获取access_token填写client_credential
appid第三方用户唯一凭证
secret第三方用户唯一凭证密钥,即appsecret

返回参数:

参数说明
access_token获取到的凭证
expires_in凭证有效时间,单位:秒

代码如下:

//获取access_token
	resp, err := g.Client().Get(ctx, model.Accesstoken_url)
	if err != nil {
		g.Log().Error(ctx, err)
	}
	defer resp.Close()

	body1 := []byte(resp.ReadAllString())
	if err := json.Unmarshal(body1, &model.WechatToken); err != nil {
		fmt.Println("解析 JSON 时出错:", err)
	}
	g.Log().Debug(ctx, "Access_Token:", model.WechatToken.AccessToken)
	return model.WechatToken.AccessToken

获取二维码ticket

我们通过access_token可以向微信服务器获取ticket用于生成二维码,在这里我们生成临时二维码。相应操作参考官方文档:https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html

获取ticket的方式是通过POST的方式从微信服务器进行获取,调用的链接为:

https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=TOKEN

POST的数据格式为json,例子:{“expire_seconds”: 604800, “action_name”: “QR_SCENE”, “action_info”: {“scene”: {“scene_id”: 123}}}

POST参数说明:

参数说明
expire_seconds该二维码有效时间,以秒为单位。 最大不超过2592000(即30天),此字段如果不填,则默认有效期为60秒。
action_name二维码类型,QR_SCENE为临时的整型参数值,QR_STR_SCENE为临时的字符串参数值,QR_LIMIT_SCENE为永久的整型参数值,QR_LIMIT_STR_SCENE为永久的字符串参数值
action_info二维码详细信息
scene_id场景值ID,临时二维码时为32位非0整型,永久二维码时最大值为100000(目前参数只支持1–100000)
scene_str场景值ID(字符串形式的ID),字符串类型,长度限制为1到64

返回参数:

参数说明
ticket获取的二维码ticket,凭借此ticket可以在有效时间内换取二维码。
expire_second该二维码有效时间,以秒为单位。 最大不超过2592000(即30天)。
url二维码图片解析后的地址,开发者可根据该地址自行生成需要的二维码图片

代码如下:

//利用access_token获取二维码
	jsondata, err := json.Marshal(model.Qr)
	if err != nil {
		g.Log().Error(ctx, err)
		return QrRes, err
	}
	Qrcode_url := fmt.Sprintf(model.Qrcode_url + s.GetAccesstoken(ctx))
	g.Log().Debug(ctx, "获取ticket的url为:", Qrcode_url)
	body, err := g.Client().Post(ctx, Qrcode_url, jsondata)
	if err != nil {
		g.Log().Error(ctx, err)
		return QrRes, err
	}
	var Qrbody model.QrResstruct
	body1 := []byte(body.ReadAllString())
	if err := json.Unmarshal(body1, &Qrbody); err != nil {
		fmt.Println("解析 JSON 时出错:", err)
		return QrRes, err
	}

使用ticket获取二维码链接

我们获取了ticket之后可以通过ticket向微信服务器发送GET请求,获取二维码链接。参考官方文档:https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html
获取二维码链接的方式是通过GET的方式从微信服务器进行获取,TICKET记得进行UrlEncode,调用的链接为:

https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=TICKET

这里我们将ticket存入Redis数据库中,并设置键值对为TICKET:“none”,为了后续前端进行轮询。

代码如下:

//g.Log().Debug(ctx, "获取到的ticket:"+Qrbody.Ticket)
	_, err = g.Redis().Do(ctx, "SET", Qrbody.Ticket, "none")
	if err != nil {
		g.Log().Error(ctx, err)
		return QrRes, err
	}
	//返回生成的二维码链接
	url_code := model.GetUrl(Qrbody.Ticket)
	QrRes = model.QrRes{
		Urlticket: url_code,
		Ticket:    Qrbody.Ticket,
	}

编写html文件显示二维码并进行轮询

在goframe工程目录下
├── resource
│ ├── public
│ │ │──html
创建一个login.html文件,需要在cmd.go文件中进行静态资源的绑定
在这里插入图片描述

需要两个后端接口:获取二维码接口/getqrcode和检查登录状态接口/checklogin

/getqrcode返回响应:

{
    "code": 0,
    "message": "",
    "data": {
        "Urlticket": "xxx",
        "Ticket": xxx"
    }
}

/checkinlogin返回响应:

{
    "code": 54,
    "message":"",
    "data": {
        "Message": "xxx"
    }
}

前端页面需求:

  • 将Urlticket的链接参数进行显示
  • 将Ticket参数传递给检查登录状态函数,并发送给后端
  • 进行轮询检查登录状态
  • 根据后端/checklogin接口响应确定页面显示,wait for login为等待登录,login success为登陆成功

html文件代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>微信登录</title>
    <style>
        body {
            margin: 0;
            font-family: Arial, sans-serif;
            background-color: #f2f2f2;
        }
        .container {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }
        .message {
            margin-bottom: 20px;
            font-size: 18px;
            color: #333;
        }
        #qrcode {
            display: none;
            max-width: 300px;
            border-radius: 10px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }
        #login-success {
            display: none;
            font-size: 24px;
            color: #4caf50;
        }
    </style>
</head>
<body>
    <div class="container">
        <div id="message" class="message">等待用户扫码...</div>
        <img id="qrcode" src="" alt="微信二维码">
        <div id="login-success">登录成功</div>
    </div>
    
    <script>
        async function fetchQRCode() {
            try {
                const response = await fetch('getqrcode的后端接口');
                const data = await response.json();
                if (data.code === 0) {
                    document.getElementById('qrcode').src = data.data.Urlticket;
                    document.getElementById('qrcode').style.display = 'block';
                    pollLoginStatus(data.data.Ticket);
                } else {
                    document.getElementById('message').innerText = '获取二维码失败,请稍后重试';
                }
            } catch (error) {
                document.getElementById('message').innerText = '请求失败,请稍后重试';
            }
        }

        async function checkLogin(ticket) {
            try {
                const response = await fetch('检查登录状态的接口', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ ticket })
                });
                return await response.json();
            } catch (error) {
                return { data: { Message: 'wait for login' } };
            }
        }

        async function pollLoginStatus(ticket) {
            const checkInterval = setInterval(async () => {
                const response = await checkLogin(ticket);
                if (response.data.Message === 'login success') {
                    clearInterval(checkInterval);
                    document.getElementById('message').style.display = 'none';
                    document.getElementById('qrcode').style.display = 'none';
                    document.getElementById('login-success').style.display = 'block';
                }
            }, 3000); // 轮询时间设置为3秒
        }

        fetchQRCode();
    </script>
</body>
</html>

处理用户扫码消息推送

用户扫描前端页面上的二维码后,微信服务器会进行消息推送,消息将会推送到我们进行签名验证的接口上。文档中说明:

在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。

参考官方文档中扫描带参数二维码事件https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html

消息推送会发送XML数据包,数据包内参数如下:

参数描述
ToUserName开发者微信号
FromUserName发送方账号(一个OpenID)
CreateTime消息创建时间 (整型)
MsgType消息类型,event
Event事件类型,SCAN
EventKey事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id
Ticket二维码的ticket,可用来换取二维码图片

我们在签名验证接口下添加处理消息推送部分的代码,将接收到的ticketopenid以键值对的形式存到Redis数据库中,在后续的检查登录状态接口进行验证。处理消息推送时echostr为空字符串,所以可以进行判断,否则下次进行签名验证将会同时进行处理消息推送事件:

//用户扫码时echostr为空
	if sign.Echostr == "" {
		g.Log().Info(ctx, "######处理消息推送事件######")
		//处理消息推送事件
		msg := new(model.XMLMessage)
		if err := r.Parse(msg); err != nil {
			g.Log().Error(ctx, err)
			return false
		}
		userinfo := model.GetuserInfo{}
		//g.Log().Debug(ctx, "xml文件:", msg)
		if msg.Ticket != " " && (msg.Event == "SCAN" || msg.Event == "subscribe") {
			userinfo.OpenID = msg.FromUserName
			expiration := 3 * time.Minute
			if msg.FromUserName != " " {
				_, err := g.Redis().Do(ctx, "SETEX", msg.Ticket, int64(expiration.Seconds()), msg.FromUserName)
				if err != nil {
					g.Log().Error(ctx, err)
					return false
				}
			}
			return true
		}
	}

检查登录状态接口

我们在处理消息推送事件时将ticketopenid存入Redis数据库,现在根据前端发送的ticket进行验证,如果取出的有openid,则表示用户已经扫码,登陆成功;如果取出的是"none",则表示还没有用户进行扫码。

g.Log().Info(ctx, "######检查登录状态######")
	r := ghttp.RequestFromCtx(ctx)
	Check := model.Check{}
	jsonData := r.GetBody()
	if err := json.Unmarshal([]byte(jsonData), &Check); err != nil {
		fmt.Println("解析 JSON 数据失败:", err)
		g.Log().Error(ctx, err)
		return err
	}
	openid, err := g.Redis().Do(ctx, "GET", Check.Ticket)
	if err != nil {
		g.Log().Error(ctx, "获取Redis中的openid失败:", err)
		return err
	}

	g.Log().Debug(ctx, "openid=", openid.String())
	if openid.String() == "none" {
		err = gerror.New("have no user login")
		g.Log().Info(ctx, "wait for login...")
		return err
	}

获取用户信息(if needed)

参考文档https://developers.weixin.qq.com/doc/offiaccount/User_Management/Get_users_basic_information_UnionID.html#UinonId

jwt鉴权,返回访问token

JWT本质上是一组字符串,又Header、Payload和Secret三部分组成。

Header: 定义了生成签名的算法以及Token的类型
Payload: 用来存放实际传递的数据
Signature: 服务器通过Header、Payload和Secret,使用Header中生成签名的算法生成

Payload部分默认是不加密的,所以不要将隐私信息放在Payload当中

JWT身份验证流程

tupianlaiyuan
图片来源:JavaGuide

  • 用户向服务器发送用户名、密码以及验证码用于登陆系统。
  • 如果用户用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token,也就是 JWT。
  • 用户以后每次向后端发请求都在 Header 中带上这个 JWT 。
  • 服务端检查 JWT 并从中获取用户相关信息。

相关代码

这里使用的是自己写的一个生成JWT令牌的代码,也可以使用goframe中的gf-jwt库或者go-jwt
gf-jwt官方文件:https://goframe.org/pages/viewpage.action?pageId=6357048
go-jwt官网:https://pkg.go.dev/github.com/golang-jwt/jwt/v5

func GenerateJWT(ctx context.Context, Phonenumber string, Expirationtime time.Time) (string, error) {
	// 创建一个新的Token对象
	token := jwt.New(jwt.SigningMethodHS256)

	// 设置Payload部分,这里以手机号作为Payload
	claims := token.Claims.(jwt.MapClaims)
	claims["Phonenumber"] = Phonenumber
	claims["exp"] = Expirationtime.Unix()

	// 使用密钥进行签名生成Token字符串
	tokenString, err := token.SignedString(model.SecretKey)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

参考文章:
【微信公众平台】扫码登陆:https://blog.csdn.net/qq_46449962/article/details/137951715?spm=1001.2014.3001.5501
基于公众号的微信扫码登陆实现:https://nodejh.com/posts/wechat-scan-qr-code-to-login/
JavaGuide:https://javaguide.cn/system-design/security/basis-of-authority-certification.html
微信扫码登录:https://developers.weixin.qq.com/community/develop/article/doc/0000a8a4f8c91040f70c9a8425c013
微信扫码登录详细操作流程(微信公众平台开发):https://blog.csdn.net/weixin_43890049/article/details/119463862

  • 46
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
开发微信扫码登录程序是一项非常有挑战性的任务。首先,我们需要了解微信扫码登录的工作原理。 微信扫码登录是一种基于OAuth 2.0协议的认证方式。下面是开发微信扫码登录程序的基本步骤: 1. 注册开发者账号:在微信开放平台注册一个开发者账号,并创建一个应用。 2. 配置开发环境:将微信提供的开发工具包集成到开发环境中。可以选择使用Java、Python等编程语言来开发。 3. 获取授权地址:在后台配置应用的授权回调地址。用户扫码登录后,微信会将授权码返回到该地址。 4. 生成二维码:调用微信提供的API生成用户扫码登录所需的二维码。 5. 监听回调:在后台实现一个回调接口,监听微信回调的授权码。 6. 获取用户信息:通过授权码,调用微信提供的API获取用户的基本信息,如昵称、头像等。 7. 实现登录逻辑:将获取到的用户信息与本地系统用户进行关联,实现用户的登录逻辑。 开发微信扫码登录程序需要对微信开放平台的文档进行深入研究,并掌握相关的API调用所需的参数和格式。此外,需要具备网络编程、接口开发等相关的技能。 开发微信扫码登录程序对于企业来说有很多好处。首先,可以提供一种方便快捷的登录方式,避免用户需要记忆过多的账号和密码。其次,可以增加用户粘性,提高用户的黏性和活跃度。最后,可以帮助企业获取用户的基本信息,便于个性化推送和精准营销。 总结来说,开发微信扫码登录程序是一项复杂的任务,需要充分了解微信扫码登录的工作原理和开发流程。通过合理的开发和配置,可以提供一种便捷的登录方式,增加用户黏性,并为企业实现个性化推送和精准营销提供基础数据。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值