Master Multi-Method Login in Spring Boot with Strategy & Factory Patterns
This guide walks you through designing a scalable login module that supports dozens of authentication methods by combining the Strategy and Factory patterns with Spring Boot, providing clean code, easy extensibility, and robust testing without the tangled if‑else spaghetti.
Requirement Analysis
When a product requires support for many login methods (e.g., username/password, WeChat QR code, SMS verification, and future additions like Alipay), the login logic quickly becomes tangled if implemented with a single LoginService and a series of if‑else checks.
Username/Password – validate encrypted password and account lock status.
WeChat QR Code – call WeChat Open Platform API and verify the auth code.
SMS Verification – generate a code, check its validity period and correctness.
Traditional implementation:
public String login(String loginType, Map<String, Object> params) {
if ("password".equals(loginType)) {
// username/password logic
} else if ("wechat".equals(loginType)) {
// wechat logic
} else if ("sms".equals(loginType)) {
// sms logic
} else {
throw new IllegalArgumentException("Unsupported login type");
}
}Problems of this approach:
Poor extensibility – every new method requires modifying the if‑else, violating the Open‑Closed Principle.
Mixed responsibilities – all logic lives in one class, making it hard to read.
Difficult reuse – shared steps (e.g., user validation) cannot be extracted cleanly.
Design Pattern Choice
Use the Strategy pattern to encapsulate each login method in its own class that implements a common LoginStrategy interface. Use the Factory pattern to create the appropriate strategy instance based on the loginType, keeping the caller unaware of concrete classes.
Spring Boot Project Setup
Project structure (src/main/java/com/example/login):
src/main/java/com/example/login
├── config
│ └── StrategyConfig.java // strategy bean configuration
├── controller
│ └── LoginController.java // REST endpoint
├── factory
│ └── LoginStrategyFactory.java // creates strategies
├── model
│ └── LoginRequest.java // request DTO
├── service
│ ├── impl
│ │ ├── PasswordLoginStrategy.java
│ │ ├── WechatLoginStrategy.java
│ │ └── SmsLoginStrategy.java
│ └── LoginStrategy.java // interface
└── Application.javaLoginStrategy Interface
public interface LoginStrategy {
// login type identifier, e.g., "password", "wechat"
String getLoginType();
// execute login, parameters are passed as a map
String execute(Map<String, Object> params);
}Strategy Implementations
1. Username/Password Strategy
@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");
// mock password check
if (!"123456".equals(password)) {
throw new IllegalArgumentException("密码错误");
}
checkUserLocked(username);
return "登录成功(用户名密码)";
}
private void checkUserLocked(String username) {
System.out.println("检查用户" + username + "是否锁定");
}
}2. WeChat QR Code Strategy
@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");
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. SMS Verification Strategy
@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");
if (!"666888".equals(code)) {
throw new IllegalArgumentException("验证码错误");
}
checkPhoneRegistered(phone);
return "登录成功(手机号验证码)";
}
private void checkPhoneRegistered(String phone) {
System.out.println("检查手机号" + phone + "是否注册");
}
}Factory Implementation
@Component
public class LoginStrategyFactory {
private final Map<String, LoginStrategy> strategyMap;
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;
}
}Controller Integration
LoginRequest DTO
public class LoginRequest {
private String loginType; // e.g., "password", "wechat"
private Map<String, Object> params; // method‑specific parameters
// getters & setters omitted for brevity
}LoginController
@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);
}
}Testing Requests
Username/Password:
{"loginType":"password","params":{"username":"user123","password":"123456"}}WeChat QR Code:
{"loginType":"wechat","params":{"authCode":"wechat_auth_code_456"}}SMS Verification:
{"loginType":"sms","params":{"phone":"13800138000","code":"666888"}}Extending with New Login Types
To add Alipay login, simply create a new strategy class that implements LoginStrategy and register it as a Spring bean; no existing code needs modification.
@Service("alipayStrategy")
public class AlipayLoginStrategy implements LoginStrategy {
@Override
public String getLoginType() { return "alipay"; }
@Override
public String execute(Map<String, Object> params) {
return "登录成功(支付宝)";
}
}Core Advantages
Strategy pattern – decouples each algorithm, improves readability.
Factory pattern – hides creation details, centralises instance management.
Spring Boot integration – automatic bean discovery, type‑safe injection, and lifecycle handling.
Best Practices
Validate loginType and required parameters in the controller before delegating to strategies.
Extract common logic (e.g., token generation) into an abstract base strategy if many methods share steps.
Add uniform logging and exception handling inside each strategy; wrap low‑level errors into business‑level exceptions.
Store supported login types in configuration files to avoid hard‑coding values in the front‑end.
Conclusion
Applying the Strategy and Factory patterns to a Spring Boot login module turns a tangled “spaghetti” implementation into a clean, extensible, and testable architecture that gracefully handles ever‑changing business requirements.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
