Intro to Spring Security Expressions – Spring安全表达式介绍

最后修改: 2016年 7月 27日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we’ll focus on Spring Security Expressions and practical examples using these expressions.

在本教程中,我们将重点介绍Spring安全表达式和使用这些表达式的实际例子。

Before looking at more complex implementations, such as ACL, it’s important to have a solid grasp on security expressions, as they can be quite flexible and powerful if used correctly.

在研究更复杂的实现(如ACL)之前,对安全表达式有一个扎实的掌握是很重要的,因为如果使用得当,它们可以相当灵活和强大。

2. Maven Dependencies

2.Maven的依赖性

In order to use Spring Security, we need to include the following section in our pom.xml file:

为了使用Spring Security,我们需要在pom.xml文件中包含以下部分。

<dependencies>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.6.0</version>
   </dependency>
</dependencies>

The latest version can be found here.

最新版本可以在这里找到。

Please note that this dependency only covers Spring Security; we’ll need to add spring-core and spring-context for a full web application.

请注意,这个依赖关系只包括Spring Security;我们需要添加spring-corespring-context来实现一个完整的Web应用。

3. Configuration

3.配置

First, let’s take a look at a Java configuration:

首先,让我们来看看一个Java配置。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan("com.baeldung.security")
public class SecurityJavaConfig {
    ...
}

We can, of course, do an XML configuration as well:

当然,我们也可以做一个XML配置。

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans ...>
    <global-method-security pre-post-annotations="enabled"/>
</beans:beans>

4. Web Security Expressions

4.网络安全表达式

Now let’s explore the security expressions:

现在我们来探讨一下安全表达。

  • hasRole, hasAnyRole
  • hasAuthority, hasAnyAuthority
  • permitAll, denyAll
  • isAnonymous, isRememberMe, isAuthenticated, isFullyAuthenticated
  • principal, authentication
  • hasPermission

4.1. hasRole, hasAnyRole

4.1.hasRole, hasAnyRole

These expressions are responsible for defining the access control or authorization to specific URLs and methods in our application:

这些表达式负责定义对我们应用程序中特定URL和方法的访问控制或授权。

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    ...
    .antMatchers("/auth/admin/*").hasRole("ADMIN")
    .antMatchers("/auth/*").hasAnyRole("ADMIN","USER")
    ...
}

In the above example, we specified access to all the links starting with /auth/, restricting them to users that log in with role USER or role ADMIN. Moreover, to access links starting with /auth/admin/, we need to have an ADMIN role in the system.

在上面的例子中,我们指定了对所有以/auth/开头的链接的访问,将它们限制在以USERADMIN.角色登录的用户。此外,要访问以/auth/admin/开头的链接,我们需要在系统中有一个ADMIN角色。

We can achieve the same configuration in an XML file by writing:

我们可以在一个XML文件中通过编写实现同样的配置。

<http>
    <intercept-url pattern="/auth/admin/*" access="hasRole('ADMIN')"/>
    <intercept-url pattern="/auth/*" access="hasAnyRole('ADMIN','USER')"/>
</http>

4.2. hasAuthority, hasAnyAuthority

4.2.hasAuthority, hasAnyAuthority

Roles and authorities are similar in Spring.

角色和权限在Spring中是类似的。

The main difference is that roles have special semantics. Starting with Spring Security 4, the ‘ROLE_‘ prefix is automatically added (if it’s not already there) by any role-related method.

主要的区别在于,角色有特殊的语义。从Spring Security 4开始,任何角色相关的方法都会自动添加’ROLE_‘前缀(如果它不在那里)。

So hasAuthority(‘ROLE_ADMIN’) is similar to hasRole(‘ADMIN’) because the ‘ROLE_‘ prefix gets added automatically.

所以hasAuthority(‘ROLE_ADMIN’)类似于hasRole(‘ADMIN’),因为’ROLE_‘前缀被自动添加。

The benefit to using authorities is that we don’t have to use the ROLE_ prefix at all.

使用当局的好处是,我们根本不必使用ROLE_前缀。

Here’s a quick example defining users with specific authorities:

下面是一个快速的例子,定义具有特定权限的用户。

