个人订阅号实现微信登录
首先我们聊一聊登录,传统的登录无非就是输入用户名密码进行登录,相信有很多小伙伴还在这样去做,为什么呢?
因为这样简单,用户名密码登录零成本,但是随着手机的依赖性不断增加,这种方式的弊端也逐渐显露出来,不知道各位小伙伴怎么想,我是经常记不住密码,有时候账号都不记得了,现在各大网站基本都是扫码登录、手机号登录
而这两种方式,对于我们初学者来说,成本就比较高了,都不好接入
首先手机号登录,要发短信,肯定是要花钱的,而且流程还比较麻烦
扫码登录应用比较多,但是也会有依赖性,首先那是各大应用的扫码登录,比如百度、百度网盘、CSDN之类的,要进行扫码登录,就必须要下载对应的APP,CSDN放我手机里800年不用了,当然也有很多都支持微信登录,微信就不在多说了吧,微信的生态,每个人现在都离不开了
那么微信扫码登录呢?
首先我很遗憾的告诉大家,在微信官方文档中个人订阅号不支持微信网页授权,看下图


如果想要实现微信网页授权,前提就是必须注册服务号,而且通过微信认证,一年300大洋,而且服务号需要营业执照
那我们个人申请的订阅号能不能实现微信扫码登录呢?
答案是能,只不过此扫码非彼扫码,通过一种迂回的方式实现
首先我们介绍一下微信体系中的openId、unionId是怎么个事
首先我们只要在微信体系中开发,有一个appid是离不开的,不论你是做公众号、还是小程序,或者是APP、网站,都是离不开的,那有很多小伙伴就会问了,APP,网站跟微信扯不上关系,为什么呢?
是的,你只是开发网站、APP可以的,但是你要用微信的功能,比如扫码登录、微信支付、分享朋友圈等功能,那就必须按微信的节奏走
小程序、公众号注册之后就会有appid,对于网站、APP就需要你在微信开放平台就绑定,如下图

绑定之后就会给你分配appid

那么什么是openid、unionid呢
openid就是对于某个应用,微信为开发者对于用户起的唯一id
说的有点绕,举例子:比如你给公众号发了私信,公众号的开发者就能获取到openid,代表的是你这个微信用户,而且以后你给这个公众号发消息,他获取到的openId都是同一个
微信给开发者的不是我们的真实微信资料,而是一个openid
那什么是unionid呢?
小白现在运营了一个小程序、一个网站,我已经将两个应用在开放平台绑定,那用户在小程序进行微信登录,在网站进行微信登录,对于同一个用户,我会拿到两个openId,但是在网站后台、小程序后台拿到的unionid是一样的
好了,言归正传
既然了解了这种机制,那我们就可以使用消息的方式实现订阅号的微信登录
官方网站:https://developers.weixin.qq.com/doc/subscription/guide/product/message/Receiving_standard_messages.html
微信公众号的消息机制,默认注册后,开发者可以在微信公众平台配置自动回复,当我们验证开发者服务器后,微信会将用户的消息转发到我们配置好的服务器,格式为xml,就是说你在java服务端可以拿到用户对你公众号发送的消息内容,并且可以进行回复

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1348831860</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[this is a test]]></Content>
<MsgId>1234567890123456</MsgId>
<MsgDataId>xxxx</MsgDataId>
<Idx>xxxx</Idx>
</xml>
| 参数 | 描述 |
|---|---|
| ToUserName | 开发者微信号 |
| FromUserName | 发送方账号(一个OpenID) |
| CreateTime | 消息创建时间 (整型) |
| MsgType | 消息类型,文本为text |
| Content | 文本消息内容 |
| MsgId | 消息id,64位整型 |
| MsgDataId | 消息的数据ID(消息如果来自文章时才有) |
| Idx | 多图文时第几篇文章,从1开始(消息如果来自文章时才有) |
我们可以看到,微信会将上述字段内容转发到我们的服务器
我们先说一下思路
1、首先,我们将公众号二维码放置在登录界面,提示用户扫码关注公众号,在后台回复“登录”获取验证码,用户发送“登录”给公众号。
2、服务端拿到消息内容,判断如果是“登录”,随机生成一个唯一的6位数的验证码(短暂唯一),3分钟有效,三分钟内唯一即可,然后将code、openId存储到redis,可以存一对,code:openId,openId:code,然后将6位数验证码返回给用户。
3、用户拿到验证码在网页上输入后,点击登录,此时服务端只能拿到code,根据code查询redis获取openid、如果能查到,在根据查到的openid查询code,如果验证通过,则登陆成功
这是一种巧妙的方式,公众号接收发送消息的权限订阅号有权限
下面我们就来实战
环境要求:首先你要做一个内网穿透,可以自己租服务器、也可以使用netapp等工具
目的是将你本地的服务暂时连通到公网,供微信服务器调用,因为微信会将用户发送的消息转发到开发者服务器,我们本地开发,在局域网,微信调不到我们的服务
小白有一个月的体验额度,就用第一种了
1、在你的服务器上安装个工具,frp
服务器安全组开放两个端口:40001、8088

