Eliminate Messy Login Logic: Unified Multi‑Channel Authentication with Spring Boot Factory & Strategy Patterns

This article demonstrates how to replace tangled if‑else login code with a clean Spring Boot solution that combines the Factory and Strategy patterns, enabling easy addition of multiple authentication methods such as password, WeChat, SMS, and future providers while improving extensibility, readability, and testability.

Java Companion
Java Companion
Java Companion
Eliminate Messy Login Logic: Unified Multi‑Channel Authentication with Spring Boot Factory & Strategy Patterns

Requirement Analysis: Supporting Multiple Login Methods

The product requests support for ten login methods. The author starts with a typical scenario that includes three methods: username/password, WeChat QR‑code, and SMS verification.

Username/password login : needs password encryption verification and account lock checking.

WeChat QR‑code login : requires calling the WeChat Open Platform API and validating the auth code.

SMS verification login : requires generating a code, checking its validity period and correctness.

Traditional implementation uses a single LoginService with nested if‑else statements to distinguish login types, leading to poor extensibility, tangled responsibilities, and difficulty reusing common logic.

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");
    }
}

Design Pattern Selection

The author chooses the Strategy pattern to decouple each login algorithm and the Factory pattern to create the appropriate strategy instance, keeping the caller unaware of concrete implementations.

Spring Boot Project Setup

Project structure:

src/main/java/com/example/login
├── config
│   └── StrategyConfig.java   // strategy bean configuration
├── controller
│   └── LoginController.java // login controller
├── factory
│   └── LoginStrategyFactory.java // factory for strategies
├── model
│   └── LoginRequest.java    // request DTO
├── service
│   ├── impl
│   │   ├── PasswordLoginStrategy.java // password strategy
│   │   ├── WechatLoginStrategy.java   // wechat strategy
│   │   └── SmsLoginStrategy.java      // sms strategy
│   └── LoginStrategy.java            // strategy interface
└── Application.java

Unified Login Strategy Interface

public interface LoginStrategy {
    // login type identifier, e.g., "password", "wechat"
    String getLoginType();

    // login method, parameters passed via 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");
        // simulate 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

@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 the Three Login Types

Username/Password Request

{
  "loginType": "password",
  "params": {
    "username": "user123",
    "password": "123456"
  }
}

WeChat QR‑Code Request

{
  "loginType": "wechat",
  "params": {
    "authCode": "wechat_auth_code_456"
  }
}

Adding a New Method (Alipay)

To support a new provider, only a new strategy class is needed; existing code remains untouched.

@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 login algorithm, improving readability.

Factory pattern : encapsulates object creation, adhering to the Open‑Closed Principle.

Spring Boot integration : automatic bean registration via @Service and injection of Map<String, LoginStrategy> simplifies management.

Type safety : the factory validates login types at runtime, preventing NullPointerException.

Best Practices

Validate loginType and params in the controller before delegating to strategies.

Extract common logic (e.g., token generation) into an abstract base strategy if multiple strategies share it.

Add logging and unified exception handling inside each strategy to produce friendly error messages.

Store supported login types in configuration files to avoid hard‑coding values in the front‑end.

Conclusion

By replacing the monolithic if‑else login service with a combination of Factory and Strategy patterns, the login module becomes modular, extensible, and easy to test. New authentication methods can be added as plug‑in strategies without touching existing code, keeping the codebase elegant in the face of changing requirements.

backendJavastrategy patternSpring BootAuthenticationFactory Pattern
Java Companion
Written by

Java Companion

A highly professional Java public account

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.