Spring Security 3.0 介绍使用 Spring EL 表达式除了配置属性和访问决策的选民已见过的简单的使用授权机制的能力。基于访问控制的表达是建立在相同的结构,但允许复杂的 Boolean 逻辑被封装在一个单一的表达。 Spring Security 使用 Spring EL 作为表达式支持,如果你有兴趣更深入地理解这个主题,那么应该看看它是如何工作的。表达式以 "root object" 作为评估上下文的一部分进行评估。Spring Security 使用用于 web 和方法安全性的特定类作为根对象,以便提供内置的表达式和对值(如当前主体)的访问。
表达式根对象的基类是 表格 27.1. 常用内建表达式
要使用表达式来保护单个 URL,首先需要将 <http> <intercept-url pattern="/admin*" access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/> ... </http>
这里,我们已经定义了应用程序的 "admin" 区域(由 URL 模式定义)应该只对具有授予的权限 "admin" 并且其 IP 地址与本地子网匹配的用户可用。我们已经在上一节中看到了内置的
如果希望扩展可用的表达式,则可以轻松地引用所公开的任何 Spring bean。例如,假设你有一个命名为 public class WebSecurity { public boolean check(Authentication authentication, HttpServletRequest request) { ... } } 你可以引用这个方法来使用: <http> <intercept-url pattern="/user/**" access="@webSecurity.check(authentication,request)"/> ... </http> 或在 Java 配置中 http .authorizeRequests() .antMatchers("/user/**").access("@webSecurity.check(authentication,request)") ...
有时,能够在 URL 内引用路径变量是很好的。例如,考虑一个 RESTful 应用程序,它以格式
通过将路径变量放置在模式中,可以轻松地引用路径变量。例如,如果你有一个命名为 public class WebSecurity { public boolean checkUserId(Authentication authentication, int id) { ... } } 你可以引用这个方法来使用: <http> <intercept-url pattern="/user/{userId}/**" access="@webSecurity.checkUserId(authentication,#userId)"/> ... </http> 或在 Java 配置中 http .authorizeRequests() .antMatchers("/user/{userId}/**").access("@webSecurity.checkUserId(authentication,#userId)") ...
在这两种配置中,匹配的 URL 将在路径变量中传递(并将其转换)为 checkUserId 方法。例如,如果 URL 是 方法安全性比简单的允许或拒绝规则更复杂一些。Spring Security 3.0 引入了一些新的注释,以便允许对表达式的使用进行全面支持。
有四个注解支持表达式属性,以允许调用前和调用后授权检查,还支持过滤提交的集合参数或返回值。它们是 <global-method-security pre-post-annotations="enabled"/>
最明显的有用注解是 @PreAuthorize("hasRole('USER')") public void create(Contact contact); 这意味着只允许使用具有 "ROLE_USER" 角色的用户访问。显然,使用传统配置和所需角色的简单配置属性可以容易地实现相同的目的。但如何: @PreAuthorize("hasPermission(#contact, 'admin')") public void deletePermission(Contact contact, Sid recipient, Permission permission);
这里,我们实际上使用方法参数作为表达式的一部分,以确定当前用户是否具有给定联系人的 "admin" 权限。内置的
有许多方法可以在 Spring Security 中解决方法参数。Spring Security 使用
表达式中可以使用任何 Spring EL 功能,因此你也可以访问参数上的属性。例如,如果希望某个特定的方法只允许访问用户名与联系人的用户名匹配的用户,则可以 @PreAuthorize("#contact.name == authentication.name") public void doSomething(Contact contact);
这里,我们正在访问另一个内置表达式,即
通常情况下,你可能希望在调用该方法之后执行访问控制检查。这可以使用 你可能已经知道,Spring Security 支持对集合和数组进行过滤,现在可以使用表达式来实现这一点。这通常在方法的返回值上执行。例如: @PreAuthorize("hasRole('USER')") @PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')") public List<Contact> getAll();
在使用 注意,过滤显然不是调整数据检索查询的替代品。如果你正在过滤大集合并删除许多条目,那么这可能是低效的。
有一些内置的表达式是特定于方法安全性的,我们已经在上面使用过。
boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission); boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission);
它直接映射到表达式的可用版本,但不提供第一个参数(
要使用 <security:global-method-security pre-post-annotations="enabled"> <security:expression-handler ref="expressionHandler"/> </security:global-method-security> <bean id="expressionHandler" class= "org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler"> <property name="permissionEvaluator" ref="myPermissionEvaluator"/> </bean>
其中 可以使用元注解来实现方法安全性,从而使代码更加可读。如果发现在整个代码库中重复相同的复杂表达式,这一点尤其方便。例如,考虑以下内容: @PreAuthorize("#contact.name == authentication.name")
我们可以创建一个可以使用的元注解,而不是到处重复。 @Retention(RetentionPolicy.RUNTIME) @PreAuthorize("#contact.name == authentication.name") public @interface ContactPermission {} 元注解可以用于任何 Spring Security 方法安全注解。为了保持符合规范,JSR-250 注解不支持元注解。 |