vue-router 'hash' 模式下微信公众号授权 redirect_uri带有'#'处理;以及授权后页面后退死循环问题

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;
  }
}

注:

  1. [多级目录路径]如果项目不是部署在网站根目录,需要在url上自己加上多级目录的路径
  2. 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
 */



注:

  1. 可以在pageB没获取到openId的时候,隐藏整个页面,这样体验更好一些,没有页面闪烁;
  2. 有个疑问,从pageB?code=***后退到pageB要跨越pageC,明显是2条历史记录,在死循环问题中就已知;
    但是pageB?code=***执行后退的时候只用返回一条记录(history.go(-1))就到了pageB.这个想不明白...

你可能感兴趣的:(vue-router 'hash' 模式下微信公众号授权 redirect_uri带有'#'处理;以及授权后页面后退死循环问题)