设计模式与权限系统:责任链、策略、代理三剑客
权限系统,说白了就是「谁能不能干什么」。我做了十几年系统,见过太多权限代码写得像一团乱麻。今天咱们聊聊怎么用三个设计模式,把权限系统理得清清楚楚。
我个人习惯把权限系统拆成三个层次:权限校验流程、权限策略选择、访问控制拦截。正好对应责任链、策略、代理三个模式。你想想看,是不是很自然?
一、责任链模式:让权限校验像流水线
先说说责任链模式。我在项目中遇到过这样一个场景:一个请求过来,要经过登录校验、角色校验、资源权限校验、数据权限校验……如果全写在一个方法里,那代码简直没法看。
责任链模式就是把这些校验器串成一条链。每个节点只做一件事,做完就传给下一个。这样既清晰又灵活。
核心思想:每个处理器只关注自己的职责,处理完就交给下一个。你可以动态增删处理器,而不影响其他环节。
// 抽象处理器
public abstract class PermissionHandler {
protected PermissionHandler next;
public void setNext(PermissionHandler next) {
this.next = next;
}
public abstract boolean handle(Request request);
protected boolean handleNext(Request request) {
if (next == null) return true;
return next.handle(request);
}
}
// 登录校验
public class LoginHandler extends PermissionHandler {
@Override
public boolean handle(Request request) {
if (request.getUser() == null) {
System.out.println("用户未登录,拒绝访问");
return false;
}
System.out.println("登录校验通过");
return handleNext(request);
}
}
// 角色校验
public class RoleHandler extends PermissionHandler {
@Override
public boolean handle(Request request) {
if (!"admin".equals(request.getUser().getRole())) {
System.out.println("角色权限不足");
return false;
}
System.out.println("角色校验通过");
return handleNext(request);
}
}
// 资源权限校验
public class ResourceHandler extends PermissionHandler {
@Override
public boolean handle(Request request) {
if (!request.getUser().hasPermission(request.getResource())) {
System.out.println("无资源访问权限");
return false;
}
System.out.println("资源权限校验通过");
return handleNext(request);
}
}
我的经验:责任链的顺序很重要。我一般把「成本低、拒绝快」的校验放前面。比如登录校验,用户没登录就直接返回,不用再去查数据库里的资源权限了。这样可以大幅提升性能。
二、策略模式:权限策略灵活切换
权限校验通过了,但不同资源、不同场景下的权限策略可能完全不同。比如:
- 普通文档:只要登录就能看
- 机密文档:需要管理员审批
- 个人资料:只能自己看
这时候策略模式就派上用场了。每种权限策略封装成一个类,运行时动态选择。
// 策略接口
public interface PermissionStrategy {
boolean checkPermission(User user, Resource resource);
}
// 公开策略:登录即可
public class PublicStrategy implements PermissionStrategy {
@Override
public boolean checkPermission(User user, Resource resource) {
return user != null;
}
}
// 机密策略:需要管理员
public class ConfidentialStrategy implements PermissionStrategy {
@Override
public boolean checkPermission(User user, Resource resource) {
return user != null && "admin".equals(user.getRole());
}
}
// 私有策略:只能自己
public class PrivateStrategy implements PermissionStrategy {
@Override
public boolean checkPermission(User user, Resource resource) {
return user != null && user.getId().equals(resource.getOwnerId());
}
}
// 策略上下文
public class PermissionContext {
private PermissionStrategy strategy;
public void setStrategy(PermissionStrategy strategy) {
this.strategy = strategy;
}
public boolean executeStrategy(User user, Resource resource) {
return strategy.checkPermission(user, resource);
}
}
避坑指南:我曾经把策略选择逻辑写死在if-else里,结果每次新增策略都要改代码。后来改成用配置文件+反射加载,新增策略只需要加一个类和一个配置项,完全符合开闭原则。
三、代理模式:优雅的访问控制
最后说说代理模式。权限校验和策略选择都做好了,但怎么把这些逻辑「无侵入」地加到业务代码里?
代理模式就是最好的答案。它像一个门卫,在真正访问目标对象之前,先执行权限检查。
// 业务接口
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 PermissionHandler handlerChain;
private PermissionContext context;
public DocumentServiceProxy(DocumentService realService) {
this.realService = realService;
// 初始化责任链
this.handlerChain = new LoginHandler();
handlerChain.setNext(new RoleHandler());
handlerChain.setNext(new ResourceHandler());
// 初始化策略上下文
this.context = new PermissionContext();
}
@Override
public void viewDocument(String docId) {
Request request = buildRequest(docId, "view");
// 先走责任链校验
if (!handlerChain.handle(request)) {
throw new SecurityException("权限不足");
}
// 再走策略校验
if (!context.executeStrategy(request.getUser(), request.getResource())) {
throw new SecurityException("策略不允许");
}
// 通过后调用真实方法
realService.viewDocument(docId);
}
@Override
public void editDocument(String docId) {
// 类似实现...
}
}
实际应用:在Spring框架中,AOP就是代理模式的典型应用。你可以用@Around注解,在方法执行前后插入权限校验逻辑。但底层原理和上面写的代理类是一样的。
四、三模式协同:一张图看懂
这三个模式不是孤立的,它们协同工作才能构建一个完整的权限系统。我画了一张图,帮你理清它们的关系:
从图上可以看得很清楚:代理模式在最外层,拦截所有请求;责任链模式在中间层,做流程化的校验;策略模式在最底层,根据资源类型选择具体的权限规则。三层各司其职,互不干扰。
五、实际项目中的组合用法
我在一个企业级文档管理系统中,就是这样设计的。具体做法是:
- 代理层:用Spring AOP的@Around注解,拦截所有Controller方法
- 责任链层:每个校验器实现Handler接口,通过配置决定顺序
- 策略层:每种文档类型对应一个策略实现,从数据库读取配置
这样做的好处是:
| 模式 | 职责 | 变更影响范围 |
|---|---|---|
| 代理模式 | 拦截请求,统一入口 | 新增接口时,只需加切点表达式 |
| 责任链模式 | 流程化校验,可动态增删 | 新增校验器,不影响已有逻辑 |
| 策略模式 | 具体权限规则,按需切换 | 新增策略,只需加类+配置 |
总结一下:这三个模式组合起来,权限系统就变得非常灵活。你想想看,如果哪天业务方说「我们要加一个IP白名单校验」,你只需要在责任链里加一个处理器;如果又说「机密文档要改成部门经理审批」,你只需要改一下策略实现。完全不用动业务代码。
嗯,这就是我多年实战中沉淀下来的权限系统设计思路。代码写得再漂亮,不如架构设计得合理。希望对你有所帮助。