在 Java 中实现微信扫码登录,核心是基于微信开放平台的网站应用与 OAuth 2.0 协议。你需要先在开放平台创建"网站应用"并通过审核,获得 AppID 和 AppSecret,并配置好授权回调域(必须是已备案的域名,本地开发需借助内网穿透)。
整个流程可以概括为:后端生成授权链接 → 前端展示二维码 → 用户扫码授权 → 微信回调你的接口并带上 code → 后端用 code 换取 access_token 与 openid → 获取用户信息并完成本地登录。
实现上有两种主流方式,你可以根据项目情况选择。
方式一:使用 WxJava SDK(推荐)
WxJava 是微信开发领域使用最广的 Java SDK,封装了 token 管理、API 调用等细节,能显著减少样板代码。
- 引入依赖
xml
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-mp</artifactId>
<version>4.6.0</version> <!-- 建议使用较新版本 -->
</dependency>
- 配置 WxMpService
java
@Configuration
public class WxConfig {
@Value("${wechat.appid}") private String appId;
@Value("${wechat.secret}") private String secret;
@Bean
public WxMpService wxMpService() {
WxMpService service = new WxMpServiceImpl();
WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage();
config.setAppId(appId);
config.setSecret(secret);
service.setWxMpConfigStorage(config);
return service;
}
}
- 生成二维码链接与处理回调
WxJava 提供了直接构建授权 URL 的方法,回调处理也更为简洁:
java
@RestController
@RequestMapping("/wechat")
public class WechatLoginController {
@Autowired private WxMpService wxMpService;
// 生成授权链接(前端渲染为二维码)
@GetMapping("/qrconnect")
public String qrConnect(@RequestParam String redirectUri,
@RequestParam String state) throws Exception {
// 确保 redirectUri 与开放平台配置的回调域一致
return wxMpService.getOAuth2Service()
.buildAuthorizationUrl(redirectUri, "snsapi_login", state);
}
// 微信回调接口
@GetMapping("/callback")
public String callback(@RequestParam("code") String code,
@RequestParam("state") String state) throws Exception {
// 1. 校验 state(防 CSRF,后文详述)
// 2. 用 code 换取 access_token 与 openid
WxOAuth2AccessToken accessToken = wxMpService.getOAuth2Service()
.getAccessToken(code);
String openId = accessToken.getOpenId();
// 3. 获取用户信息(可选)
WxOAuth2UserInfo userInfo = wxMpService.getOAuth2Service()
.getUserInfo(accessToken, null);
// 4. 执行本地登录/注册逻辑,返回登录态(如 JWT)
return "login_success";
}
}
方式二:手动调用 HTTP API
如果不希望引入额外依赖,可以用 RestTemplate 或 WebClient 直接调用微信接口,这样能更清楚地看到每一层细节。
- 构建授权 URL(与官方文档一致)
java
String url = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=" + appId +
"&redirect_uri=" + URLEncoder.encode(redirectUri, "UTF-8") +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=" + state +
"#wechat_redirect";
- 回调中用 code 换取 access_token
微信接口地址固定为 api.weixin.qq.com/sns/oauth2/... appid、secret、code 和 grant_type=authorization_code。
java
String tokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=" + appId +
"&secret=" + appSecret +
"&code=" + code +
"&grant_type=authorization_code";
ResponseEntity<String> resp = restTemplate.getForEntity(tokenUrl, String.class);
JSONObject json = JSON.parseObject(resp.getBody());
String accessToken = json.getString("access_token");
String openId = json.getString("openid");
- 获取用户信息(可选)
拿到 access_token 和 openid 后,调用 api.weixin.qq.com/sns/userinf... 可以获取昵称、头像等信息:
java
String userInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=" + accessToken +
"&openid=" + openId;
// 解析返回的 JSON,得到 nickname、headimgurl 等字段
两个关键的安全与设计要点
① 必须校验 state 参数
state 用于防止 CSRF 攻击。在生成授权链接前,服务端应生成一个随机值(如 UUID)并存入当前会话(Session 或 Redis);回调时,必须校验微信回传的 state 与会话中保存的值一致,否则拒绝登录。
② openid 是网站应用内的唯一标识
同一个用户在不同网站应用下的 openid 是不同的;但在同一个网站应用内,openid 是稳定且唯一的。本地用户表应以此字段作为微信登录的关联键,而不是依赖昵称或头像(它们可能被用户修改)。如果需要跨应用统一用户标识,则需使用 unionid(要求多个应用绑定在同一开放平台账号下)。
两种方式的选择
· 追求开发效率、项目已有 Spring 生态:选 WxJava。它帮你处理了 token 缓存、HTTP 错误、JSON 解析等细节,代码量少,维护成本低。 · 希望零外部依赖、需要完全掌控 HTTP 调用:选手动调用。适合对依赖敏感的项目,或需要深度定制请求/响应的场景。
无论哪种方式,微信官方的授权回调域必须提前配置且与实际回调地址严格匹配,这是接入时最常见的卡点。建议开发阶段先用内网穿透工具(如 ngrok)生成临时公网域名完成调试,上线前再切换到正式备案域名。