微信公众号自定义菜单配置的H5页面本质上就是一个普通网页,用户点击菜单后微信客户端会打开对应的URL。由于菜单链接可以被复制并在浏览器或其他环境打开,如果H5页面不做任何访问控制,敏感内容就会直接暴露。路由守卫的作用是在页面切换前拦截请求,判断当前用户是否具备访问权限,从而决定是否继续渲染目标页面或重定向到登录、提示页。

微信环境下路由守卫的设计原理
在单页应用(SPA)中,路由守卫是框架提供的钩子函数,例如Vue的beforeEach或React配合react-router的render拦截。微信内访问H5时,系统不会自动注入用户凭证,因此守卫不能仅依赖本地存储的标记。正确的思路是:每次进入受保护路由前,守卫先向后端发起校验请求,后端通过微信的网页授权code换取的openid或业务token,判断该用户是否已绑定账号且有权限。
如果仅在前端用localStorage里的一个isLogin字段做判断,攻击者清空存储或伪造请求就能绕过。路由守卫必须是一个异步验证过程,且验证结果以后端为准。微信环境还涉及静默授权获取code,守卫中需要处理未拿到code时先跳微信授权页,回来再继续校验的逻辑,否则会出现死循环或白屏。
另外一个核心点是守卫的粒度。菜单跳转的H5可能包含多个子路由,建议在路由表中给每个路由加meta.requiresAuth字段,守卫统一读取该字段决定是否拦截。这样新增页面时只需配置路由元信息,不必修改守卫主体代码,也降低遗漏风险。
Vue项目中的路由守卫代码实现
下面以Vue Router为例,展示如何在微信内实现带后端校验的路由守卫。代码中假设后端提供/api/checkAuth接口,接收微信code或token返回是否有权限。
import router from './router'
import axios from 'axios'
// 后端校验接口,返回 { valid: true/false }
async function verifyUser() {
let token = localStorage.getItem('token')
if (!token) {
// 尝试从微信授权回调拿code换token
const code = new URLSearchParams(location.search).get('code')
if (code) {
const res = await axios.get('/api/wxLogin?code=' + code)
token = res.data.token
localStorage.setItem('token', token)
}
}
if (!token) return false
const check = await axios.get('/api/checkAuth?token=' + token)
return check.data.valid
}
router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAuth) {
const ok = await verifyUser()
if (ok) {
next()
} else {
// 无权限跳转到提示页或微信授权页
const appid = 'your_appid'
const redirect = encodeURIComponent(location.href)
location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=' + appid + '&redirect_uri=' + redirect + '&response_type=code&scope=snsapi_userinfo&state=1#wechat_redirect'
}
} else {
next()
}
})
上述代码把微信授权跳转和后端校验都封在守卫里。需要注意的是,微信授权回调后URL带有code参数,再次进入守卫时应先用code换token再校验,避免重复跳授权页。生产环境还应处理接口异常,比如网络失败时暂存目标路由,提示用户重试。
对比纯前端方案,这种写法的优势是权限实时生效。后台封禁某用户后,下次路由跳转校验就会失败,而纯前端方案在token过期前都拦不住。缺点是每次跳转多一次网络请求,可通过在守卫内加短时长缓存(如30秒内存标记)减少重复校验,但绝不能永久缓存。
常见误区与前后端协同方案对比
不少开发者认为微信自定义菜单只有粉丝能点,所以H5不用做权限。实际上菜单链接本质是公开URL,非粉丝通过分享也能打开。另一个误区是把路由守卫写成同步函数,仅判断localStorage有无token,这在微信里极容易被绕过,因为token可以伪造或过期不刷新。
前后端协同的正确做法是:前端守卫负责拦截并触发授权,后端负责签发短期有效的签名,并校验每个敏感接口的访问权。下表列出两种方案差异:
| 方案 | 实现方式 | 安全性 | 适用场景 |
|---|---|---|---|
| 纯前端判断 | 路由守卫读localStorage标记 | 低,可伪造 | 非敏感展示页 |
| 前后端校验 | 守卫调后端接口验openid或token | 高,后端可控 | 会员、订单等敏感页 |
在微信内还应利用公众号的网页授权域名限制,把H5部署在已配置的域名下,配合后端referer校验,进一步防止链接被放到其他域名的 iframe 中套取内容。路由守卫只是第一道门,后端接口必须再次校验用户身份,做到双层防护。
最后提醒,自定义菜单的链接如果带参数如?from=menu,守卫中不要信任这些参数做权限判断,因为攻击者能随意修改。所有权限依据必须来自后端基于微信用户唯一标识的计算结果,才能保证微信公众号H5页面的访问可控。