SpringBoot 实现微信扫码登录
一、准备工作
注册微信开放平台
申请网站应用,获取AppID和AppSecret(需 ICP 备案域名)
设置授权回调域名(如 www.yourdomain.com)
添加 Maven 依赖
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
二、核心代码实现
- 生成微信登录二维码(Controller 层)
@RestController
@RequestMapping("/auth/wechat")
public class WechatAuthController {
// 从配置文件读取(application.yml)
@Value("${wechat.appid}")
private String appId;
@Value("${wechat.callback}")
private String callbackUrl;
/**
* 生成微信登录二维码的URL
*/
@GetMapping("/qrcode-url")
public String getQrCodeUrl() {
// 微信开放平台生成二维码的固定URL格式
String url = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" + // 固定值
"&state=YOUR_STATE"; // 防CSRF攻击随机字符串(需存储校验)
return String.format(url, appId, URLEncoder.encode(callbackUrl, StandardCharsets.UTF_8));
}
}
- 处理微信回调(获取用户信息)
/**
-
微信回调接口(需与开放平台配置的回调地址一致)
*/
@GetMapping("/callback")
public ResponseEntity<?> callback(@RequestParam String code, @RequestParam String state) {
// 1. 校验state参数(防止CSRF攻击)
if (!validateState(state)) {
return ResponseEntity.badRequest().body("非法请求");
}// 2. 用code换取access_token
String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";String response = OkHttpUtil.get(String.format(tokenUrl, appId, appSecret, code));
JsonNode tokenJson = JsonUtil.parse(response);// 3. 获取用户信息
String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";String userResponse = OkHttpUtil.get(
String.format(userInfoUrl, tokenJson.get("access_token").asText(),
tokenJson.get("openid").asText()));
JsonNode userInfo = JsonUtil.parse(userResponse);// 4. 处理用户登录(示例:创建本地用户或绑定已有账号)
User user = userService.createOrUpdateWechatUser(userInfo);// 5. 生成JWT或Session(此处以JWT为例)
String jwtToken = JwtUtil.generateToken(user.getId());return ResponseEntity.ok().header("Authorization", jwtToken).build();
}
- 工具类封装
public class OkHttpUtil {
/**
* 发送GET请求
*/
public static String get(String url) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
}
public class JsonUtil {
private static final ObjectMapper mapper = new ObjectMapper();
/**
* JSON字符串转对象
*/
public static JsonNode parse(String json) throws JsonProcessingException {
return mapper.readTree(json);
}
}
三、关键安全措施
State 参数校验
生成随机 state 字符串并存储(如 Redis),回调时验证是否匹配。
敏感信息保护
AppSecret必须存储在服务端,不可暴露给前端
用户 openid 等敏感信息需加密存储
错误处理
// 在回调接口中添加异常处理
try {
// ... 业务逻辑
} catch (WechatApiException e) {
// 处理微信接口错误(如code过期)
return ResponseEntity.status(500).body("微信登录失败:" + e.getMessage());
}
四、配置示例(application.yml)
ruby 代码解读复制代码wechat:
appid: wx1234567890abcdef # 微信应用ID
app-secret: your_app_secret_here # 微信应用密钥
callback: https://www.yourdomain.com/auth/wechat/callback # 授权回调地址
五、前端对接建议
前端通过 /auth/wechat/qrcode-url 获取二维码 URL,使用微信官方 JS 库渲染
扫码成功后,前端通过轮询或 WebSocket 检查登录状态