@Bean
public InMemoryUserDetailsManager userDetailsService() {
    UserDetails admin = User.withUsername("admin")
        .password(encoder().encode("adminPass"))
        .roles("ADMIN")
        .build();
    UserDetails user = User.withUsername("user")
        .password(encoder().encode("userPass"))
        .roles("USER")
        .build();
    return new InMemoryUserDetailsManager(admin, user);
}

We can then use these authorities expressions:

然后我们可以使用这些当局的表达方式。

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    ...
    .antMatchers("/auth/admin/*").hasAuthority("ADMIN")
    .antMatchers("/auth/*").hasAnyAuthority("ADMIN", "USER")
    ...
}

As we can see, we don’t mention roles here at all.

我们可以看到,我们在这里根本没有提到角色。

Additionally, starting with Spring 5, we need a PasswordEncoder bean:

此外,从Spring 5开始,我们需要一个PasswordEncoder bean。

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Finally, we have the option to achieve the same functionality using XML configuration as well:

最后,我们也可以选择使用XML配置来实现同样的功能。

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user1" password="user1Pass" authorities="ROLE_USER"/>
            <user name="admin" password="adminPass" authorities="ROLE_ADMIN"/>
        </user-service>
    </authentication-provider>
</authentication-manager>
<bean name="passwordEncoder" 
  class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>

And:

还有。

<http>
    <intercept-url pattern="/auth/admin/*" access="hasAuthority('ADMIN')"/>
    <intercept-url pattern="/auth/*" access="hasAnyAuthority('ADMIN','USER')"/>
</http>

4.3. permitAll, denyAll

4.3.permitAll, denyAll

These two annotations are also quite straightforward. We may either permit or deny access to some URL in our service.

这两个注释也是很直接的。我们可以允许或拒绝对我们服务中的一些URL的访问。

Let’s have a look at the example:

让我们看一下这个例子。

...
.antMatchers("/*").permitAll()
...

With this config, we’ll authorize all users (both anonymous and logged in) to access the page starting with ‘/’ (for example, our homepage).

有了这个配置,我们将授权所有用户(包括匿名用户和登录用户)访问以”/”开头的页面(例如,我们的主页)。

We can also deny access to our entire URL space:

我们还可以拒绝对我们整个URL空间的访问。

...
.antMatchers("/*").denyAll()
...

And again, we can do the same configuration with XML as well:

同样,我们也可以用XML做同样的配置。

<http auto-config="true" use-expressions="true">
    <intercept-url access="permitAll" pattern="/*" /> <!-- Choose only one -->
    <intercept-url access="denyAll" pattern="/*" /> <!-- Choose only one -->
</http>

4.4. isAnonymous, isRememberMe, isAuthenticated, isFullyAuthenticated

4.4. isAnonymous, isRememberMe, isAuthenticated, isFullyAuthenticated

In this subsection, we’ll focus on expressions related to the login status of the user. Let’s start with a user that didn’t log in to our page. By specifying the following in the Java config, we’ll enable all unauthorized users to access our main page:

在本小节中,我们将重点讨论与用户的登录状态有关的表达式。让我们从一个没有登录到我们页面的用户开始。通过在Java配置中指定以下内容,我们将使所有未经授权的用户能够访问我们的主页面。

...
.antMatchers("/*").anonymous()
...

Here’s the same in XML config:

下面是XML配置中的相同内容。

<http>
    <intercept-url pattern="/*" access="isAnonymous()"/>
</http>

If we want to secure the website so that everyone who uses it needs to log in, we’ll need to use the isAuthenticated() method:

如果我们想确保网站的安全,使每个使用它的人都需要登录,我们就需要使用isAuthenticated() 方法。

...
.antMatchers("/*").authenticated()
...

Or we can use the XML version:

或者我们可以使用XML版本。

<http>
    <intercept-url pattern="/*" access="isAuthenticated()"/>
</http>

We also have two additional expressions, isRememberMe() and isFullyAuthenticated(). Through the use of cookies, Spring enables remember-me capabilities, so there’s no need to log into the system each time. We can read more about Remember Me here.

我们还有两个额外的表达式:isRememberMe()isFullyAuthenticated()。通过使用cookies,Spring实现了记住我的功能,所以不需要每次都登录到系统中。我们可以在这里阅读更多关于Remember Me的内容

In order to give access to users that were logged in by the remember me function, we can use:

为了让那些通过记住我的功能登录的用户访问,我们可以使用。

