7.6 KiB
7.6 KiB
ADR-003-02: Salesforce 策略模式登录架构决策
元数据
- ADR 编号:ADR-003-02
- 需求编号:2026-01-21-003-02
- 创建时间:2026-01-22
- 状态:已采纳
- 决策人:SSOT 架构师
背景和问题陈述
背景
Salesforce 多系统认证功能需要支持多种登录方式,包括 OAuth2 Password 登录、OAuth2 Client Credentials 登录、OAuth2 Authorization Code 登录、Session ID 登录。用户需要能够通过页面选择登录策略,系统需要支持扩展新的登录策略。
问题
- 如何实现多种登录方式的灵活切换?
- 如何支持用户选择登录策略?
- 如何支持扩展新的登录策略?
- 如何管理登录策略的依赖注入?
决策驱动因素
技术因素
- 扩展性:需要支持未来新增登录策略
- 可维护性:代码结构清晰,易于维护
- 灵活性:支持用户选择登录策略
- 解耦性:登录策略与业务逻辑解耦
业务因素
- 用户体验:用户可以方便地选择登录策略
- 功能完整:支持 OAuth2 Password、OAuth2 Client Credentials、OAuth2 Authorization Code、Session ID 四种登录方式
- 未来扩展:预留接口支持新的登录策略
非功能因素
- 性能:登录响应时间 < 3 秒
- 安全:敏感信息加密存储,HTTPS 传输
- 可用性:系统可用性 99.9%
考虑的选项
选项 1:使用策略模式 + 工厂方法模式
描述
使用策略模式实现不同登录策略,使用工厂方法模式进行依赖注入。
优点
- 符合开闭原则,对扩展开放,对修改关闭
- 策略之间相互独立,互不影响
- 工厂方法模式管理策略实例,便于依赖注入
- 代码结构清晰,易于理解和维护
- 支持运行时动态选择策略
缺点
- 需要定义策略接口和多个策略实现
- 增加了类的数量
- 需要额外的工厂类
影响
- 代码复杂度:中等
- 性能影响:低
- 可维护性:高
- 扩展性:高
选项 2:使用 if-else 条件判断
描述
使用 if-else 条件判断不同的登录类型,执行对应的登录逻辑。
优点
- 实现简单,代码量少
- 不需要额外的类和接口
缺点
- 违反开闭原则,新增登录策略需要修改现有代码
- 代码耦合度高,难以维护
- 不支持运行时动态选择策略
- 代码可读性差,if-else 嵌套多
影响
- 代码复杂度:低
- 性能影响:低
- 可维护性:低
- 扩展性:低
选项 3:使用状态模式
描述
使用状态模式管理登录状态,不同状态对应不同的登录策略。
优点
- 状态转换清晰
- 支持状态相关的行为
缺点
- 状态模式适用于状态转换场景,不适合登录策略选择
- 增加了不必要的复杂性
- 不符合业务场景
影响
- 代码复杂度:高
- 性能影响:低
- 可维护性:中
- 扩展性:中
决策结果
选择的方案
选项 1:使用策略模式 + 工厂方法模式
决策理由
- 扩展性:策略模式支持新增登录策略,符合开闭原则
- 可维护性:代码结构清晰,策略之间相互独立
- 灵活性:工厂方法模式管理策略实例,支持运行时动态选择
- 解耦性:登录策略与业务逻辑解耦
- 符合业务场景:用户选择登录策略的场景适合使用策略模式
拒绝其他方案的理由
- 拒绝选项 2:if-else 条件判断违反开闭原则,不利于扩展
- 拒绝选项 3:状态模式不适合登录策略选择的场景
决策细节
策略接口设计
public interface LoginStrategy {
LoginResult login(LoginRequest request) throws LoginException;
LoginResult refreshToken(RefreshTokenRequest request) throws RefreshTokenException;
void logout(LogoutRequest request) throws LoginException;
String getLoginType();
boolean supportRefreshToken();
}
策略实现
- OAuth2LoginStrategy:实现 OAuth2 登录策略
- SessionIdLoginStrategy:实现 Session ID 登录策略
- 预留接口:支持扩展新的登录策略
工厂方法设计
@Component
public class LoginStrategyFactory {
private final Map<String, LoginStrategy> strategyMap;
@Autowired
public LoginStrategyFactory(List<LoginStrategy> strategies) {
this.strategyMap = strategies.stream()
.collect(Collectors.toMap(LoginStrategy::getLoginType, Function.identity()));
}
public LoginStrategy getStrategy(String loginType) {
LoginStrategy strategy = strategyMap.get(loginType);
if (strategy == null) {
throw new IllegalArgumentException("不支持的登录类型: " + loginType);
}
return strategy;
}
public List<String> getSupportedLoginTypes() {
return new ArrayList<>(strategyMap.keySet());
}
public void registerStrategy(String loginType, LoginStrategy strategy) {
strategyMap.put(loginType, strategy);
}
}
依赖注入方式
使用 Spring 的依赖注入机制,通过 @Autowired 注入 List<LoginStrategy>,工厂类自动收集所有策略实现。
后果
正面后果
- 扩展性:新增登录策略只需实现 LoginStrategy 接口,无需修改现有代码
- 可维护性:策略之间相互独立,易于维护和测试
- 灵活性:工厂方法模式支持运行时动态选择策略
- 解耦性:登录策略与业务逻辑解耦,降低耦合度
- 代码质量:符合设计模式最佳实践,代码结构清晰
负面后果
- 类数量增加:需要定义策略接口、多个策略实现和工厂类
- 学习成本:需要理解策略模式和工厂方法模式
- 初始化复杂度:工厂类需要收集和管理所有策略实例
风险
- 策略实例管理:需要确保策略实例正确初始化和管理
- 异常处理:需要统一处理策略执行过程中的异常
- 性能考虑:频繁创建策略实例可能影响性能
实施计划
阶段 1:策略接口和工厂实现
- 定义 LoginStrategy 接口
- 实现 LoginStrategyFactory 工厂类
- 配置 Spring 依赖注入
阶段 2:OAuth2 登录策略实现
- 实现 OAuth2LoginStrategy 类
- 实现登录、刷新令牌、登出方法
- 集成 Salesforce OAuth2 API
阶段 3:Session ID 登录策略实现
- 实现 SessionIdLoginStrategy 类
- 实现登录、登出方法
- 集成 Salesforce Identity API
阶段 4:服务层和控制器实现
- 实现 ISalesforceAuthService 接口
- 实现 SalesforceAuthServiceImpl 服务类
- 实现 SalesforceAuthController 控制器类
阶段 5:测试和优化
- 编写单元测试
- 编写集成测试
- 性能优化
监控和评估
监控指标
- 登录成功率:监控登录成功率,目标 ≥ 95%
- 登录响应时间:监控登录响应时间,目标 < 3 秒
- 异常率:监控登录异常率,目标 < 5%
评估周期
- 短期评估:上线后 1 周内评估
- 中期评估:上线后 1 个月内评估
- 长期评估:上线后 3 个月内评估
评估标准
- 功能完整性:所有功能点是否实现
- 性能指标:是否达到性能目标
- 用户体验:用户反馈是否良好
- 扩展性:是否易于扩展新的登录策略
相关决策
- ADR-003-01: Salesforce 多系统配置架构决策