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.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Master Multi-Method Login in Spring Boot with Strategy & Factory Patterns

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.java

LoginStrategy 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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Design PatternsJavastrategy patternSpring BootloginFactory Pattern
Code Ape Tech Column
Written by

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

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.