在设计多端登录系统时,你是否曾被错综复杂的if-else逻辑所困扰?随着业务发展,支持微信、手机号、用户名密码乃至支付宝等多种登录方式已成为常态。本文将深入探讨如何运用SpringBoot的依赖注入特性,结合工厂模式与策略模式,构建一个高扩展、易维护的统一登录架构,彻底告别“面条式”代码。
一、需求分析与模式选型
假设系统需支持三种主流登录方式:
- 用户名密码登录:需校验密码、账号状态。
- 微信扫码登录:需调用第三方API校验授权码。
- 手机验证码登录:需校验验证码与手机号。
传统的if-else实现方式将导致代码臃肿、难以扩展,且违反开闭原则。为此,我们引入两种经典设计模式:
- 策略模式:将每种登录算法封装成独立的策略类。
- 工厂模式:统一负责策略实例的创建与管理。
二、项目结构与核心接口定义
首先创建一个标准的Spring Boot项目。核心目录结构如下:
src/main/java/com/example/login
├── config
│ └── StrategyConfig.java
├── controller
│ └── LoginController.java
├── factory
│ └── LoginStrategyFactory.java
├── model
│ └── LoginRequest.java
├── service
│ ├── impl
│ │ ├── PasswordLoginStrategy.java
│ │ ├── WechatLoginStrategy.java
│ │ └── SmsLoginStrategy.java
│ └── LoginStrategy.java
└── Application.java
定义统一的策略接口,所有具体登录方式都将实现此接口:
public interface LoginStrategy {
// 返回登录类型标识,如 "password"
String getLoginType();
// 执行登录逻辑
String execute(Map<String, Object> params);
}
三、具体策略实现
1. 用户名密码登录策略
@Service
public class PasswordLoginStrategy implements LoginStrategy {
@Override
public String getLoginType() {
return "password";
}
@Override
public String execute(Map<String, Object> params) {
String username = (String) params.get("username");
String password = (String) params.get("password");
// 模拟密码校验(实际应从数据库查询并解密)
if (!"123456".equals(password)) {
throw new IllegalArgumentException("密码错误");
}
// 模拟检查账号状态
checkUserLocked(username);
return "登录成功(用户名密码)";
}
private void checkUserLocked(String username) {
System.out.println("检查用户" + username + "是否锁定");
}
}
2. 微信扫码登录策略
@Service
public class WechatLoginStrategy implements LoginStrategy {
@Override
public String getLoginType() {
return "wechat";
}
@Override
public String execute(Map<String, Object> params) {
String authCode = (String) params.get("authCode");
// 模拟调用微信开放平台API
String openId = callWechatApi(authCode);
String userId = getUserIdByOpenId(openId);
if (userId == null) {
throw new IllegalArgumentException("微信账号未绑定系统用户");
}
return "登录成功(微信扫码)";
}
private String callWechatApi(String authCode) {
System.out.println("调用微信接口,authCode=" + authCode);
return "wechat_open_id_123";
}
}
3. 手机验证码登录策略
@Service
public class SmsLoginStrategy implements LoginStrategy {
@Override
public String getLoginType() {
return "sms";
}
@Override
public String execute(Map<String, Object> params) {
String phone = (String) params.get("phone");
String code = (String) params.get("code");
// 模拟验证码校验(实际应从Redis获取)
if (!"666888".equals(code)) {
throw new IllegalArgumentException("验证码错误");
}
checkPhoneRegistered(phone);
return "登录成功(手机号验证码)";
}
private void checkPhoneRegistered(String phone) {
System.out.println("检查手机号" + phone + "是否注册");
}
}
四、工厂模式与Spring集成
1. 策略工厂类
工厂类的核心作用是建立登录类型标识与具体策略Bean的映射关系。
@Component
public class LoginStrategyFactory {
private final Map<String, LoginStrategy> strategyMap;
// 构造函数注入所有LoginStrategy Bean,并建立映射
public LoginStrategyFactory(Map<String, LoginStrategy> strategyMap) {
this.strategyMap = new HashMap<>();
strategyMap.forEach((beanName, strategy) ->
this.strategyMap.put(strategy.getLoginType(), strategy)
);
}
public LoginStrategy getStrategy(String loginType) {
LoginStrategy strategy = strategyMap.get(loginType);
if (strategy == null) {
throw new IllegalArgumentException("不支持的登录类型:" + loginType);
}
return strategy;
}
}
2. 控制器层实现
提供统一的登录入口,通过工厂获取对应策略执行。
@RestController
@RequestMapping("/login")
public class LoginController {
private final LoginStrategyFactory factory;
@Autowired
public LoginController(LoginStrategyFactory factory) {
this.factory = factory;
}
@PostMapping
public String login(@RequestBody LoginRequest request) {
String loginType = request.getLoginType();
Map<String, Object> params = request.getParams();
// 通过工厂获取策略并执行
LoginStrategy strategy = factory.getStrategy(loginType);
return strategy.execute(params);
}
}
请求体LoginRequest定义:
public class LoginRequest {
private String loginType; // 如 "password"
private Map<String, Object> params;
// getters and setters
}
五、测试与扩展
1. 测试请求示例
2. 轻松扩展新登录方式
当需要新增(例如)支付宝登录时,只需增加新的策略类,无需修改任何现有业务代码:
@Service
public class AlipayLoginStrategy implements LoginStrategy {
@Override
public String getLoginType() {
return "alipay";
}
@Override
public String execute(Map<String, Object> params) {
// 实现支付宝特有的登录逻辑
return "登录成功(支付宝)";
}
}
系统会自动识别并纳入此新策略。
六、架构优势与最佳实践
核心优势:
- 高扩展性:符合开闭原则,新增登录方式如同“热插拔”。
- 职责清晰:每种登录逻辑独立封装,代码可读性与可维护性极佳。
- 便于测试:各策略可独立进行单元测试。
最佳实践建议:
- 参数校验前置:在控制器层对公共参数进行统一校验。
- 公共逻辑抽取:将生成Token、记录日志等公共操作抽取到父类或工具类中。
- 统一异常处理:在策略中抛出自定义业务异常,在全局异常处理器中统一捕获并转换为友好提示。
- 配置化支持:将支持的登录类型列表置于配置文件中,便于动态管理。
总结
通过将工厂模式与策略模式相结合,并充分利用SpringBoot的IoC容器管理能力,我们成功构建了一个松耦合、易扩展的统一登录架构。面对未来可能出现的任何新登录方式,本架构都能从容应对,真正实现了让核心代码在需求万变中保持稳定与优雅。