微信扫码登录的核心流程是:
- 前端展示微信登录二维码(来自后端生成的 URL)。
- 用户用微信扫描二维码,在微信客户端授权。
- 微信服务器回调后端接口,携带
code。 - 后端用
code换取access_token和用户信息。 - 后端将用户信息与系统账号绑定(或自动注册),生成自己的登录态(如 JWT Token)。
- 前端轮询或 WebSocket 等方式得知登录成功,拿到 Token,完成登录。
一、准备工作(微信开放平台)
-
注册微信开放平台账号
-
创建网站应用
- 填写网站域名(如
www.yoursite.com) - 获取 AppID 和 AppSecret
- 填写网站域名(如
-
配置授权回调域名
在开放平台后台设置 授权回调域名(不带
http://或https://),例如www.yoursite.com。
二、整体流程设计
┌──────────────┐ ┌──────────────┐
│ 前端 Vue │ │ Spring Boot │
└─────┬────────┘ └─────┬────────┘
│ │
│ 1.请求微信登录URL │
│───────────────────────>│
│ │ 2.生成state,构造微信授权URL
│<───────────────────────│
│ 3.展示二维码(微信URL) │
│ │
│ 4.用户扫码授权 │
│ │
│ │ 5.微信回调 /wechat/callback?code=xxx&state=yyy
│ │ 6.用code换access_token和用户信息
│ │ 7.查找或创建系统用户,生成JWT Token
│ │ 8.返回登录成功页面(带Token)
│ 9.前端拿到Token,登录成功│
三、后端 Spring Boot 实现
1. 添加依赖(pom.xml)
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- HTTP Client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- JSON解析 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.40</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2. 配置文件 application.yml
wechat:
appid: your_appid
secret: your_secret
redirect-uri: https://www.yoursite.com/wechat/callback
scope: snsapi_login
state: random_state_string
3. 微信配置类
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WeChatConfig {
private String appid;
private String secret;
private String redirectUri;
private String scope;
private String state;
}
4. 微信服务类(核心逻辑)
@Service
public class WeChatService {
@Autowired
private WeChatConfig weChatConfig;
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token";
private static final String USER_INFO_URL = "https://api.weixin.qq.com/sns/userinfo";
/**
* 生成微信授权URL
*/
public String getWeChatAuthUrl() {
return "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=" + weChatConfig.getAppid() +
"&redirect_uri=" + URLEncoder.encode(weChatConfig.getRedirectUri(), StandardCharsets.UTF_8) +
"&response_type=code" +
"&scope=" + weChatConfig.getScope() +
"&state=" + weChatConfig.getState();
}
/**
* 用 code 换 access_token
*/
public JSONObject getAccessToken(String code) throws Exception {
String url = ACCESS_TOKEN_URL +
"?appid=" + weChatConfig.getAppid() +
"&secret=" + weChatConfig.getSecret() +
"&code=" + code +
"&grant_type=authorization_code";
String result = httpGet(url);
return JSON.parseObject(result);
}
/**
* 用 access_token 和 openid 获取用户信息
*/
public JSONObject getUserInfo(String accessToken, String openid) throws Exception {
String url = USER_INFO_URL +
"?access_token=" + accessToken +
"&openid=" + openid;
String result = httpGet(url);
return JSON.parseObject(result);
}
private String httpGet(String url) throws Exception {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
try (CloseableHttpResponse response = client.execute(httpGet)) {
return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
}
}
}
5. 控制器
@RestController
@RequestMapping("/wechat")
public class WeChatController {
@Autowired
private WeChatService weChatService;
/**
* 第一步:前端请求微信登录URL
*/
@GetMapping("/login-url")
public Map<String, String> getLoginUrl() {
String authUrl = weChatService.getWeChatAuthUrl();
return Collections.singletonMap("url", authUrl);
}
/**
* 第二步:微信回调
*/
@GetMapping("/callback")
public void callback(@RequestParam String code,
@RequestParam String state,
HttpServletResponse response) throws Exception {
// 1. 用 code 换 token
JSONObject tokenJson = weChatService.getAccessToken(code);
String accessToken = tokenJson.getString("access_token");
String openid = tokenJson.getString("openid");
// 2. 获取用户信息
JSONObject userInfo = weChatService.getUserInfo(accessToken, openid);
String nickname = userInfo.getString("nickname");
String headimgurl = userInfo.getString("headimgurl");
// 3. 根据 openid 查找或创建系统用户(这里省略具体业务逻辑)
User user = userService.findOrCreateByWeChat(openid, nickname, headimgurl);
// 4. 生成系统 Token(JWT)
String token = JwtUtil.generateToken(user.getId());
// 5. 重定向到前端登录成功页面,带上 token
response.sendRedirect("https://www.yoursite.com/login-success?token=" + token);
}
}
6. JWT 工具类(示例)
public class JwtUtil {
private static final String SECRET = "your_jwt_secret";
private static final long EXPIRATION = 86400000; // 1天
public static String generateToken(Long userId) {
return Jwts.builder()
.setSubject(userId.toString())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public static Long parseUserId(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
return Long.valueOf(claims.getSubject());
}
}
四、前端 Vue 实现
1. 安装 axios
npm install axios
2. 登录页面组件
<template>
<div>
<h2>微信扫码登录</h2>
<img :src="qrCodeUrl" alt="微信登录二维码" v-if="qrCodeUrl" />
<p v-else>加载中...</p>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
qrCodeUrl: ''
};
},
mounted() {
this.getQrCode();
},
methods: {
async getQrCode() {
const res = await axios.get('/api/wechat/login-url');
// 这里可以用 qrcode.vue 等库把 URL 转成二维码图片
this.qrCodeUrl = res.data.url;
}
}
};
</script>
3. 登录成功页面
<template>
<div>
<h2>登录成功</h2>
<p>欢迎 {{ user.nickname }}</p>
<img :src="user.headimgurl" alt="头像" />
</div>
</template>
<script>
export default {
data() {
return {
user: {}
};
},
created() {
// 从 URL 参数获取 token
const token = this.$route.query.token;
if (token) {
localStorage.setItem('token', token);
// 调用接口获取用户信息
this.fetchUser();
}
},
methods: {
async fetchUser() {
const res = await axios.get('/api/user/info', {
headers: { Authorization: 'Bearer ' + localStorage.getItem('token') }
});
this.user = res.data;
}
}
};
</script>
五、安全与优化建议
-
State 防 CSRF
生成随机 state 并存入 session/redis,回调时校验,防止跨站请求伪造。
-
Token 安全
- 使用 HTTPS
- 设置合理的过期时间
- 刷新 Token 机制
-
用户绑定
- 微信 openid 与系统账号绑定
- 支持手机号/密码等其他方式注册绑定
-
前端轮询备选
如果不用重定向,可以让前端轮询
/api/wechat/check-login?state=xxx判断登录状态。 -
日志与异常处理
记录微信接口调用日志,处理 access_token 失效、用户拒绝授权等情况。
六、总结
- Spring Boot 负责:生成微信授权 URL、处理回调、换取 token 和用户信息、生成本系统登录态。
- Vue 负责:展示二维码、跳转微信授权、接收登录成功后的 Token、完成前端登录态保存。
- 关键点:微信开放平台配置、回调域名、state 校验、Token 管理、用户绑定逻辑。