"用户点一下,系统跑千里。"
------小程序登录看似简单,背后却是一套精密的身份信任链。
在微信生态中,小程序登录不是"输账号密码",而是一场三方协作的信任建立仪式:用户、你的服务器、微信平台------三者缺一不可。详解 token 过期处理、静默刷新、退出登录等关键场景,并明确每个参数的作用与安全边界。帮助 Java 开发者构建清晰、可靠、符合微信规范的登录体系。
一、为什么不能像 Web 那样直接登录?
传统 Web 登录依赖账号密码体系,但小程序运行在微信封闭环境中,不允许开发者直接获取用户密码或手机号等敏感信息。微信出于安全和隐私考虑,提供了一套标准的身份识别机制:
- 微信负责验证"这个用户是不是真实的微信用户";
- 你(开发者)负责决定"这个用户能做什么"。
因此,登录过程被拆解为两个阶段:
- 身份识别 :通过微信获取用户唯一标识(
openid); - 会话管理:你自己生成并维护登录状态(如 Token)。
核心原则:前端只传递临时凭证,后端才是身份仲裁者。
二、登录全流程详解

第一步:前端获取临时登录凭证 code
当用户点击"登录"按钮时,小程序调用:
javascript
wx.login({
success(res) {
if (res.code) {
// 将 code 发送给你的后端
wx.request({
url: 'https://api.yourdomain.com/auth/login',
method: 'POST',
data: { code: res.code }
});
}
}
});
code 是什么?有什么用?
- 它是微信生成的一次性、短期有效的临时票据。
- 有效期约 5 分钟 ,且只能使用一次。
- 它本身不包含任何用户信息,仅作为"请求微信帮忙查身份"的凭证。
- 类比:就像你去银行办事,先拿一个叫号单(code),柜台(微信)看到号单才知道要为你服务。
第二步:后端用 code 向微信换取用户身份
你的 Java 后端收到 code 后,必须立即向微信接口发起请求:
java
// 构造请求 URL
String url = "https://api.weixin.qq.com/sns/jscode2session" +
"?appid=YOUR_APPID" +
"&secret=YOUR_APPSECRET" +
"&js_code=" + code +
"&grant_type=authorization_code";
// 发起 HTTP GET 请求(使用 RestTemplate、OkHttp 等)
Map<String, Object> wxResponse = httpClient.get(url);
微信返回的 JSON 示例:
json
{
"openid": "oAbc123xyz...",
"session_key": "KLMnop456qrs..."
}
appid 和 appsecret是什么?
appid:你的小程序唯一标识,告诉微信"你是谁"。appsecret:只有你和微信知道的密钥,用于证明"你确实是这个小程序的合法后端"。
openid 是什么?
- 用户在当前小程序下的唯一 ID。
- 同一个用户在不同小程序中
openid不同。 - 可用于数据库关联用户数据(如
user.openid = openid)。 - 但它不能直接当作登录凭证,因为它是永久不变的,一旦泄露无法撤销。
session_key 是什么?
- 用于解密用户敏感数据 的密钥,例如:
- 用户点击"获取手机号"后返回的加密数据;
- 用户头像、昵称等通过
<button open-type="getUserInfo">获取的信息。
- 必须安全存储在后端 ,绝不能返回给前端。
- 有效期由微信控制,通常在用户长时间无操作后失效。
第三步:后端生成自定义登录凭证(Token)
微信给了你身份(openid)和解密钥匙(session_key),但你不能直接把它们交给前端。你需要颁发一张通行证------即你自己的登录凭证。
java
// 1. 将 session_key 存入 Redis,以 openid 为键
redis.setex("wechat:session:" + openid, 86400, sessionKey); // 24小时过期
// 2. 生成 JWT Token
String token = Jwts.builder()
.setSubject(openid) // 载荷:用户标识
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24小时过期
.signWith(SignatureAlgorithm.HS256, "your_jwt_secret")
.compact();
// 3. 返回给前端
return new LoginResponse(token);
为什么不用 openid 直接当 Token?
openid永久不变,一旦泄露,攻击者可永久冒充用户。- 自定义 Token 可设置过期时间、可主动注销,更安全可控。
第四步:前端存储 Token 并用于后续请求
javascript
// 登录成功后
wx.setStorageSync('ACCESS_TOKEN', token);
// 封装带认证的请求
function authRequest(url) {
const token = wx.getStorageSync('ACCESS_TOKEN');
return wx.request({
url,
header: { Authorization: 'Bearer ' + token }
});
}
此后,所有需要身份的 API 请求都会自动携带 Token。
三、登录状态如何维持?
场景:用户第二天打开小程序,还能自动登录吗?
可以!只要 Token 未过期。
- 小程序启动时检查本地是否有
ACCESS_TOKEN; - 有则直接用于 API 请求;
- 后端验证 Token 有效 → 返回数据 → 用户无感登录。
Token 过期了怎么办?
方案一:被动刷新
- 后端返回
401 Unauthorized; - 前端捕获后,重新调用
wx.login()流程。
方案二:主动静默刷新
利用 wx.checkSession() 判断微信原始会话是否仍有效:
javascript
wx.checkSession({
success: () => {
// 微信会话有效 → 可静默获取新 code 换新 Token
wx.login({ success: ({ code }) => refreshToken(code) });
},
fail: () => {
// 微信会话已失效 → 必须用户重新触发登录
doFullLogin();
}
});
wx.checkSession()本质是检查session_key是否未过期。如果有效,说明用户近期活跃,可免打扰刷新登录态。
四、后端如何验证每次请求?
通过拦截器解析并验证 Token:
java
// 从 Header 中提取 Token
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new UnauthorizedException();
}
String token = authHeader.substring(7);
// 验证 JWT
Claims claims = Jwts.parser()
.setSigningKey("your_jwt_secret")
.parseClaimsJws(token)
.getBody();
String openid = claims.getSubject();
// 可选:检查 session_key 是否仍存在(增强安全性)
if (redis.get("wechat:session:" + openid) == null) {
throw new UnauthorizedException(); // 用户已退出或会话过期
}
// 将 openid 放入上下文,供业务逻辑使用
UserContext.setCurrentUser(openid);
这样,你的业务接口就能安全地知道"当前是谁在操作"。
五、如何实现"退出登录"?
很简单:
1.让 Token 失效。
javascript
// 前端清除本地 Token
wx.removeStorageSync('ACCESS_TOKEN');
2.销毁当前会话
java
@PostMapping("/auth/logout")
public void logout() {
String openid = UserContext.getCurrentUser();
redis.delete("wechat:session:" + openid); // 删除 session_key
}
即使不销毁session_key,由于 Token 本身有过期时间,风险也是可控的。
六、总结:参数与职责一览
| 参数 | 产生方 | 用途 | 是否可暴露 |
|---|---|---|---|
code |
微信 → 前端 | 一次性票据,用于换取用户身份 | 可短暂传输,但不可存储 |
appid |
开发者配置 | 标识小程序身份 | 可公开(前端可见) |
appsecret |
开发者配置 | 证明后端合法性 | 绝对保密 |
openid |
微信 → 后端 | 用户在本小程序的唯一 ID | 可用于后端关联数据,不可直接当 Token |
session_key |
微信 → 后端 | 解密用户敏感数据 | 必须保密,仅后端使用 |
| 自定义 Token | 后端 → 前端 | 维持登录状态 | 可传输,但需 HTTPS 保护 |
七、总结:
微信只负责"你是谁",你负责"你能做什么"。
通过这套机制,你既遵守了微信的安全规范,又构建了灵活、可控、可审计的用户会话体系。登录不再是黑盒,而是一条清晰、安全、可维护的信任链。
参考文章: