Tame Messy Login Logic: 3‑Step Spring Boot Factory & Strategy Pattern for Multi‑Channel Auth
The article shows how tangled if‑else login code caused by adding password, SMS, WeChat, and Alipay authentication can be refactored using the Strategy and Factory patterns in Spring Boot, resulting in a clean, extensible, plug‑in‑style login module that requires no changes to existing code when new methods are added.
Problem Overview
In medium‑to‑large systems the login module often starts with a simple username‑password flow and later accumulates SMS, WeChat, Alipay, etc. The service class becomes a tangled if‑else chain, requiring modifications in many places (e.g., 17 spots for an Alipay addition) and risking missed validations.
Login, a high‑frequency change module, must be inherently extensible rather than maintained by courage.
Design Goal
New login methods should be added by writing new code only, without touching existing logic.
New login method = new code, not modification of old logic.
Solution Architecture
Combine Strategy pattern (encapsulate each login type as an independent strategy) with Factory pattern (select the appropriate strategy based on loginType). The controller interacts with a single interface and never knows the concrete implementations.
Controller always faces one interface, oblivious to implementation details.
Project Structure
src/main/java/com/icoderoad/login<br/>├── config/StrategyConfig.java<br/>├── controller/LoginController.java<br/>├── factory/LoginStrategyFactory.java<br/>├── model/LoginRequest.java<br/>├── service/LoginStrategy.java<br/>├── service/impl/PasswordLoginStrategy.java<br/>├── service/impl/WechatLoginStrategy.java<br/>├── service/impl/SmsLoginStrategy.java<br/>└── LoginApplication.javaCore Abstraction
package com.icoderoad.login.service;
import java.util.Map;
public interface LoginStrategy {
/** Return login type identifier, e.g., "password", "wechat", "sms" */
String getLoginType();
/** Execute the login logic */
String execute(Map<String, Object> params);
}Strategy Implementations
Username‑Password Strategy
package com.icoderoad.login.service.impl;
import com.icoderoad.login.service.LoginStrategy;
import org.springframework.stereotype.Service;
import java.util.Map;
@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("密码错误");
}
checkUserStatus(username);
return "登录成功(账号密码)";
}
private void checkUserStatus(String username) {
System.out.println("校验账号状态:" + username);
}
}WeChat QR Code Strategy
package com.icoderoad.login.service.impl;
import com.icoderoad.login.service.LoginStrategy;
import org.springframework.stereotype.Service;
import java.util.Map;
@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 = findUserByOpenId(openId);
if (userId == null) {
throw new IllegalArgumentException("微信账号未绑定用户");
}
return "登录成功(微信)";
}
private String callWechatApi(String authCode) {
System.out.println("调用微信接口:" + authCode);
return "wx_open_id_xxx";
}
private String findUserByOpenId(String openId) {
return "user_001";
}
}SMS Verification Strategy
package com.icoderoad.login.service.impl;
import com.icoderoad.login.service.LoginStrategy;
import org.springframework.stereotype.Service;
import java.util.Map;
@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("验证码错误");
}
return "登录成功(短信)";
}
}Factory – Strategy Dispatcher
package com.icoderoad.login.factory;
import com.icoderoad.login.service.LoginStrategy;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@Component
public class LoginStrategyFactory {
private final Map<String, LoginStrategy> strategyMap = new HashMap<>();
public LoginStrategyFactory(Map<String, LoginStrategy> strategies) {
strategies.forEach((beanName, strategy) ->
strategyMap.put(strategy.getLoginType(), strategy));
}
public LoginStrategy getStrategy(String loginType) {
LoginStrategy strategy = strategyMap.get(loginType);
if (strategy == null) {
throw new IllegalArgumentException("不支持的登录方式:" + loginType);
}
return strategy;
}
}Key points of the design: Spring automatically collects all LoginStrategy beans. The factory only retrieves an instance by type. New strategies become effective without modifying old code.
Controller – Unified Entry Point
package com.icoderoad.login.controller;
import com.icoderoad.login.factory.LoginStrategyFactory;
import com.icoderoad.login.model.LoginRequest;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/login")
public class LoginController {
private final LoginStrategyFactory factory;
public LoginController(LoginStrategyFactory factory) { this.factory = factory; }
@PostMapping
public String login(@RequestBody LoginRequest request) {
LoginStrategy strategy = factory.getStrategy(request.getLoginType());
return strategy.execute(request.getParams());
}
}Adding a New Login Method (Alipay Example)
package com.icoderoad.login.service.impl;
import com.icoderoad.login.service.LoginStrategy;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class AlipayLoginStrategy implements LoginStrategy {
@Override
public String getLoginType() { return "alipay"; }
@Override
public String execute(Map<String, Object> params) {
return "登录成功(支付宝)";
}
}The controller, factory, and existing strategies remain untouched.
Best‑Practice Add‑Ons
Move parameter validation to the controller layer.
Extract common logic into an abstract strategy if needed.
Apply unified exception handling and logging for all login attempts.
Expose configurable login types to the front‑end via configuration.
Conclusion
By delegating variability to the Strategy pattern and selection to the Factory pattern, Spring Boot assembles a plug‑in‑style login module that remains stable as requirements evolve. The design eliminates the need to modify existing code when adding new login methods, thereby reducing risk and maintenance cost.
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.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
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.
