设计模式与安全:认证中的策略、授权中的代理、加密中的模板方法
说实话,很多做业务系统的朋友觉得设计模式跟安全八竿子打不着。但我在做支付系统的时候,被安全架构折腾得够呛,才真正体会到——设计模式在安全领域,简直就是救星。
今天咱们就聊三个最典型的场景:认证、授权、加密。这三个场景分别对应三种设计模式:策略模式、代理模式、模板方法模式。我一个个拆开讲。
认证中的策略模式
先说说认证。你想想看,一个系统里可能有多种认证方式:用户名密码、短信验证码、微信扫码、指纹识别……如果把这些逻辑全写在一个类里,那代码会变成什么样?
我曾经接手过一个老项目,认证逻辑全堆在一个叫 AuthService 的类里,光 if-else 就有十几层。每次加一种新的认证方式,就得改这个类,测试回归一遍,累得够呛。
策略模式就是干这个的。它把每种认证方式封装成一个独立的策略类,客户端只需要选择策略,不用关心具体实现。
来看代码。先定义一个认证策略接口:
// Java 版本
public interface AuthenticationStrategy {
boolean authenticate(String credentials);
}
// 用户名密码策略
public class UsernamePasswordStrategy implements AuthenticationStrategy {
@Override
public boolean authenticate(String credentials) {
// 解析用户名和密码
// 校验逻辑
System.out.println("使用用户名密码认证");
return true;
}
}
// 短信验证码策略
public class SmsCodeStrategy implements AuthenticationStrategy {
@Override
public boolean authenticate(String credentials) {
// 校验短信验证码
System.out.println("使用短信验证码认证");
return true;
}
}
// 上下文类
public class AuthenticationContext {
private AuthenticationStrategy strategy;
public void setStrategy(AuthenticationStrategy strategy) {
this.strategy = strategy;
}
public boolean executeAuthentication(String credentials) {
return strategy.authenticate(credentials);
}
}
C++ 版本也差不多,只是多了指针管理:
// C++ 版本
class AuthenticationStrategy {
public:
virtual ~AuthenticationStrategy() = default;
virtual bool authenticate(const std::string& credentials) = 0;
};
class UsernamePasswordStrategy : public AuthenticationStrategy {
public:
bool authenticate(const std::string& credentials) override {
std::cout << "使用用户名密码认证" << std::endl;
return true;
}
};
class AuthenticationContext {
private:
std::unique_ptr<AuthenticationStrategy> strategy_;
public:
void setStrategy(std::unique_ptr<AuthenticationStrategy> strategy) {
strategy_ = std::move(strategy);
}
bool executeAuthentication(const std::string& credentials) {
return strategy_->authenticate(credentials);
}
};
授权中的代理模式
授权跟认证不一样。认证是「你是谁」,授权是「你能干什么」。在授权场景里,代理模式特别好用。
我记得有一次做权限系统,业务方要求:某些操作只有管理员能做,普通用户不能做。如果直接在业务代码里写权限判断,那每个方法都得加一段 if(checkPermission()),代码会变得很脏。
代理模式的做法是:创建一个代理对象,在代理对象里做权限检查,通过后才调用真实对象的方法。
// Java 版本
// 真实主题接口
public interface DocumentService {
void viewDocument(String docId);
void editDocument(String docId);
}
// 真实主题
public class DocumentServiceImpl implements DocumentService {
@Override
public void viewDocument(String docId) {
System.out.println("查看文档: " + docId);
}
@Override
public void editDocument(String docId) {
System.out.println("编辑文档: " + docId);
}
}
// 代理类
public class DocumentServiceProxy implements DocumentService {
private DocumentService realService;
private String userRole;
public DocumentServiceProxy(String userRole) {
this.realService = new DocumentServiceImpl();
this.userRole = userRole;
}
@Override
public void viewDocument(String docId) {
// 查看权限:所有用户都可以
realService.viewDocument(docId);
}
@Override
public void editDocument(String docId) {
// 编辑权限:只有管理员可以
if (!"ADMIN".equals(userRole)) {
throw new SecurityException("无编辑权限");
}
realService.editDocument(docId);
}
}
C++ 版本:
// C++ 版本
class DocumentService {
public:
virtual ~DocumentService() = default;
virtual void viewDocument(const std::string& docId) = 0;
virtual void editDocument(const std::string& docId) = 0;
};
class DocumentServiceProxy : public DocumentService {
private:
std::unique_ptr<DocumentService> realService_;
std::string userRole_;
public:
DocumentServiceProxy(const std::string& role)
: realService_(std::make_unique<DocumentServiceImpl>()), userRole_(role) {}
void viewDocument(const std::string& docId) override {
realService_->viewDocument(docId);
}
void editDocument(const std::string& docId) override {
if (userRole_ != "ADMIN") {
throw std::runtime_error("无编辑权限");
}
realService_->editDocument(docId);
}
};
加密中的模板方法模式
加密算法有很多种:AES、DES、RSA、SM4……但它们的流程其实差不多:初始化密钥、加密、解密。不同的只是具体算法实现。
模板方法模式正好适合这种场景。它把不变的流程写在父类里,把可变的算法实现留给子类去完成。
// Java 版本
public abstract class EncryptionTemplate {
// 模板方法,定义加密流程
public final byte[] encrypt(byte[] data, String key) {
byte[] processedKey = processKey(key);
byte[] encryptedData = doEncrypt(data, processedKey);
return postProcess(encryptedData);
}
// 模板方法,定义解密流程
public final byte[] decrypt(byte[] data, String key) {
byte[] processedKey = processKey(key);
byte[] decryptedData = doDecrypt(data, processedKey);
return postProcess(decryptedData);
}
// 子类必须实现的步骤
protected abstract byte[] processKey(String key);
protected abstract byte[] doEncrypt(byte[] data, byte[] key);
protected abstract byte[] doDecrypt(byte[] data, byte[] key);
// 可选钩子方法
protected byte[] postProcess(byte[] data) {
return data; // 默认不做后处理
}
}
// AES 加密实现
public class AESEncryption extends EncryptionTemplate {
@Override
protected byte[] processKey(String key) {
// AES 密钥处理逻辑
System.out.println("处理 AES 密钥");
return key.getBytes();
}
@Override
protected byte[] doEncrypt(byte[] data, byte[] key) {
System.out.println("执行 AES 加密");
return data; // 实际加密逻辑
}
@Override
protected byte[] doDecrypt(byte[] data, byte[] key) {
System.out.println("执行 AES 解密");
return data; // 实际解密逻辑
}
}
C++ 版本:
// C++ 版本
class EncryptionTemplate {
public:
virtual ~EncryptionTemplate() = default;
// 模板方法
std::vector<uint8_t> encrypt(const std::vector<uint8_t>& data, const std::string& key) {
auto processedKey = processKey(key);
auto encryptedData = doEncrypt(data, processedKey);
return postProcess(encryptedData);
}
std::vector<uint8_t> decrypt(const std::vector<uint8_t>& data, const std::string& key) {
auto processedKey = processKey(key);
auto decryptedData = doDecrypt(data, processedKey);
return postProcess(decryptedData);
}
protected:
virtual std::vector<uint8_t> processKey(const std::string& key) = 0;
virtual std::vector<uint8_t> doEncrypt(const std::vector<uint8_t>& data,
const std::vector<uint8_t>& key) = 0;
virtual std::vector<uint8_t> doDecrypt(const std::vector<uint8_t>& data,
const std::vector<uint8_t>& key) = 0;
virtual std::vector<uint8_t> postProcess(const std::vector<uint8_t>& data) {
return data;
}
};
三种模式的对比
| 设计模式 | 安全场景 | 核心作用 | 我的建议 |
|---|---|---|---|
| 策略模式 | 认证 | 多种认证方式灵活切换 | 适合认证方式经常变化的系统 |
| 代理模式 | 授权 | 控制访问权限 | 适合需要细粒度权限控制的场景 |
| 模板方法模式 | 加密 | 固定加密流程,可变算法 | 适合需要支持多种加密算法的系统 |
知识体系结构图
最后说一句:设计模式不是银弹,但用对地方,它就是最好的工具。安全领域尤其如此,因为安全需求变化快、规则复杂,设计模式正好能帮你应对这种变化。