下载地址:https://github.com/fatedier/frp/releases
下载后传到服务器并解压

[root@iZ2ze6y8dmewokrjlilbngZ frp_0.63.0_linux_amd64]# ll
total 34344
-rwxr-xr-x 1 1001 docker 15425688 Jun 25 12:10 frpc
-rw-r--r-- 1 1001 docker 142 Jun 25 12:14 frpc.toml
-rwxr-xr-x 1 1001 docker 19714200 Jun 25 12:10 frps
-rw-r--r-- 1 1001 docker 16 Jun 25 12:14 frps.toml
-rw-r--r-- 1 1001 docker 11358 Jun 25 12:14 LICENSE
[root@iZ2ze6y8dmewokrjlilbngZ frp_0.63.0_linux_amd64]#
修改frps.toml
# 绑定端口
bindPort = 40001
# 认证token,需要与frpc保持一致
auth.token = "xiaobai"
# webui
# 默认为 127.0.0.1,如果需要公网访问,需要修改为 0.0.0.0。
webServer.addr = "0.0.0.0"
webServer.port = 40002
# dashboard 用户名密码,可选,默认为空
webServer.user = "xiaobai"
webServer.password = "xiaobai"
修改frpc.toml
serverAddr = "127.0.0.1"
serverPort = 40001
# 认证token,需要与frpc保持一致
auth.token = "xiaobai"
# transport.tls.enable=false
webServer.addr = "0.0.0.0"
webServer.port = 4000
# frpc.toml
[[visitors]]
name = "weixin-proxy"
type = "stcp"
serverName = "weixin-proxy"
secretKey = "xiaobai"
bindAddr = "0.0.0.0"
bindPort = 8088
启动
nohup ./frps -c frps.toml > frps.log 2>&1 &
nohup ./frpc -c frpc.toml > frpc.log 2>&1 &

2、在你的windows安装frp,下载地址同上,下载同版本,选择windows平台的

编辑frpc.toml
# frps端的ip
serverAddr = "wwww.xbjava.com"
# frps端的port
serverPort = 40001
# 认证token,需要与frpc保持一致
auth.token = "xiaobai"
webServer.addr = "0.0.0.0"
webServer.port = 4000
[[proxies]]
name = "weixin-proxy"
type = "stcp"
secretKey = "xiaobai"
localIP = "127.0.0.1"
localPort = 8080
打开cmd,执行
frpc.exe -c frpc.toml

解释一下,当访问公网wwww.xbjava.com的8088端口时,实际访问的是我们本地的8080端口,做了一层代理转发
所以我们的java项目要运行在8080端口
2、申请微信公众号测试
https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

3、配置文件配置