...
.antMatchers("/*").rememberMe()
...

We can also use the XML version:

我们也可以使用XML版本。

<http>
    <intercept-url pattern="*" access="isRememberMe()"/>
</http>

Finally, some parts of our services require the user to be authenticated again, even if the user is already logged in. For example, let’s say a user wants to change the settings or payment information; it’s good practice to ask for manual authentication in the more sensitive areas of the system.

最后,我们服务的某些部分需要用户再次进行认证,即使用户已经登录了。例如,假设一个用户想改变设置或支付信息;在系统中比较敏感的地方,要求手动认证是很好的做法。

In order to do this, we can specify isFullyAuthenticated(), which returns true if the user isn’t an anonymous or remember-me user:

为了做到这一点,我们可以指定isFullyAuthenticated(),如果用户不是匿名或记住我的用户,则返回true

...
.antMatchers("/*").fullyAuthenticated()
...

Here’s the XML version:

这里是XML版本。

<http>
    <intercept-url pattern="*" access="isFullyAuthenticated()"/>
</http>

4.5. principal, authentication

4.5.主要的,认证

These expressions allow us to access the principal object representing the current authorized (or anonymous) user and the current Authentication object from the SecurityContext, respectively.

这些表达式允许我们分别从SecurityContext访问代表当前授权(或匿名)用户的principal对象和当前Authentication对象。

We can, for example, use principal to load a user’s email, avatar, or any other data that’s accessible from the logged-in user.

例如,我们可以使用principal加载一个用户的电子邮件、头像或任何其他可以从登录用户那里获得的数据。

And authentication provides information about the full Authentication object, along with its granted authorities.

Authentication提供了关于完整的Authentication对象的信息,以及其授予的权限。

Both of these expressions are described in further detail in the article Retrieve User Information in Spring Security.

Retrieve User Information in Spring Security一文中对这两个表达式进行了进一步的详细描述。

4.6. hasPermission APIs

4.6.hasPermissionAPIs

This expression is documented, and intended to be a bridge between the expression system and Spring Security’s ACL system, allowing us to specify authorization constraints on individual domain objects based on abstract permissions.

该表达式是文档化的,旨在成为表达式系统和Spring Security的ACL系统之间的桥梁,允许我们根据抽象权限对单个域对象指定授权约束。

Let’s look at an example. Imagine that we have a service that allows cooperative article writing, with a main editor who decides which articles proposed by the authors should be published.

让我们看一个例子。想象一下,我们有一个允许合作写文章的服务,有一个主要的编辑,决定哪些由作者提出的文章应该发表。

In order to allow the use of such a service, we can create the following methods with access control methods:

为了允许使用这样的服务,我们可以用访问控制方法创建以下方法。

@PreAuthorize("hasPermission(#articleId, 'isEditor')")
public void acceptArticle(Article article) {
   …
}

Only the authorized user can call this method, and they need to have isEditor permission in the service.

只有被授权的用户可以调用这个方法,而且他们需要在服务中拥有isEditor 权限。

We also need to remember to explicitly configure a PermissionEvaluator in our application context, where customInterfaceImplementation will be the class that implements PermissionEvaluator:

我们还需要记住在我们的应用上下文中明确配置一个PermissionEvaluator,其中customInterfaceImplementation将是实现PermissionEvaluator的类。

<global-method-security pre-post-annotations="enabled">
    <expression-handler ref="expressionHandler"/>
</global-method-security>

<bean id="expressionHandler"
    class="org.springframework.security.access.expression
      .method.DefaultMethodSecurityExpressionHandler">
    <property name="permissionEvaluator" ref="customInterfaceImplementation"/>
</bean>

Of course, we can also do this with Java configuration as well:

当然,我们也可以用Java配置做到这一点。

@Override
protected MethodSecurityExpressionHandler expressionHandler() {
    DefaultMethodSecurityExpressionHandler expressionHandler = 
      new DefaultMethodSecurityExpressionHandler();
    expressionHandler.setPermissionEvaluator(new CustomInterfaceImplementation());
    return expressionHandler;
}

5. Conclusion

5.结论

This article is a comprehensive introduction and guide to Spring Security Expressions.

本文是对Spring Security Expressions的全面介绍和指导。

All of the examples discussed here are available on the GitHub project.

这里讨论的所有示例都可以在GitHub项目上获得