前言
尝试开发微信扫码功能,参考了两位学长的文章:
https://segmentfault.com/a/1190000043395324
https://segmentfault.com/a/1190000043413908?utm_source=sf-similar-article
功能尚未完成,不过记录一下目前为止在此实现方式上遇到的一些问题;
记录一下相关变量含义:
wsAuthToken:前台生成的uuid。
xAuthToken: 用户身份验证令牌
map(WebSocketService): 绑定wsAuthToken和xAuthToken
map(UserServiceImpl): 绑定loginUid和username
openid: 微信向开发者提供的用户唯一标识符
微信对接
微信对接的基本流程是:微信服务器向开发者在微信公众平台提供的服务器地址发送请求,然后服务器返回原样给微信服务器请求内容,微信服务器验证通过后对接成功。
因为这个过程中涉及到了微信服务器向服务器发起请求,如果服务器位于内网,那么外界就无法访问到,所以需要通过内网穿透将内网的服务器映射到外网。
首先尝试使用了ngrok,尝试对接时却发现调用不到后台的方法,但是在本机上使用浏览器访问外网地址就可以出发对应的方法。
发现出现以上情况是因为在访问ngrok提供的外网地址时,会先弹出一个界面
所以微信服务器接收到的返回值实际上是这个界面,最终也就配置失败了。
ngrok没有成功之后切换使用frp工具来内网穿透,不过使用frp需要有一台公网的服务器。
Invalid Host Header
微信对接完成之后通过外网地址访问服务出现了Invalid Host Header
的错误
如果在本机访问服务可以正常访问。
出现原因如下:
"Invalid Host Header" 是由 Web 服务器的安全机制所触发的错误,通常是为了防止 HTTP Host 欺骗攻击。当 Web 服务器收到一个请求时,它会检查请求头中的 Host 字段是否与服务器上的域名匹配。如果不匹配,则服务器会拒绝该请求并返回 "Invalid Host Header" 错误。
通过浏览器访问服务器时,请求的Host为abc.com:7005,然后通过nginx将请求转发到服务器上4200端口的web服务上。
在这个过程中,如果nginx设置了proxy_set_header Host $host
,nginx在转发时就会将host修改为客户端请求的主机名,也就是abc.com:7005;如果不设置,发送到web服务的host的值就会是nginx所在服务器的域名或IP地址。
因为我这里已经配置该项,所以我的web服务接收到的请求的host的值是abc.com:7005
。而进行检验之后发现请求头中的Host字段与服务器的域名不匹配最终出现了invalid host header的错误。
解决该问题只需要在启动开发服务器时候禁用hostCheck即可。
解决问题时发现在浏览器的开发者工具中查看网络请求没有host字段。应该是因为浏览器通常会将请求头分组并隐藏许多头信息,包括 Host 头。
Chrome 浏览器的开发者工具文档
未获取到sessionId(xAuthToken)
这里获取不到sessionId,打印后发现map的内容为空,说明webSocket没有向map中存值,map的值是通过webSocket来获取的。
不过这个方法并没有被调用,所以webSocket连接并没有成功。
检查后发现我的项目中建立连接的代码没有执行所以前台无法通过webSocket连接向map存值。
建立webSocket连接。
在开发扫码登录时,一方面因为代码逻辑问题导致连接建立失败之外。还有就是没有引入相关的js文件。
在参考文章中,SockJS
,Stomp
两个类需要单独引入文件来提供,分别是sockjs.min.js
,stomp.min.js
(见参考文章源码的assets
)。
连接方法:
前台WebsocketService->onInit()
=> 后台 WebSocketController->bindToXAuthToken()
可以扫码绑定,无法扫码登录
经过排查之后,发现是因为在登录界面时,webSocket连接还未建立,所以扫码之后,后台服务器无法向前台推送用于登录的loginUid。前台也就无法发送登录请求进行登录。问题解决只需要实现在登录界面就建立连接即可。学长的示例中连接建立如下:
private currentLoginUser$ = new ReplaySubject(1);
WebsocketService
private onInit(): void {
this.userService?.getCurrentLoginUser$().pipe(first())
.subscribe(() => {
// 连接代码
});
}
订阅用户,用户变化时重新建立连接。
着陆组件中初始化用户
this.userService.initCurrentLoginUser();
执行error代码,设置用户为undefined,并且前台此时开始尝试建立websocket连接。
this.setCurrentLoginUser(undefined);
接收到的appId与自己的不符
用户扫码后微信服务器向开发服务器推送消息时包含的信息涉及appid。
在后台配置完成之后进行扫码操作时,后台接收到的信息:
而toUser
这个字段值之后被作为WechatUser
的appid
属性的值。
而收到值的始终是如图的gh_d7exxx
与微信公众平台测试号实际的appid
不相同。
可能是因为使用的测试号,不过还没有验证,之后再进行测试。
Subject示例
Subject
订阅之后有新值再发布
BehaviorSubject
订阅时接收到上一次发布的值。创建该对象时需要指定初值。
ReplaySubject
订阅时接收到之前缓存的值,数量在创建该对象时指定。
总结
开发过程也遇到不少问题,发现对一些基本内容的了解上有所欠缺,如SpringBootSecurity登录认证,host等。也学习到了一些新的内容,之后在平常的学习中需要注意多去扩展一些新内容。