软件开发者:微信小程序获取用户手机号
在微信小程序中通过获取用户手机号实现登录验证的完整流程如下:
一、准备工作
- 微信认证
- 小程序必须已完成微信认证(非个人主体)
- 权限配置
- 在小程序管理后台的 开发 > 开发管理 > 接口设置 中开通 获取手机号 权限
二、前端实现流程
步骤1:用户登录凭证获取
// 获取临时登录凭证 code
wx.login({
success: (res) => {
if (res.code) {
// 将 code 发送到开发者服务器
this.sendCodeToServer(res.code)
} else {
console.error('登录失败:' + res.errMsg)
}
}
})
步骤2:添加手机号获取按钮
<button
open-type="getPhoneNumber"
@getphonenumber="handleGetPhone"
type="primary"
>授权手机号登录</button>
步骤3:处理授权回调
methods: {
handleGetPhone(e) {
if (e.detail.errMsg === 'getPhoneNumber:ok') {
// 获取加密数据
const { encryptedData, iv } = e.detail
// 发送加密数据到服务器解密
wx.request({
url: 'https://yourdomain.com/api/decrypt-phone',
method: 'POST',
data: {
encryptedData,
iv,
code: this.loginCode // 之前获取的登录凭证
},
success: (res) => {
// 处理服务器返回的解密后手机号
console.log('用户手机号:', res.data.phoneNumber)
}
})
} else {
console.error('用户拒绝授权')
}
}
}
三、后端处理流程(JAVA处理)
步骤1:获取 session_key (HTTP 请求工具类)
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import com.alibaba.fastjson.JSONObject;
public class WeChatUtils {
// 获取 session_key 和 openid
public static JSONObject getSessionInfo(String code, String appId, String appSecret) {
String urlStr = "https://api.weixin.qq.com/sns/jscode2session?" +
"appid=" + appId +
"&secret=" + appSecret +
"&js_code=" + code +
"&grant_type=authorization_code";
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return JSONObject.parseObject(response.toString());
} catch (Exception e) {
throw new RuntimeException("获取 session_key 失败", e);
}
}
}
步骤2:手机号解密工具类
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class PhoneDecryptor {
// 解密手机号
public static String decryptPhone(String encryptedData, String iv, String sessionKey) {
try {
// Base64 解码
byte[] encryptedDataBytes = Base64.getDecoder().decode(encryptedData);
byte[] ivBytes = Base64.getDecoder().decode(iv);
byte[] sessionKeyBytes = Base64.getDecoder().decode(sessionKey);
// AES 解密
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec keySpec = new SecretKeySpec(sessionKeyBytes, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
byte[] decryptedBytes = cipher.doFinal(encryptedDataBytes);
// 处理 PKCS#7 填充
int pad = decryptedBytes[decryptedBytes.length - 1];
byte[] data = new byte[decryptedBytes.length - pad];
System.arraycopy(decryptedBytes, 0, data, 0, data.length);
// 解析 JSON
String result = new String(data, StandardCharsets.UTF_8);
return JSONObject.parseObject(result).getString("phoneNumber");
} catch (Exception e) {
throw new RuntimeException("手机号解密失败", e);
}
}
}
步骤3:Controller 层示例(Spring Boot)
import com.alibaba.fastjson.JSONObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
private static final String APP_ID = "your_appid";
private static final String APP_SECRET = "your_secret";
@PostMapping("/login")
public String login(@RequestBody LoginRequest request) {
// 1. 获取 session_key
JSONObject sessionInfo = WeChatUtils.getSessionInfo(
request.getCode(),
APP_ID,
APP_SECRET
);
// 2. 解密手机号
String phoneNumber = PhoneDecryptor.decryptPhone(
request.getEncryptedData(),
request.getIv(),
sessionInfo.getString("session_key")
);
// 3. 业务处理(保存用户信息等)
return "登录成功,手机号:" + phoneNumber;
}
}
// 请求参数 DTO
class LoginRequest {
private String code;
private String encryptedData;
private String iv;
// getters & setters
}
步骤4:Maven 依赖
<dependencies>
<!-- FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
四、安全注意事项
- 会话密钥管理
- session_key 的有效期为 5 分钟,且每次登录会刷新
- 加密验证
- 必须验证 encryptedData 和 iv 的完整性
- 防重放攻击
- 对解密请求添加时效性验证(建议 5 分钟内有效)
- 数据存储
- 手机号等敏感信息需加密存储(如 AES-256)
五、错误处理方案
错误场景 | 处理方案 |
用户拒绝授权 | 显示引导提示,允许手动输入手机号 |
网络请求失败 | 自动重试机制 + 友好错误提示 |
session_key 失效 | 重新执行登录流程获取新 code |
解密失败 | 日志记录 + 人工审核流程 |
相关文章
- 图文并茂,带你梳理一下 OAuth2.0 概念和授权流程
- 理解OIDC协议和认证机制(ociq协议)
- Spring Boot OAuth 2.0整合详解(springbootspringsecurity oauth)
- 授权码 + PKCE 模式|OIDC & OAuth2.0 认证协议最佳实践系列【03】
- 微服务下统一认证解决方案 Spring Cloud OAuth2 + JWT
- 无惧面试官,带你全面了解OAuth2.0
- 干货:分享一个自己开发的oauth2单点登录项目
- 掏出手机,扫一扫,即刻登录——SpringBoot实现二维码扫码登录
- Oauth2.0详解及安全使用(oauth2.0原理)
- 5分钟彻底搞懂"OAuth2.0"协议