1 redirect_uri带有'#'问题
当然,如果可以的话直接使用history
模式即可.
vue-router
默认hash
模式下,页面的url都带有#
,但微信授权的回调地址不能有#
,所以要进行一些处理
1.1 获取code
const getCodeParams = {
redirect_uri: encodeURIComponent(location.href.split('#')[0]),
appid: "***",
scope: "snsapi_base",
state: encodeURIComponent(
JSON.stringify({
p: "/login", // 实际的redirect_uri路由path
... // 其它参数
})
),
};
location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${getCodeParams.appid}&redirect_uri=${getCodeParams.redirect_uri}&response_type=code&scope=${getCodeParams.scope}&state=${getCodeParams.state}#wechat_redirect`;
1.2 全局路由守卫处理
授权后微信自动跳转的url:http://***/login?code=***&state=***#/
处理后的url:http://***/#/login?code=***&state=***
vue router 4.x代码实现,
router.beforeEach((to, from) => {
// console.log(to, from);
if (replaceWechatRedirectUri()) return false;
});
/**
* 处理微信授权回调redirect_uri
*/
function replaceWechatRedirectUri() {
const w = location.href.indexOf('?');
const j = location.href.indexOf('#');
if (w !== -1 && j > w && getQueryVariable('state')) {
const state = (getQueryVariable('state') as string).split('#')[0];
const redirect_path = JSON.parse(decodeURIComponent(state)).p;
const url =
location.origin +
'[多级目录路径]/#' +
redirect_path +
`?code=${getQueryVariable('code')}&state=${state}`;
location.replace(url);
return true;
}
}
注:
-
[多级目录路径]
如果项目不是部署在网站根目录,需要在url上自己加上多级目录的路径 -
getQueryVariable()
是获取url参数的自定义方法
2 授权后执行后退操作,死循环问题
页面历史
pageA -> pageB(在此页面开始授权) -> pageC(微信授权页面) -> pageB?code=***(授权完成后微信重定向)
2.1 问题描述
我们在pageB?code=***
可以获取到code
,然后去换取openid
;但是在pageB?code=***
执行后退操作的时候,会自动重授权再进入pageB?code=***
,再后退亦然,就进入了一个死循环.
2.2 原因
微信授权页pageC
会push到浏览器的历史记录中,而不是replace的方式
2.3 解决
在pageB?code=***
中把url
携带的code
参数保存在sessionStorage
,然后自动执行执行一次后退操作,返回到pageB
,这样既能拿到code
又能保持正常的页面历史了
vue3代码实现
/**
* useGetOpenId
*/
import { ref, Ref } from 'vue';
import { useRoute } from 'vue-router';
import { RequestWeChatUserInfo } from '***';
/**
* 获取用户openid
*/
interface Result {
openIdRef: Ref;
}
export const useGetOpenId = (): Result => {
const route = useRoute();
const openIdRef = ref('');
const urlCode = route.query.code;
const localCode = sessionStorage.getItem('wxAuthCode');
const localOpenId = sessionStorage.getItem('wxOpenId');
if (localOpenId) {
openIdRef.value = localOpenId;
} else if (urlCode) {
sessionStorage.setItem('wxAuthCode', urlCode as string);
history.go(-1); // 后退
} else if (localCode) {
sessionStorage.removeItem('wxAuthCode');
// 获取openid
RequestWeChatUserInfo({ code: localCode }).then((res: any) => {
openIdRef.value = res.data.openid;
sessionStorage.setItem('wxOpenId', res.data.openid);
});
} else {
// 获取code
const getCodeParams = {
redirect_uri: encodeURIComponent(location.href.split('#')[0]),
appid: '***',
scope: 'snsapi_base',
state: encodeURIComponent(
JSON.stringify({
p: route.path,
}),
),
};
location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${getCodeParams.appid}&redirect_uri=${getCodeParams.redirect_uri}&response_type=code&scope=${getCodeParams.scope}&state=${getCodeParams.state}#wechat_redirect`;
}
return { openIdRef };
};
/**
* pageB
*/
{{ openIdRef }}
注:
- 可以在
pageB
没获取到openId
的时候,隐藏整个页面,这样体验更好一些,没有页面闪烁; - 有个疑问,从
pageB?code=***
后退到pageB
要跨越pageC
,明显是2条历史记录,在死循环问题中就已知;
但是pageB?code=***
执行后退的时候只用返回一条记录(history.go(-1)
)就到了pageB
.这个想不明白...