加群联系作者vx:xiaoda0423
仓库地址:https://webvueblog.github.io/JavaPlusDoc/
https://1024bat.cn/
在 Java PC 网站中实现 微信扫码登录功能,一般包括以下几个核心步骤:
🧩 一、整体流程
PC 页面点击“微信扫码登录”
后端生成一个带有
state
的二维码 URL,返回给前端展示用户使用微信扫描二维码并授权登录
微信服务器回调你的服务(通过 redirect_uri)并返回
code
后端用
code
换取access_token
和openid
后端根据
openid
查找或注册用户,建立登录态(如生成 JWT/token)
📦 二、准备工作
✅ 注册微信公众号平台账号
登录微信开放平台 申请网站应用,设置:
授权回调域名(如:
yourdomain.com
)获取 AppID 和 AppSecret
🔧 三、核心代码实现
1️⃣ 获取二维码链接
public String buildQrConnectUrl(String state) {
String appId = "yourAppId";
String redirectUri = URLEncoder.encode("https://yourdomain.com/wechat/callback", StandardCharsets.UTF_8);
return String.format(
"https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect",
appId, redirectUri, state
);
}
前端拿这个 URL 去渲染二维码(可用 JavaScript + qrcode.js)
2️⃣ 微信回调接口(code 换 token)
@GetMapping("/wechat/callback")
public void wechatCallback(@RequestParam String code, @RequestParam String state, HttpServletResponse response) {
String appId = "yourAppId";
String appSecret = "yourAppSecret";
// Step1: 获取 access_token
String tokenUrl = String.format(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appId, appSecret, code
);
String tokenResponse = restTemplate.getForObject(tokenUrl, String.class);
JSONObject json = JSON.parseObject(tokenResponse);
String accessToken = json.getString("access_token");
String openId = json.getString("openid");
// Step2: 获取用户信息(可选)
String userInfoUrl = String.format(
"https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s",
accessToken, openId
);
String userInfoResponse = restTemplate.getForObject(userInfoUrl, String.class);
JSONObject userInfo = JSON.parseObject(userInfoResponse);
// Step3: 业务逻辑(查找/注册用户,设置 session/JWT)
User user = userService.loginOrRegisterByOpenId(openId, userInfo);
// Step4: 登录成功,跳转回原页面 + token
response.sendRedirect("/login/success?token=" + jwtService.generateToken(user));
}
3️⃣ 前端页面轮询(或用 WebSocket)
扫码后微信授权完成,你的系统需要通知前端“扫码成功”。
实现方式:
使用
state
存 redis(标记为“登录中”)前端轮询
/check-login?state=xxx
登录完成后,状态标记为“已登录”,带上登录信息
3️⃣ 前端页面轮询(或用 WebSocket)
扫码后微信授权完成,你的系统需要通知前端“扫码成功”。
实现方式:
使用
state
存 redis(标记为“登录中”)前端轮询
/check-login?state=xxx
登录完成后,状态标记为“已登录”,带上登录信息
📌 四、安全建议
state
必须随机生成(防止 CSRF)微信授权接口调用频率有限,注意做容错处理
对微信返回信息签名校验(可选)
最终登录态建议使用 JWT + HttpOnly Cookie 管理
Spring Boot 实现微信扫码登录 的项目结构
🏗️ 项目结构概览
src/
└── main/
└── java/
└── com/example/wechatlogin/
├── controller/
│ └── WeChatLoginController.java
├── service/
│ ├── WeChatLoginService.java
│ └── UserService.java
├── dto/
│ └── WeChatUserInfo.java
└── util/
└── JwtUtil.java
🎯 1. Controller - 控制层
@RestController
@RequestMapping("/wechat")
public class WeChatLoginController {
@Autowired
private WeChatLoginService weChatLoginService;
@GetMapping("/qr-url")
public Map<String, String> getQrUrl() {
String state = UUID.randomUUID().toString();
String qrUrl = weChatLoginService.buildQrUrl(state);
// 可将 state 缓存到 Redis(未登录状态)
return Map.of("qrUrl", qrUrl, "state", state);
}
@GetMapping("/callback")
public void callback(@RequestParam String code, @RequestParam String state, HttpServletResponse response) throws IOException {
String token = weChatLoginService.handleCallback(code, state);
// 登录成功后跳转页面
response.sendRedirect("https://your-pc-site.com/login-success?token=" + token);
}
@GetMapping("/check-login")
public ResponseEntity<?> checkLogin(@RequestParam String state) {
// 查询是否登录成功
Optional<String> token = weChatLoginService.checkLoginStatus(state);
return token.map(t -> ResponseEntity.ok(Map.of("token", t)))
.orElse(ResponseEntity.status(202).build());
}
}
🧠 2. Service - 核心逻辑封装
@Service
public class WeChatLoginService {
private final String appId = "yourAppId";
private final String appSecret = "yourAppSecret";
@Autowired
private RestTemplate restTemplate;
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private RedisTemplate<String, String> redisTemplate;
public String buildQrUrl(String state) {
String redirectUri = URLEncoder.encode("https://yourdomain.com/wechat/callback", StandardCharsets.UTF_8);
return String.format(
"https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s#wechat_redirect",
appId, redirectUri, state
);
}
public String handleCallback(String code, String state) {
String tokenUrl = String.format(
"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
appId, appSecret, code
);
JSONObject tokenJson = JSON.parseObject(restTemplate.getForObject(tokenUrl, String.class));
String openId = tokenJson.getString("openid");
String accessToken = tokenJson.getString("access_token");
String userInfoUrl = String.format(
"https://api.weixin.qq.com/sns/userinfo?access_token=%s&openid=%s",
accessToken, openId
);
JSONObject userInfoJson = JSON.parseObject(restTemplate.getForObject(userInfoUrl, String.class));
WeChatUserInfo userInfo = userInfoJson.toJavaObject(WeChatUserInfo.class);
// 登录或注册
String userId = userService.loginOrRegister(openId, userInfo);
// 生成 token
String token = jwtUtil.generateToken(userId);
// 缓存 state-token 映射
redisTemplate.opsForValue().set("wechat_login:" + state, token, Duration.ofMinutes(2));
return token;
}
public Optional<String> checkLoginStatus(String state) {
String token = redisTemplate.opsForValue().get("wechat_login:" + state);
return Optional.ofNullable(token);
}
}
📄 3. DTO - 微信用户信息
@Data
public class WeChatUserInfo {
private String openid;
private String nickname;
private String sex;
private String province;
private String city;
private String headimgurl;
}
👤 4. UserService - 模拟用户注册/登录逻辑
@Service
public class UserService {
// 示例:真实场景中应连接数据库
private final Map<String, String> fakeDb = new ConcurrentHashMap<>();
public String loginOrRegister(String openId, WeChatUserInfo userInfo) {
return fakeDb.computeIfAbsent(openId, k -> UUID.randomUUID().toString());
}
}
🔐 5. JwtUtil - 简单 JWT 工具类(可替换为 jjwt)
@Component
public class JwtUtil {
private final String secretKey = "yourSecretKey";
public String generateToken(String userId) {
return Jwts.builder()
.setSubject(userId)
.setIssuedAt(new Date())
.setExpiration(Date.from(Instant.now().plus(Duration.ofHours(2))))
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
}
⚙️ 6. 配置推荐
spring:
redis:
host: localhost
port: 6379
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
🔹 一、用户相关业务
功能 | 描述 |
---|---|
用户注册/登录 | 支持密码、验证码、第三方登录(如微信、支付宝、GitHub) |
用户认证与授权 | JWT、OAuth2、Spring Security |
密码找回 / 修改 | 邮箱/短信验证码验证流程 |
用户信息管理 | 头像、昵称、手机号、邮箱等 |
🔹 二、基础增删改查(CRUD)
模块 | 示例 |
---|---|
商品管理 | 商品列表、上下架、库存管理 |
订单系统 | 下单、支付、退款、订单跟踪 |
文章管理 | 发布文章、评论、点赞、浏览量统计 |
🔹 三、常见业务模块
功能 | 描述 |
---|---|
文件上传 | 支持本地、OSS(如阿里云、MinIO) |
数据导入导出 | Excel 导入导出(EasyExcel、POI) |
富文本编辑 | 支持富文本上传(如:富文本 + 图片 + Markdown) |
多语言国际化 | 适用于跨境电商、全球化 SaaS |
🔹 四、高级功能
模块 | 描述 |
---|---|
缓存系统 | Redis 实现热点缓存、缓存穿透/雪崩/击穿防护 |
消息队列 | Kafka/RabbitMQ 用于异步下单、延迟任务、削峰 |
定时任务 | Spring Scheduled / XXL-JOB 实现定时推送、对账 |
搜索系统 | Elasticsearch 用于商品搜索、全文索引 |
日志追踪 | 接入链路追踪(如 Sleuth + Zipkin)、用户行为埋点 |
实时通知 | WebSocket 实现 IM 消息通知 / 实时告警 |
异常报警 | 钉钉/飞书 webhook 报警,Sentinel 限流报警等 |
🔹 五、微服务 & 分布式相关
模块 | 描述 |
---|---|
服务注册与发现 | Spring Cloud Eureka / Nacos |
网关鉴权 | Spring Cloud Gateway / Nginx |
分布式锁 | Redisson 实现分布式事务/库存扣减锁 |
分布式事务 | Seata / TCC |
配置中心 | Apollo / Nacos 动态配置刷新 |
服务监控 | Prometheus + Grafana 监控系统状态 |
链路追踪 | Zipkin / SkyWalking / Jaeger |
🔹 六、常见系统功能案例
模块 | 特性 |
---|---|
秒杀系统 | 限流、抢购、库存原子扣减、异步下单 |
高性能下单系统 | Redis + Kafka + DB 双写,支持限流/幂等 |
权限管理系统 | RBAC、组织架构、权限树构建 |
后台管理系统 | 菜单、按钮权限控制,动态路由 |
数据大屏 | 接口聚合统计、实时数据刷新 |
支付集成 | 微信/支付宝/PayPal 支付回调、签名验证 |
🔹 七、三方平台对接示例
接口集成 | 说明 |
---|---|
微信公众号 / 小程序 | 登录、模板消息、扫码授权 |
支付宝开放平台 | 登录、支付、订单同步 |
GitHub / Gitee | OAuth2 登录 |
快递物流接口 | 快递鸟、顺丰 API 查询 |
第三方短信 | 阿里云短信、腾讯云短信 |
地图服务 | 高德/百度地图地址解析、打点展示 |
完整的用户注册/登录系统,支持 密码、验证码、第三方登录(微信、支付宝、GitHub) ,这也是常见的用户认证体系。
🧩 一、模块化用户登录功能结构
用户认证模块
├── 账号密码登录(传统登录)
├── 验证码登录(短信、邮箱验证码)
├── 第三方登录
│ ├── 微信登录(扫码、公众号、小程序)
│ ├── 支付宝登录(扫码/授权)
│ └── GitHub 登录(OAuth2)
├── 注册流程(支持邀请码)
├── 用户资料补充 / 修改
├── 登录态管理(JWT / Token / Session)
└── 安全控制(风控、设备校验、防暴力破解)
🔐 二、账号密码登录
✅ 接口流程
用户提交手机号/邮箱 + 密码
服务端验证账号密码
验证通过后生成登录 token(JWT / Session)
返回给前端保存(如保存在 Cookie)
🔧 示例代码
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
User user = userService.findByUsername(request.getUsername());
if (user == null || !passwordEncoder.matches(request.getPassword(), user.getPassword())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("账号或密码错误");
}
String token = jwtUtil.generateToken(user.getId());
return ResponseEntity.ok(Map.of("token", token));
}
📩 三、验证码登录(短信/邮箱)
✅ 流程说明
用户输入手机号/邮箱请求验证码
后端生成验证码并通过短信/邮件发送
用户输入验证码登录
验证通过后注册或登录,生成 token
🔧 核心点
验证码缓存:使用 Redis 缓存验证码,有效期如 5 分钟
发送接口:接入阿里云短信、腾讯云短信、SendGrid、SMTP 邮件等
🌐 四、第三方登录(OAuth2)
平台 | 关键步骤 |
---|---|
微信 | 获取二维码 -> 用户扫码授权 -> 获取 code -> 换 access_token -> 获取 openid/userinfo |
支付宝 | 同上 |
GitHub | GitHub 授权页 -> 获取 code -> 换 access_token -> 拉用户信息 |
🔧 示例:GitHub OAuth2 登录流程
@GetMapping("/github/callback")
public void githubCallback(@RequestParam String code, HttpServletResponse response) throws IOException {
// 用 code 换取 access_token
String accessToken = githubService.getAccessToken(code);
// 用 access_token 获取用户信息
GitHubUser user = githubService.getUserInfo(accessToken);
// 登录或注册用户
String userId = userService.loginOrRegisterByGitHub(user);
// 返回 JWT
String token = jwtUtil.generateToken(userId);
response.sendRedirect("/login-success?token=" + token);
}
🧾 五、注册接口建议
支持:
账号密码注册
验证码注册
第三方授权后补全资料注册
可以加入:
图形验证码(防刷)
邀请码 / 渠道来源标记
注册来源追踪(Web、H5、小程序)
🧰 六、技术栈推荐
组件 | 推荐 |
---|---|
框架 | Spring Boot + Spring Security |
密码加密 | BCryptPasswordEncoder |
Token | JWT(jjwt)或 Spring Security OAuth2 |
数据库 | MySQL + MyBatis Plus |
验证码 | Redis + 自定义验证码组件 |
邮件 | Spring Mail |
第三方登录 | 手动集成 OAuth2(或用 Spring Social / OAuth2 Client) |