跳至主要内容

Foundational Architecture

在 ChatGPT 中打开

webforJ安全系统建立在核心接口的基础之上,这些接口共同工作以提供基于路由的访问控制。这些接口定义了安全行为的契约,允许不同的实现方式,无论是基于会话的、基于JSON Web令牌(JWT)的、集成LDAP的还是基于数据库的,都可以接入相同的底层框架。

理解这一体系结构有助于你理解像@RolesAllowed@PermitAll这样的安全注解是如何被评估的,导航拦截是如何工作的,以及你如何可以为特定需求构建自定义的安全实现。

核心接口

安全基础建立在关键抽象之上,每个抽象都有特定的责任:

RouteSecurityManager

RouteSecurityManager是安全系统的中央协调者。它管理安全评估器,协调评估过程,并通过重定向用户到适当的页面来处理访问拒绝。

责任:

  • 注册和管理优先级的安全评估器
  • 在用户导航到路由时协调评估过程
  • 通过触发重定向到登录或拒绝访问页面来处理访问拒绝
  • 存储和检索用于登录后重定向的预认证位置
public interface RouteSecurityManager {
RouteAccessDecision evaluate(Class<?> routeClass, NavigationContext context);
void onAccessDenied(RouteAccessDecision decision, NavigationContext context);
RouteSecurityContext getSecurityContext();
RouteSecurityConfiguration getConfiguration();
void registerEvaluator(RouteSecurityEvaluator evaluator, int priority);
Optional<Location> consumePreAuthenticationLocation();
}

管理器本身不做安全决策,它将任务委托给评估器和配置。它是连接所有安全组件的纽带。

RouteSecurityContext

RouteSecurityContext提供当前用户的认证状态访问。它可以回答像用户是否已认证、用户的用户名是什么以及用户是否拥有ADMIN角色的问题。

责任:

  • 确定当前用户是否已认证
  • 提供用户的主体(通常是他们的用户名或用户对象)
  • 检查用户是否拥有特定角色或权限
  • 存储和检索自定义安全属性
public interface RouteSecurityContext {
boolean isAuthenticated();
Optional<Object> getPrincipal();
boolean hasRole(String role);
boolean hasAuthority(String authority);
Optional<Object> getAttribute(String name);
void setAttribute(String name, Object value);
}

实现方式根据认证系统、HTTP会话存储、从头部解码的JWT令牌、数据库查询、LDAP查找或其他机制而有所不同。

RouteSecurityConfiguration

RouteSecurityConfiguration定义安全行为和重定向位置。它告诉安全系统在需要认证或拒绝访问时应该将用户重定向到哪里。

责任:

  • 定义安全是否启用
  • 指定默认安全行为
  • 提供认证页面位置(通常是/login
  • 提供访问拒绝页面位置
public interface RouteSecurityConfiguration {
default boolean isEnabled() { return true; }
default boolean isSecureByDefault() { return true; }
default Optional<Location> getAuthenticationLocation() {
return Optional.of(new Location("/login"));
}
default Optional<Location> getDenyLocation() { /* ... */ }
}

该接口将安全策略与安全执行分离。你可以更改重定向位置或切换默认安全而无需修改管理器或评估器。

RouteSecurityEvaluator

RouteSecurityEvaluator是检查实际安全规则的地方。每个评估器检查一个路由并决定是否授予访问权限、拒绝访问权限或将决定委托给链中的下一个评估器。

责任:

  • 确定此评估器是否处理给定的路由
  • 评估路由类上的安全注解
  • 授予访问权限、拒绝访问权限或委托给下一个评估器
  • 参与责任链模式
public interface RouteSecurityEvaluator {
RouteAccessDecision evaluate(Class<?> routeClass,
NavigationContext context,
RouteSecurityContext securityContext,
SecurityEvaluatorChain chain);
default boolean supports(Class<?> routeClass) { return true; }
}

内置评估器处理标准注解,如@RolesAllowed@PermitAll@DenyAll@AnonymousAccess。你可以创建自定义评估器以实现特定领域的安全逻辑。

接口如何协同工作

这四个接口在导航过程中协作以强制执行安全规则:

当用户导航时,RouteSecurityObserver拦截导航并请求RouteSecurityManager评估访问。管理器查询RouteSecurityConfiguration以获取设置,从RouteSecurityContext获取用户信息,按照优先级顺序运行每个RouteSecurityEvaluator,直到其中一个做出决定。

接口作为契约

每个接口定义了一个契约,一组安全系统需要回答的问题。如何回答这些问题是你的实现选择:

RouteSecurityContext契约:

  • "当前用户是否已认证?" (isAuthenticated())
  • "用户是谁?" (getPrincipal())
  • "用户是否具有角色X?" (hasRole())

你决定这些信息来自HTTP会话、从头部解码的JWT令牌、数据库查找、LDAP查询或任何其他认证后端的来源。

RouteSecurityConfiguration契约:

  • "安全是否启用?" (isEnabled())
  • "路由是否应默认安全?" (isSecureByDefault())
  • "未认证用户应该去哪儿?" (getAuthenticationLocation())

你决定如何获取这些值:硬编码的、来自配置文件的、来自环境变量的、来自数据库的或动态计算的。

RouteSecurityManager契约:

  • "该用户应该访问此路由吗?" (evaluate())
  • "当访问被拒绝时发生什么?" (onAccessDenied())
  • "应该运行哪些评估器?" (registerEvaluator())

你决定认证流程、在哪里存储预认证位置以及如何处理自定义拒绝场景。

基础架构定义了这些契约,但实现是灵活的。不同的系统可以根据特定要求以完全不同的方式实现这些接口。

AbstractRouteSecurityManager 基类

大多数实现并不直接实现RouteSecurityManager。相反,它们扩展AbstractRouteSecurityManager,该类提供:

  • 评估器注册和基于优先级的排序
  • 链执行逻辑
  • 处理访问拒绝及自动重定向
  • 在HTTP会话中存储预认证位置
  • 默认安全的回退行为

基类实现了RouteSecurityManager接口,并为评估器管理、访问评估和拒绝处理提供了具体实现。子类只需要提供安全上下文和配置。基类自动处理评估器管理、链执行和拒绝处理。