分布式微服务系统架构第111集:spring三大特性,跨境电商、SaaS 平台、B2B 管理后台实现多语言国际化

加群联系作者vx:xiaoda0423

仓库地址:https://webvueblog.github.io/JavaPlusDoc/

https://1024bat.cn/

在 Java PC 网站中实现 微信扫码登录功能,一般包括以下几个核心步骤:


🧩 一、整体流程

  1. PC 页面点击“微信扫码登录”

  2. 后端生成一个带有 state 的二维码 URL,返回给前端展示

  3. 用户使用微信扫描二维码并授权登录

  4. 微信服务器回调你的服务(通过 redirect_uri)并返回 code

  5. 后端用 code 换取 access_token 和 openid

  6. 后端根据 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)
└── 安全控制(风控、设备校验、防暴力破解)

🔐 二、账号密码登录

✅ 接口流程

  1. 用户提交手机号/邮箱 + 密码

  2. 服务端验证账号密码

  3. 验证通过后生成登录 token(JWT / Session)

  4. 返回给前端保存(如保存在 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));
}

📩 三、验证码登录(短信/邮箱)

✅ 流程说明

  1. 用户输入手机号/邮箱请求验证码

  2. 后端生成验证码并通过短信/邮件发送

  3. 用户输入验证码登录

  4. 验证通过后注册或登录,生成 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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值