对于最新的稳定版本,请使用 Spring Security 6.4.1! |
方法安全性
从版本 2.0 开始,Spring Security 大大改进了对向服务层方法添加安全性的支持。
它支持 JSR-250 注释安全性以及框架的原始@Secured
注解。
从 3.0 开始,您还可以使用新的基于表达式的注释。
您可以通过使用intercept-methods
元素来装饰 bean 声明,或者你可以通过使用 AspectJ 样式的切入点来保护整个服务层中的多个 bean。
EnableMethodSecurity
在 Spring Security 5.6 中,我们可以使用@EnableMethodSecurity
对任何@Configuration
实例。
这@EnableGlobalMethodSecurity
以多种方式。@EnableMethodSecurity
:
-
使用简化的
AuthorizationManager
API 而不是元数据源、配置属性、决策管理器和投票者。 这简化了重用和定制。 -
支持直接基于 Bean 的配置,而不是要求扩展
GlobalMethodSecurityConfiguration
自定义 bean -
使用原生 Spring AOP 构建,去除抽象并允许您使用 Spring AOP 构建块进行自定义
-
检查冲突的注释以确保安全配置明确
-
符合 JSR-250
-
使
@PreAuthorize
,@PostAuthorize
,@PreFilter
和@PostFilter
默认情况下
对于早期版本,请阅读 @EnableGlobalMethodSecurity 的类似支持。 |
例如,以下将启用 Spring Security 的@PreAuthorize
注解:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
// ...
}
@Configuration
@EnableMethodSecurity
class MethodSecurityConfig {
// ...
}
<sec:method-security/>
然后,向方法添加注释(在类或接口上)将相应地限制对该方法的访问。
Spring Security 的本机 Comments 支持为该方法定义了一组属性。
这些将传递给DefaultAuthorizationMethodInterceptorChain
让它做出实际决定:
-
Java
-
Kotlin
public interface BankService {
@PreAuthorize("hasRole('USER')")
Account readAccount(Long id);
@PreAuthorize("hasRole('USER')")
List<Account> findAccounts();
@PreAuthorize("hasRole('TELLER')")
Account post(Account account, Double amount);
}
interface BankService {
@PreAuthorize("hasRole('USER')")
fun readAccount(id : Long) : Account
@PreAuthorize("hasRole('USER')")
fun findAccounts() : List<Account>
@PreAuthorize("hasRole('TELLER')")
fun post(account : Account, amount : Double) : Account
}
您可以启用对 Spring Security 的@Secured
注释使用:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
@Configuration
@EnableMethodSecurity(securedEnabled = true)
class MethodSecurityConfig {
// ...
}
<sec:method-security secured-enabled="true"/>
或 JSR-250 使用:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
}
@Configuration
@EnableMethodSecurity(jsr250Enabled = true)
class MethodSecurityConfig {
// ...
}
<sec:method-security jsr250-enabled="true"/>
自定义授权
Spring Security 的@PreAuthorize
,@PostAuthorize
,@PreFilter
和@PostFilter
附带基于丰富表达式的支持。
如果需要自定义表达式的处理方式,可以公开自定义MethodSecurityExpressionHandler
这样:
-
Java
-
Kotlin
-
Xml
@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setTrustResolver(myCustomTrustResolver);
return handler;
}
companion object {
@Bean
fun methodSecurityExpressionHandler() : MethodSecurityExpressionHandler {
val handler = DefaultMethodSecurityExpressionHandler();
handler.setTrustResolver(myCustomTrustResolver);
return handler;
}
}
<sec:method-security>
<sec:expression-handler ref="myExpressionHandler"/>
</sec:method-security>
<bean id="myExpressionHandler"
class="org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler">
<property name="trustResolver" ref="myCustomTrustResolver"/>
</bean>
我们揭露 |
此外,对于基于角色的授权, Spring Security 添加了一个默认的ROLE_
前缀,用于计算hasRole
.
-
Java
-
Kotlin
-
Xml
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
companion object {
@Bean
fun grantedAuthorityDefaults() : GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("MYPREFIX_");
}
}
<sec:method-security/>
<bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<constructor-arg value="MYPREFIX_"/>
</bean>
我们揭露 |
自定义授权管理器
方法授权是方法授权之前和方法之后授权的组合。
Before-method 授权在调用方法之前执行。
如果该授权拒绝访问,则不会调用该方法,并且 |
要重新创建添加@EnableMethodSecurity
默认情况下,您将发布以下配置:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preFilterAuthorizationMethodInterceptor() {
return new PreFilterAuthorizationMethodInterceptor();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postFilterAuthorizationMethodInterceptor() {
return new PostFilterAuthorizationMethodInterceptor();
}
}
@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun preFilterAuthorizationMethodInterceptor() : Advisor {
return PreFilterAuthorizationMethodInterceptor();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun preAuthorizeAuthorizationMethodInterceptor() : Advisor {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun postAuthorizeAuthorizationMethodInterceptor() : Advisor {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun postFilterAuthorizationMethodInterceptor() : Advisor {
return PostFilterAuthorizationMethodInterceptor();
}
}
<sec:method-security pre-post-enabled="false"/>
<aop:config/>
<bean id="preFilterAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor"/>
<bean id="preAuthorizeAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor"
factory-method="preAuthorize"/>
<bean id="postAuthorizeAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor"
factory-method="postAuthorize"/>
<bean id="postFilterAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor"/>
请注意,Spring Security 的方法安全性是使用 Spring AOP 构建的。
因此,根据指定的顺序调用拦截器。
这可以通过调用setOrder
在 interceptor 实例上,如下所示:
-
Java
-
Kotlin
-
Xml
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postFilterAuthorizationMethodInterceptor() {
PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor();
interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1);
return interceptor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun postFilterAuthorizationMethodInterceptor() : Advisor {
val interceptor = PostFilterAuthorizationMethodInterceptor();
interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1);
return interceptor;
}
<bean id="postFilterAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor">
<property name="order"
value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).POST_AUTHORIZE.getOrder() -1}"/>
</bean>
您可能只想支持@PreAuthorize
在您的应用程序中,在这种情况下,您可以执行以下作:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorize() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
}
@Configuration
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun preAuthorize() : Advisor {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize()
}
}
<sec:method-security pre-post-enabled="false"/>
<aop:config/>
<bean id="preAuthorizeAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor"
factory-method="preAuthorize"/>
或者,您可能有一个自定义的 before-methodAuthorizationManager
,以便将其添加到列表中。
在这种情况下,您需要告诉 Spring Security 的AuthorizationManager
以及授权管理器应用于哪些方法和类。
因此,您可以将 Spring Security 配置为调用您的AuthorizationManager
介于两者之间@PreAuthorize
和@PostAuthorize
这样:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Advisor customAuthorize() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
AuthorizationManager<MethodInvocation> rule = AuthorityAuthorizationManager.isAuthenticated();
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
@Configuration
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun customAuthorize() : Advisor {
val pattern = JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
val rule = AuthorityAuthorizationManager.isAuthenticated();
val interceptor = AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
<sec:method-security/>
<aop:config/>
<bean id="customAuthorize"
class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor">
<constructor-arg>
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="org.mycompany.myapp.service.*"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
factory-method="isAuthenticated"/>
</constructor-arg>
<property name="order"
value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1}"/>
</bean>
你可以使用 Order 常量中指定的 order 常量将拦截器放置在 Spring Security 方法拦截器之间 |
对于方法后授权,也可以执行相同的作。 方法后授权通常涉及分析返回值以验证访问。
例如,您可能有一个方法来确认请求的帐户确实属于已登录用户,如下所示:
-
Java
-
Kotlin
public interface BankService {
@PreAuthorize("hasRole('USER')")
@PostAuthorize("returnObject.owner == authentication.name")
Account readAccount(Long id);
}
interface BankService {
@PreAuthorize("hasRole('USER')")
@PostAuthorize("returnObject.owner == authentication.name")
fun readAccount(id : Long) : Account
}
您可以自备AuthorizationMethodInterceptor
自定义如何评估对 return 值的访问权限。
例如,如果您有自己的自定义注释,则可以按如下方式对其进行配置:
-
Java
-
Kotlin
-
Xml
@Configuration
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Advisor customAuthorize(AuthorizationManager<MethodInvocationResult> rules) {
AnnotationMatchingPointcut pattern = new AnnotationMatchingPointcut(MySecurityAnnotation.class);
AuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(pattern, rules);
interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
@Configuration
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun customAuthorize(rules : AuthorizationManager<MethodInvocationResult>) : Advisor {
val pattern = AnnotationMatchingPointcut(MySecurityAnnotation::class.java);
val interceptor = AuthorizationManagerAfterMethodInterceptor(pattern, rules);
interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
<sec:method-security/>
<aop:config/>
<bean id="customAuthorize"
class="org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor">
<constructor-arg>
<bean class="org.springframework.aop.support.annotation.AnnotationMethodMatcher">
<constructor-arg value="#{T(org.mycompany.MySecurityAnnotation)}"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
factory-method="isAuthenticated"/>
</constructor-arg>
<property name="order"
value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1}"/>
</bean>
它将在@PostAuthorize
拦截 器。
EnableGlobalMethodSecurity
我们可以通过使用@EnableGlobalMethodSecurity
对任何@Configuration
实例。
以下示例启用 Spring Security 的@Secured
注解:
-
Java
-
Kotlin
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
open class MethodSecurityConfig {
// ...
}
然后,向方法添加注释(在类或接口上)将相应地限制对该方法的访问。
Spring Security 的本机 Comments 支持为该方法定义了一组属性。
这些参数将传递给AccessDecisionManager
让它做出实际决定:
-
Java
-
Kotlin
public interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
fun readAccount(id: Long): Account
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
fun findAccounts(): Array<Account>
@Secured("ROLE_TELLER")
fun post(account: Account, amount: Double): Account
}
可以通过以下方式启用对 JSR-250 注释的支持:
-
Java
-
Kotlin
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
}
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true)
open class MethodSecurityConfig {
// ...
}
这些是基于标准的,允许应用简单的基于角色的约束,但没有 Spring Security 的原生 Comments 的功能。 要使用新的基于表达式的语法,您将使用:
-
Java
-
Kotlin
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig {
// ...
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
open class MethodSecurityConfig {
// ...
}
等效的 Java 代码为:
-
Java
-
Kotlin
public interface BankService {
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
interface BankService {
@PreAuthorize("isAnonymous()")
fun readAccount(id: Long): Account
@PreAuthorize("isAnonymous()")
fun findAccounts(): Array<Account>
@PreAuthorize("hasAuthority('ROLE_TELLER')")
fun post(account: Account, amount: Double): Account
}
GlobalMethodSecurityConfiguration
有时,您可能需要执行比使用@EnableGlobalMethodSecurity
注解。
对于这些实例,您可以扩展GlobalMethodSecurityConfiguration
,确保@EnableGlobalMethodSecurity
annotation 存在于您的子类上。
例如,如果您想提供自定义MethodSecurityExpressionHandler
,您可以使用以下配置:
-
Java
-
Kotlin
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
// ... create and return custom MethodSecurityExpressionHandler ...
return expressionHandler;
}
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
open class MethodSecurityConfig : GlobalMethodSecurityConfiguration() {
override fun createExpressionHandler(): MethodSecurityExpressionHandler {
// ... create and return custom MethodSecurityExpressionHandler ...
return expressionHandler
}
}
有关可以覆盖的方法的其他信息,请参阅GlobalMethodSecurityConfiguration
类。
<global-method-security> 元素
此元素用于在应用程序中启用基于 Comments 的安全性(通过在元素上设置适当的属性),并将应用于整个应用程序上下文的安全切入点声明组合在一起。
您应该只声明一个<global-method-security>
元素。
以下声明启用了对 Spring Security 的@Secured
:
<global-method-security secured-annotations="enabled" />
然后,向方法添加注释(在类或接口上)将相应地限制对该方法的访问。
Spring Security 的本机 Comments 支持为该方法定义了一组属性。
这些参数将传递给AccessDecisionManager
让它做出实际决定。
以下示例显示了@Secured
注解:
-
Java
-
Kotlin
public interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
fun readAccount(id: Long): Account
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
fun findAccounts(): Array<Account>
@Secured("ROLE_TELLER")
fun post(account: Account, amount: Double): Account
}
可以通过以下方式启用对 JSR-250 注释的支持:
<global-method-security jsr250-annotations="enabled" />
这些是基于标准的,并允许应用简单的基于角色的约束,但它们没有 Spring Security 的原生 Comments 的强大功能。 要使用基于表达式的语法,请使用:
<global-method-security pre-post-annotations="enabled" />
等效的 Java 代码为:
-
Java
-
Kotlin
public interface BankService {
@PreAuthorize("isAnonymous()")
public Account readAccount(Long id);
@PreAuthorize("isAnonymous()")
public Account[] findAccounts();
@PreAuthorize("hasAuthority('ROLE_TELLER')")
public Account post(Account account, double amount);
}
interface BankService {
@PreAuthorize("isAnonymous()")
fun readAccount(id: Long): Account
@PreAuthorize("isAnonymous()")
fun findAccounts(): Array<Account>
@PreAuthorize("hasAuthority('ROLE_TELLER')")
fun post(account: Account, amount: Double): Account
}
如果您需要定义简单的规则,而不仅仅是根据用户的权限列表检查角色名称,则基于表达式的注释是一个不错的选择。
带注释的方法将仅对定义为 Spring bean 的实例进行保护(在启用了 method-security 的同一应用程序上下文中)。
如果要保护不是由 Spring 创建的实例(使用 |
您可以在同一个应用程序中启用多种类型的 Comments,但任何接口或类都只能使用一种类型,否则行为将不会得到明确定义。 如果找到两个适用于特定方法的 Comments,则只会应用其中一个。 |
使用 protect-pointcut 添加安全切入点
protect-pointcut
特别强大,因为它允许您仅通过一个简单的声明将安全性应用于许多 bean。
请考虑以下示例:
<global-method-security>
<protect-pointcut expression="execution(* com.mycompany.*Service.*(..))"
access="ROLE_USER"/>
</global-method-security>
d.
此配置保护在应用程序上下文中声明的 bean 上的所有方法,这些 bean 的类位于com.mycompany
package 及其类名以Service
.
只有ROLE_USER
role 可以调用这些方法。
与 URL 匹配一样,最具体的匹配项必须位于切入点列表中的第一个,因为使用的是第一个匹配表达式。
安全注释优先于切入点。