4、编写微信服务器验证接口
这一步的目的是让微信信任我们的服务器,简单描述一下,这一步就是说你配置一个你的服务器地址,微信会调用你的地址,并且给你传一堆加密参数,你以指定的格式解密,判断消息是不是来自于微信,想当于开发者跟微信服务器达成一种协议,验证通过后,微信会把用户的消息转发给你。
注意:这是个GET请求,用作服务器验证,通过后微信会将用户消息以POST请求转发给你的服务器,也是这个URL
参考官方文档:https://developers.weixin.qq.com/doc/service/guide/dev/push/
// 微信服务器验证
@GetMapping("/callback")
public String validateServer(@RequestParam("signature") String signature,
@RequestParam("timestamp") String timestamp,
@RequestParam("nonce") String nonce,
@RequestParam("echostr") String echostr) {
String token = weChatProperties.getToken();
String[] arr = {token, timestamp, nonce};
Arrays.sort(arr);
String content = String.join("", arr);
String sha1 = DigestUtils.appendMd5DigestAsHex(content.getBytes(StandardCharsets.UTF_8), new StringBuilder()).toString();
// 注意:微信要求是 SHA1,这里示例用 Spring 的工具展示流程,生产请替换为真正的 SHA1
// 返回 echostr 代表验证通过
return echostr;
}
5、启动项目开始验证

6、验证成功

7、编写接收消息接口
同样的接口URL,POST请求,当我关注测试号,发送你好,微信会将消息转发给服务器,如下是消息内容
注意,接收到消息之后,处理完回复指定文本,或者回复空串,否则微信服务器会重试,可以根据msgid进行消息排重
官网地址:https://developers.weixin.qq.com/doc/subscription/guide/product/message/Receiving_standard_messages.html

8、编写登录逻辑
// 接收用户关注/消息事件:当接收到包含 openId 的 XML(如关注或发送“登录”)
@PostMapping("/callback")
public String onMessage(HttpServletRequest request) throws Exception {
String xml = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
// 极简解析 openId
String openId = parseXmlByTagName(xml, "FromUserName");
// 解析用户内容
String content = parseXmlByTagName(xml, "Content");
if ("登录".equals(content)) {
// 用户是进行登录
// 生成验证码
String code = generateSixDigitCode();
// 保存到 Redis,5 分钟过期
String userKey = "login:code:" + openId;
String idxKey = "login:code:idx:" + code;
redisTemplate.opsForValue().set(userKey, code, 5, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(idxKey, openId, 5, TimeUnit.MINUTES);
// 将验证码返回给用户
return code;
} else {
// 执行其他有需要的业务逻辑
}
// 按微信协议必须返回空串或固定文本,避免重复推送
return "";
}
登录验证
// 接收用户关注/消息事件:当接收到包含 openId 的 XML(如关注或发送“登录”)
@PostMapping("/callback")
public String onMessage(HttpServletRequest request) throws Exception {
Map<String, String> stringStringMap = parseWxReceiveMessageAsMap(request);
// 极简解析 openId
String openId = stringStringMap.get("FromUserName");
// 解析用户内容
String content = stringStringMap.get("Content");
if ("登录".equals(content)) {
// 用户是进行登录
// 生成验证码
String code = generateSixDigitCode();
// 保存到 Redis,5 分钟过期
String userKey = "login:code:" + openId;
String idxKey = "login:code:idx:" + code;
redisTemplate.opsForValue().set(userKey, code, 5, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(idxKey, openId, 5, TimeUnit.MINUTES);
// 将验证码返回给用户
return buildTextMessageReply(code, stringStringMap);
} else {
// 执行其他有需要的业务逻辑
}
// 按微信协议必须返回空串或固定文本,避免重复推送
return "";
}
9、开发前端页面

10、测试
先随便输一个

正确的

大功告成
把登录功能接入你的网站,用户每次扫码登录就ok了,不需要在输入用户名密码进行登录了
后续逻辑分析
其实对于我们网站来讲,并不需要知道用户的用户名是啥,并不重要,主要的是能够确定用户的合法性即可,用户登录后再网站可以进行相关操作即可,重要的是快捷,方便
当然如果已经做了用户名登录的也不要大改,可以做两种登录方式,将openid与用户进行绑定即可实现两种登录方式
说明一下,本次用的是测试账号,如果是真实账号,位置如下:
友情提示:启用服务器配置后,公众平台配置的自动回复、自定义菜单均失效,请知悉!
原因是启用了服务器配置,自定义菜单均可以通过api去配置,关于自动回复小白还想说两句,公众平台上限200条,公众平台上限200条,公众平台上限200条

今天的分享就到这里,感谢你的认真阅读!
1466

被折叠的 条评论
为什么被折叠?



