项目场景:
微信授权获取用户相关头像和昵称
问题描述
提示:现在微信官方整改了,不能在用户无知的情况下强制授权才能访问
微信网页授权规范
- 授权流程需引导清晰、准确:在申请获取用户信息的弹窗出现前,应该清晰、准确地告知用户获 取信息的范围及获取信息的目的;
- 必要场景申请:在必须获取用户信息时才申请,而不是用户尚未了解服务前就强制弹窗。如使用医院挂号时才需要获取用户信息;
- 不强制登录:提供游客模式,供用户了解网页提供的基础服务,不强制用户允许网页获取用户信息后才能使用网页服务。
常见的微信网页授权不规范使用案例
- 强制登录:在用户打开网页时立即要求用户授权,用户拒绝后无法使用网页提供的服务;
- 违规收集个人信息:未在网页提前告知使用个人信息的目的、方式和范围;
- 非必要收集:非必要获取用户信息的网页,如文章、视频等,要求用户在浏览内容前登录;
- 差别对待微信用户:同样的网页在浏览器内可以无需登录直接访问,在微信内却要求用户先登录才可访问。
微信授权限制解决方案:
链接: 微信调整公告.
微信h5授权在授权前会加载出快照页,需要点击右下角按钮进行手动授权。这种方式会给业务带来一些麻烦
提示:这样给用户体验非常不好,有点懵的
解决方案:
1、我们可以先以snsapi_base为 scope 发起的网页静默授权进入预览页
2、用户在预览页触发点击事件,那么可以走正常的授权,再以snsapi_userinfo为 scope 发起的网页非静默授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
效果图:
这是预览首页图
这是预览触发点击事件,授权获取用户信息
实现逻辑
提示:以下是本篇文章正文内容,下面案例可供参考
-
main.js
-
checkLogin.js
import { getToken, setToken } from '@/libs/util';
import weixinAuthorize, { hasCodeAndState, snsapiUserInfo } from '@/features/weixin/authorize';
import axios from '@/libs/api.request'
import { authorize } from './weixin/config'
import store from '@/store'
import Vconsole from 'vconsole'
// 非静默授权获取微信用户信息
export const weixinAuthorizeUserInfo = () => snsapiUserInfo({ state: 1 });
const checkLogin = async (isForceWeixinAuthorize = false) => {
// 登录过期后,清除缓存token,强制授权登录
if (isForceWeixinAuthorize) setToken("")
let token = getToken();
//开发者便利代码 仅开发环境使用
if (process.env.NODE_ENV === "development") {
let token = sessionStorage.getItem('gzcd_web_ApiAuth')
if (!token) token = prompt("检测到当前为开发环境 请先输入token");
setToken(token)
// sessionStorage.setItem('gzcd_web_ApiAuth', token)
}
if (!token || isForceWeixinAuthorize || hasCodeAndState) {
// 这里是因为多个公众号的问题,动态获取appid
let scope
if (!hasCodeAndState) {
// alert('获取url连接appid')
scope = sessionStorage.getItem('OauthScope') || 'snsapi_base';
let spArr = location.hash.split('?'), baseUrl;
if (location.hash) {
baseUrl = encodeURIComponent(location.protocol + '//' +location.host + "?" + spArr[1])
localStorage.setItem("km_type", 1)
} else {
let [, kmType] = location.href.match(/[?&]kmType=(\w+)/) || [];
baseUrl = encodeURIComponent(location.protocol + '//' +location.host)
localStorage.setItem("km_type", kmType || 2)
}
await getOauthUrl(baseUrl, scope);
}
// 微信授权获取 code 和 state
let res = await weixinAuthorize()
// 判断是否是区代
let [, use_nal] = location.href.match(/[?&]user_type=(\w+)/) || [];
if (location.pathname == "/profit" && use_nal == 3) {
sessionStorage.setItem('is_regional', 1)
sessionStorage.setItem('user_type', 3)
}
// 是否是静默授权获取微信用户信息和token
scope = res.state == 1 ? 'snsapi_userinfo' : 'snsapi_base'
res = await getAuUserInfo(res.code, scope, res.state);
setToken(res.data.token)
token = res.data.token
}
// 绑定用户
let [, , pid] = location.href.match(/[?&](shareMember|dst_id)=(\w+)/) || [];
let [, , bindType] = location.href.match(/[?&](shareType|bind_type)=(\w+)/) || [];
if (pid) {
bindType = bindType || 0
await bindSuper({ super_id: pid, type: bindType })
}
// 获取用户信息
await getUseInfo()
return token
}
export default checkLogin;
// 获取授权code的url连接
const getOauthUrl = async (baseUrl, scope) => {
let res = await axios.request({
url: '/v1/wx/appid',
method: 'get',
params: {}
})
authorize.appid = res.data.data
sessionStorage.setItem('appid', res.data.data)
return res;
};
// 根据授权code获取 是否是静默或者非静默登录 state: 0 是, 1 否 改版
const getAuUserInfo = async (code, scope, state) => {
let res = await axios.request({
url: 'v1/wx/index',
method: 'get',
params: {code: code, sns_scope: scope, silent: state}
})
let time = Math.round(new Date().getTime() / 1000)
if (res.code === 200) {
if (res.data.info.is_debug != 0) {
new Vconsole();
}
let regional = sessionStorage.getItem('is_regional') || 0
if (regional && res.data.info.is_regional == 1) {
res.data.info.user_type = 3
}
store.commit('setNewUser', res.data.info.is_new)
store.commit('setUserInfo', res.data.info)
localStorage.setItem('shareMember', res.data.info.id)
sessionStorage.setItem('user_login_time', time);
sessionStorage.setItem("user_type", res.data.info.user_type)
}
return res;
};
// 绑定用户
const bindSuper = async (data) => {
if (sessionStorage.getItem('is_bind_share')) {
return;
}
let res = await axios.request({
url: 'v1/user/bind-user',
method: 'post',
data: data
})
sessionStorage.setItem('is_bind_share', 1)
return res;
};
// 获取用户相关信息
const getUseInfo = async () => {
let res = await axios.request({
url: 'v1/wx/ref-token',
method: 'get',
})
let time = Math.round(new Date().getTime() / 1000)
if (res.code === 200) {
if (res.data.member.is_debug != 0) {
new Vconsole();
}
// state.km1_show = Vue.prototype.$config.km1_show
// state.km3_show = Vue.prototype.$config.km3_show
let regional = sessionStorage.getItem('is_regional') || 0
if (regional && res.data.member.is_regional == 1) {
res.data.member.user_type = 3
}
store.commit('setUserInfo', res.data.member)
store.commit('setToken', res.data.token)
sessionStorage.setItem('user_login_time', time);
sessionStorage.setItem('user_type', res.data.member.user_type)
}
return res;
};
- authorize.js
import stringify from 'qs/lib/stringify';
// 这是判断是否微信浏览器打开 export const isWeixin = /micromessenger/i.test(navigator.userAgent);
import { isWeixin } from '@/features/weixin/utils';
// sortObj.js文件
import sortObj from '../common/utils/sortObj';
// 配置文件 builderAuthorizeUrl 方便获取
// export const authorize = {
// uri: 'https://open.weixin.qq.com/connect/oauth2/authorize?',
// appid: '',
// state: 0,
// };
import { authorize } from './config'
// 清除 code 和 state,locationHrefReplaceSearchKey.js文件, 也可以用自己的思路清除
import locationHrefReplaceSearchKey from '@/features/common/utils/locationHrefReplaceSearchKey';
export const codeStateRegExp = /[?&]code=(.*)&state=(\w+)/;
export const defaultConfig = {
scope: 'snsapi_base',
};
export const builderAuthorizeUrl = (config) => {
// config 作为参数动态覆盖之前的参数
let search = {
appid: sessionStorage.getItem('appid'),
state: authorize.state,
response_type: 'code',
redirect_uri: location.href.replace(codeStateRegExp, ''),
...defaultConfig,
...(config || {}),
}
// 判断两者是否包含在参数里
if (!['snsapi_base', 'snsapi_userinfo'].includes(search.scope)) {
search.scope = 'snsapi_base';
}
// 检查参数排序
search = sortObj(search);
if (search.scope === 'snsapi_userinfo') {
search.forcePopup = true;
// search.forceSnapShot = true;
}
return `${authorize.uri}${stringify(search)}#wechat_redirect`;
};
export const hasCodeAndState = codeStateRegExp.test(location.href) || sessionStorage.getItem('gzcd_code');
const snsapiBase = async (config) => {
const [, code, state] = location.href.match(codeStateRegExp) || [];
// 2 获取url 的 code & state 存缓存,并移除url的 code & state
if (code && state) {
sessionStorage.setItem('gzcd_code', code)
sessionStorage.setItem('gzcd_state', state)
if (!/android/i.test(navigator.userAgent)) {
locationHrefReplaceSearchKey(['code', 'state']);
if (codeStateRegExp.test(location.search)) throw '';
} else {
history.go(-1);
throw '';
}
}
// 3
if (sessionStorage.getItem('gzcd_code') || sessionStorage.getItem('gzcd_state')) {
let code = sessionStorage.getItem('gzcd_code')
let state = sessionStorage.getItem('gzcd_state')
if (!code || !state) return history.back();
sessionStorage.removeItem('gzcd_code')
sessionStorage.removeItem('gzcd_state')
return { code, state };
}
// 1 判断是否是微信浏览器打开 重定向到微信
if (isWeixin || !location.port) {
// alert('判断是否是微信浏览器打开')
location.replace(builderAuthorizeUrl(config));
}
throw '';
};
export default snsapiBase;
// 非静默授权获取用户信息
export const snsapiUserInfo = (config = {}) =>
snsapiBase({ scope: 'snsapi_userinfo', ...config });
- sortObj.js
export default (obj) =>
Object.keys(obj)
.sort()
.reduce((result, key) => {
result[key] = obj[key];
return result;
}, {});
- locationHrefReplaceSearchKey.js
export default (keys, useHistory = false) => {
if (typeof keys === 'string') keys = [keys];
let replaceUrl = location.href
.replace(new RegExp(`${location.origin}//?`), '/')
.replace(new RegExp(`&?(${keys.join('|')})=([\\w-]+)`, `g`), '')
.replace(/\?&/, '?')
.replace(/\?#/, '#')
.replace(/\?$/, '');
// tips: 必须得在路由初始化前后执行
if (useHistory) {
history.replaceState(null, null, replaceUrl);
} else {
location.replace(replaceUrl);
}
};
以上就是该功能的内容,如果有不对之处或者有不懂,可以留言指出改正,谢谢!!!