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.

LuTiao Programming
LuTiao Programming
LuTiao Programming
Tame Messy Login Logic: 3‑Step Spring Boot Factory & Strategy Pattern for Multi‑Channel Auth

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

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

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 BootauthenticationloginFactory Pattern
LuTiao Programming
Written by

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.

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.