前后端分离 微信登录 高并发问题
具体描述
前端发起请求 ->(改进 插入 state: uuid)
后端拉起二维码 ->
用户确认 ->
微信回调 ->
自己的处理逻辑(将用户信息 存入 token (这里使用的是jwt)) ->
重定向到 (首页)等等
(改进 前端再次访问 后端 获取 token)
这里出现的问题
如何将 token 提供给前端
因为是前后端分离 直接 return 会原路返回,不能跳转的自己的界面
1.使用ThreadLocal,由前端主动发起请求获取 token
private ThreadLocal token = new ThreadLocal();
但是 springboot 中 controller 是单例多线程的
而 ThreadLocal 是为 当前线程赋值, 前端来取是另一个线程,故取不到
2.存入cookie、session
cookie 可以被用户禁用
session 在app上没有这种概念
故不可取
3.最开始是想,在回调的时候响应的时候直接 将token由response来设置头信息设置上去,但随后 redirect 重定向 (但 这里 重定向 是 网页自动完成 (token在上一个 现在重定向是另一个获取不到)(又因为是自动完成的重定向 故获取不到(这里是本人觉得获取不到,因为不太会))
没实现
成功解决的方案
在用户 点击 微信登录 拉起 后端 这里将 拉起微信二维码 的 参数
appid、redirect_uri、scope 传给前端由前端实现二维码的拉起
这里加入 一个操作
获取一个uuid 将 uuid 也传给前端 前端接收 接收名为 state
拉起的时候将 state 传入 微信二维码 这样 回调 访问后端的时候
将解析出来的用户信息 存入 token (这里用的 jwt )
以 state为key ----- token为 value 存入 redis
回调完成后 重定向 到 首页 ,
此时来完成轮询 (通过判断 来 携带 state 访问 后端 )
由state (key) 获取 redis 中的 token (value)
由此来获取用户信息
- 用户点击微信登录
@GetMapping("/login/wechat")
// Result 是自定义的 统一返回 结果 map 结构
public Result wxLogin(HttpServletResponse response) throws IOException {
String uuId = UUID.randomUUID().toString().replace("-", "");
System.out.println("state = " + uuId);
return Result.ok()
.setData("state", uuId)
.setDate("scope",wechatConfig.getScope())
.setData("appid", wechatConfig.getAppid())
.setData("redirect_uri", wechatConfig.getRedirectUri());
}
- 前端处理 二维码 参数
loginWx() {
this.axios
.get("http://localhost:9000/user/user/login/wechat")
.then((result) => {
console.log(result)
document.getElementById("loginForm").style.display = "none";
// 显示二维码的容器
document.getElementById("wxLoginForm").style.display = "block";
// const url = result.data.data.item;
this.state = result.data.data.state;
this.appid = result.data.data.appid;
this.scope = result.data.data.scope;
this.redirect_uri = result.data.data.redirect_uri;
localStorage.setItem("state", this.state);
this.$nextTick(function () {
this.createCode();
});
})
将这些参数接收后 拼接完成地址 由前端打开(即二维码窗口)同时是由前端控制,可以设置 样式
createCode() {
console.log(this.state)
var obj = new WxLogin({
id: "wxLoginForm", // 挂载点,二维码的容器
appid: this.appid, // 应用唯一标识,在微信开放平台提交应用审核通过后获得
scope: this.scope, // 应用授权作用域,网页应用目前仅填写snsapi_login即可
redirect_uri: this.redirect_uri, //重定向地址,(回调地址)
state: this.state,
href: "data:text/css;base64,LmltcG93ZXJCb3ggLnFyY29kZSB7d2lkdGg6IDIwMHB4O30NCi5pbXBvd2VyQm94IC50aXRsZSB7ZGlzcGxheTogbm9uZTt9DQouaW1wb3dlckJveCAuaW5mbyB7d2lkdGg6IDIwMHB4O30NCi5zdGF0dXNfaWNvbiB7ZGlzcGxheTogbm9uZX1jcw0KLmltcG93ZXJCb3ggLnN0YXR1cyB7dGV4dC1hbGlnbjogY2VudGVyO30=" // 加载修饰二维码的css样式
});
},
!(function (a, b, c) {
function d(a) {
var c = "default";
a.self_redirect === !0
? (c = "true")
: a.self_redirect === !1 && (c = "false");
var d = b.createElement("iframe"),
e =
"https://open.weixin.qq.com/connect/qrconnect?appid=" +
a.appid +
"&scope=" +
a.scope +
"&redirect_uri=" +
a.redirect_uri +
"&state=" +
a.state +
"&login_type=jssdk&self_redirect=" +
c +
"&styletype=" +
(a.styletype || "") +
"&sizetype=" +
(a.sizetype || "") +
"&bgcolor=" +
(a.bgcolor || "") +
"&rst=" +
(a.rst || "");
(e += a.style ? "&style=" + a.style : ""),
(e += a.href ? "&href=" + a.href : ""),
(d.src = e),
(d.frameBorder = "0"),
(d.allowTransparency = "true"),
(d.sandbox = "allow-scripts allow-top-navigation allow-same-origin"), // 允许多种请求
(d.scrolling = "no"),
(d.width = "300px"),
(d.height = "400px");
var f = b.getElementById(a.id);
(f.innerHTML = ""), f.appendChild(d);
}
a.WxLogin = d;
})(window, document);
- 此处省略 回调 获取 用户信息 由jwt加密 放入 token
重定向前端
response.sendRedirect(wechatConfig.getSuccessUrl());
- 由state获取token
@GetMapping("checkWxStatus")
public Result checkWxStatus(String state) {
Object o = redisUtils.getValue(state);
System.out.println("state = " + state);
redisUtils.delete(state);
return Result.ok().setData("item", o.toString());
}