策略模式(Strategy Pattern)是一种行为设计模式,用于定义一系列算法,让它们可以相互替换,并且算法的变化不会影响使用算法的客户端。在身份认证系统中,策略模式常用于支持多种认证方式(如JWT、Basic Auth),以实现灵活性和可扩展性。
设计模式原理
策略模式通过将变化的算法部分提取到独立的策略类中,由上下文类动态选择和执行具体策略。这样可以实现:
- 单一职责:每个策略类只负责一种算法实现,上下文负责协调。
- 灵活性:支持运行时切换算法,无需修改上下文代码。
- 开闭原则:对扩展开放(新增策略),对修改关闭(上下文不变)。
在 TypeScript 中,策略模式通过接口和多态实现,确保类型安全和代码清晰。
策略模式的结构
策略模式包含以下角色:
- Strategy(策略接口) :定义算法的公共接口,如
authenticate()
。 - ConcreteStrategy(具体策略) :实现策略接口,提供具体算法。
- Context(上下文) :维护策略对象的引用,通过接口调用算法。
优点
- 解耦合:算法与上下文分离,降低耦合度。
- 可扩展性:新增算法只需添加新策略类。
- 类型安全:TypeScript 的类型系统确保接口一致性。
身份认证中的策略模式
在身份认证系统中(如 Passport.js),策略模式用于支持多种认证方式。例如:
- JWT认证:通过验证JSON Web Token来确认用户身份。
- Basic Auth:通过用户名和密码进行认证。
- OAuth:通过第三方服务(如Google、GitHub)进行认证。
以下以身份认证为例,展示策略模式如何支持动态选择认证方式,类似 Passport.js 的机制。
TypeScript 实现示例:身份认证中的策略模式
以下是一个身份认证系统的实现,动态选择认证策略(如JWT、Basic Auth)来验证用户请求。
typescript
// 策略接口
interface AuthStrategy {
authenticate(credentials: any): Promise<boolean>;
getUserInfo(): string;
}
// 具体策略:JWT 认证
class JwtStrategy implements AuthStrategy {
async authenticate(credentials: { token: string }): Promise<boolean> {
// 模拟 JWT 验证逻辑
if (credentials.token.startsWith("jwt_")) {
return true; // 假设 token 以 "jwt_" 开头表示有效
}
throw new Error("无效的 JWT Token");
}
getUserInfo(): string {
return "JWT 用户: user@example.com";
}
}
// 具体策略:Basic Auth 认证
class BasicAuthStrategy implements AuthStrategy {
async authenticate(credentials: { username: string; password: string }): Promise<boolean> {
// 模拟 Basic Auth 验证逻辑
if (credentials.username === "admin" && credentials.password === "pass123") {
return true;
}
throw new Error("无效的用户名或密码");
}
getUserInfo(): string {
return "Basic Auth 用户: admin";
}
}
// 具体策略:OAuth 认证
class OAuthStrategy implements AuthStrategy {
async authenticate(credentials: { accessToken: string }): Promise<boolean> {
// 模拟 OAuth 验证逻辑
if (credentials.accessToken.startsWith("oauth_")) {
return true; // 假设 accessToken 以 "oauth_" 开头表示有效
}
throw new Error("无效的 OAuth Access Token");
}
getUserInfo(): string {
return "OAuth 用户: oauth_user@example.com";
}
}
// 上下文类:认证上下文
class AuthContext {
private strategy: AuthStrategy;
constructor(strategy: AuthStrategy) {
this.strategy = strategy;
}
setStrategy(strategy: AuthStrategy): void {
this.strategy = strategy;
}
async authenticate(credentials: any): Promise<string> {
try {
const isAuthenticated = await this.strategy.authenticate(credentials);
if (isAuthenticated) {
return this.strategy.getUserInfo();
}
return "认证失败";
} catch (error) {
return `认证错误: ${(error as Error).message}`;
}
}
}
// 客户端代码
async function main() {
// 创建上下文并设置 JWT 策略
let context = new AuthContext(new JwtStrategy());
console.log(await context.authenticate({ token: "jwt_valid_token" })); // 输出: JWT 用户: user@example.com
console.log(await context.authenticate({ token: "invalid_token" })); // 输出: 认证错误: 无效的 JWT Token
// 切换到 Basic Auth 策略
context.setStrategy(new BasicAuthStrategy());
console.log(await context.authenticate({ username: "admin", password: "pass123" })); // 输出: Basic Auth 用户: admin
console.log(await context.authenticate({ username: "admin", password: "wrong" })); // 输出: 认证错误: 无效的用户名或密码
// 切换到 OAuth 策略
context.setStrategy(new OAuthStrategy());
console.log(await context.authenticate({ accessToken: "oauth_valid_token" })); // 输出: OAuth 用户: oauth_user@example.com
}
main();
运行结果
运行以上代码,将输出:
sql
JWT 用户: user@example.com
认证错误: 无效的 JWT Token
Basic Auth 用户: admin
认证错误: 无效的用户名或密码
OAuth 用户: oauth_user@example.com
为什么使用策略模式?
在身份认证系统中(如 Passport.js),认证逻辑因方式不同而变化(如JWT解析、Basic Auth验证、OAuth回调)。使用策略模式:
- 动态切换:无需修改认证上下文代码,只需注入新策略。
- 扩展性:新增认证方式(如SAML、OpenID)只需实现新策略类。
- 可测试性:每个策略独立,便于单元测试。
其他适用场景
虽然本示例聚焦身份认证,策略模式在认证系统中还可用于:
- 令牌解析:不同类型的令牌(如JWT、API Key)使用不同解析逻辑。
- 权限检查:根据用户角色或认证方式应用不同权限验证规则。
- 日志记录:不同认证方式记录不同格式的日志。
总结
策略模式在身份认证系统中通过封装可互换的认证算法(如JWT、Basic Auth、OAuth),提供了动态选择和扩展机制。TypeScript 的类型系统确保策略接口的一致性,而认证上下文的示例展示了如何通过策略模式实现灵活的认证逻辑。这使得系统能够轻松适应多种认证需求,同时保持代码的可维护性和扩展性。