Securing Jakarta EE with Spring Security – 用Spring Security保证Jakarta EE的安全

最后修改: 2017年 8月 5日

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

1. Overview

1.概述

In this quick tutorial, we’ll be looking at how to secure a Jakarta EE web application with Spring Security.

在这个快速教程中,我们将研究如何用Spring Security保护Jakarta EE网络应用。

2. Maven Dependencies

2.Maven的依赖性

Let’s start with the required Spring Security dependencies for this tutorial:

让我们从本教程所需的Spring Security依赖项开始

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.3.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>4.2.3.RELEASE</version>
</dependency>

The latest Spring Security version (at the time of writing this tutorial) is 4.2.3.RELEASE; as always, we can check Maven Central for newest versions.

最新的Spring Security版本(在编写本教程时)是4.2.3.RELEASE;一如既往,我们可以查看Maven Central的最新版本。

3. Security Configuration

3.安全配置

Next, we need to set up the security configuration for the existing Jakarta EE application:

接下来,我们需要为现有的Jakarta EE应用程序设置安全配置。

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig 
  extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth)
      throws Exception {
        auth.inMemoryAuthentication()
          .withUser("user1").password("user1Pass").roles("USER")
          .and()
          .withUser("admin").password("adminPass").roles("ADMIN");
    }
}

In the configure() method, we setup the AuthenticationManager. For the sake of simplicity, we implement a simple in-memory authentication. User details are hard-coded.

configure()方法中,我们设置AuthenticationManager。为了简单起见,我们实现了一个简单的内存认证。用户的详细信息是硬编码的。

This is meant to be used for rapid prototyping when a full persistence mechanism is not necessary.

这是为了在不需要完整的持久化机制时用于快速原型设计。

Next, let’s integrate security into the existing system by adding the SecurityWebApplicationInitializer class:

接下来,让我们通过添加SecurityWebApplicationInitializer类将安全问题整合到现有系统中。

public class SecurityWebApplicationInitializer
  extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(SpringSecurityConfig.class);
    }
}

This class will ensure the SpringSecurityConfig is loaded during application startup. At this stage, we’ve achieved a basic implementation of Spring Security. With this implementation, Spring Security will require authentication for all requests and routes by default.

这个类将确保SpringSecurityConfig在应用程序启动时被加载。在这个阶段,我们已经实现了Spring Security的基本实现。有了这个实现,Spring Security将默认要求所有的请求和路由进行认证。

4. Configuring Security Rules

4.配置安全规则

We can further customize Spring Security by overriding WebSecurityConfigurerAdapter‘s configure(HttpSecurity http) method:

我们可以通过覆盖WebSecurityConfigurerAdapterconfigure(HttpSecurity http)方法进一步定制Spring Security。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf().disable()
      .authorizeRequests()
      .antMatchers("/auth/login*").anonymous()
      .anyRequest().authenticated()
      .and()
      .formLogin()
      .loginPage("/auth/login")
      .defaultSuccessUrl("/home", true)
      .failureUrl("/auth/login?error=true")
      .and()
      .logout().logoutSuccessUrl("/auth/login");
}

Using the antMatchers() method, we configure Spring Security to allow anonymous access to /auth/login and authenticate any other request.

使用antMatchers()方法,我们配置Spring Security以允许匿名访问/auth/login认证任何其他请求。

4.1. Custom Login Page

4.1.自定义登录页面

A custom login page is configured using the formLogin() method:

使用formLogin()方法配置了一个自定义的登录页面。

http.formLogin()
  .loginPage("/auth/login")

If this is not specified, Spring Security generates a default login page at /login:

如果没有指定,Spring Security会在/login生成一个默认的登录页面。

<html>
<head></head>
<body>
<h1>Login</h1>
<form name='f' action="/auth/login" method='POST'>
    <table>
        <tr>
            <td>User:</td>
            <td><input type='text' name='username' value=''></td>
        </tr>
        <tr>
            <td>Password:</td>
            <td><input type='password' name='password'/></td>
        </tr>
        <tr>
            <td><input name="submit" type="submit" 
              value="submit"/></td>
        </tr>
    </table>
</form>
</body>
</html>

4.2. Custom Landing Page

4.2.自定义着陆页

Upon successful login, Spring Security redirects the user to the root of the application. We can override this by specifying a default success URL:

登录成功后,Spring Security会将用户重定向到应用程序的根。我们可以通过指定一个默认的成功URL来覆盖这一点。

http.formLogin()
  .defaultSuccessUrl("/home", true)

By setting the defaultSuccessUrl() method’s alwaysUse parameter to true, a user will always be redirected to the specified page.

通过将defaultSuccessUrl()方法的alwaysUse参数设置为true,用户将始终被重定向到指定页面。

If the alwaysUse parameter is not set or is set to false, a user will be redirected to the previous page he tried to access before being prompted for authentication.

如果alwaysUse参数没有设置或设置为false,用户将被重定向到他试图访问的前一个页面,然后被提示验证。

Similarly, we can also specify a custom failure landing page:

同样地,我们也可以指定一个自定义的失败登陆页面。

http.formLogin()
  .failureUrl("/auth/login?error=true")

4.3. Authorization

4.3.授权

We can restrict access to a resource by role:

我们可以通过角色来限制对资源的访问。

http.formLogin()
  .antMatchers("/home/admin*").hasRole("ADMIN")

A non-admin user will receive an Access Denied error if he/she tries to access the /home/admin endpoint.

如果非管理员用户试图访问/home/admin端点,他/她将收到一个拒绝访问的错误。

We can also restrict data on a JSP page based on a user’s role. This is done using the <security:authorize> tag:

我们还可以根据用户的角色来限制JSP页面上的数据。这是用<security:authorize>标签完成的。

<security:authorize access="hasRole('ADMIN')">
    This text is only visible to an admin
    <br/>
    <a href="<c:url value="/home/admin" />">Admin Page</a>
    <br/>
</security:authorize>

To use this tag, we have to include the Spring Security tags taglib at the top of the page:

要使用这个标签,我们必须在页面的顶部包含Spring Security标签taglib。

<%@ taglib prefix="security" 
  uri="http://www.springframework.org/security/tags" %>

5. Spring Security XML Configuration

5.Spring Security XML配置

So far we have looked at configuring Spring Security in Java. Let’s take a look at an equivalent XML configuration.

到目前为止,我们已经研究了在Java中配置Spring Security的问题。让我们看一下同等的XML配置。

First, we need to create a security.xml file in the web/WEB-INF/spring folder that contains our XML configurations. An example of such a security.xml config file is available at the end of the article.

首先,我们需要在web/WEB-INF/spring文件夹中创建一个security.xml文件,其中包含我们的XML配置。这样一个security.xml配置文件的例子可在文章末尾找到。

Let’s start by configuring the authentication manager and authentication provider. For the sake of simplicity we use simple hard-coded user credentials:

让我们从配置认证管理器和认证提供者开始。为了简单起见,我们使用简单的硬编码的用户凭证。

<authentication-manager>
    <authentication-provider>
        <user-service>
            <user name="user" 
              password="user123" 
              authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

What we just did is to create a user with a username, password, and a role.

我们刚才所做的是创建一个具有用户名、密码和角色的用户。

Alternatively, we can configure our authentication provider with a password encoder:

另外,我们可以用密码编码器来配置我们的认证提供者。

<authentication-manager>
    <authentication-provider>
        <password-encoder hash="sha"/>
        <user-service>
            <user name="user"
              password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" 
              authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
</authentication-manager>

We can also specify a custom implementation of Spring’s UserDetailsService or a Datasource as our authentication provider. More details can be found here.

我们还可以指定Spring的UserDetailsServiceDatasource的自定义实现作为我们的认证提供者。更多细节可以在这里找到。

Now that we have configured the authentication manager, let’s setup the security rules and apply access control:

现在我们已经配置了认证管理器,让我们设置安全规则并应用访问控制。

<http auto-config='true' use-expressions="true">
    <form-login default-target-url="/secure.jsp" />
    <intercept-url pattern="/" access="isAnonymous()" />
    <intercept-url pattern="/index.jsp" access="isAnonymous()" />
    <intercept-url pattern="/secure.jsp" access="hasRole('ROLE_USER')" />
</http>

In the above snippet, we have configured HttpSecurity to use form login and have set /secure.jsp as the login success URL. We granted anonymous access to /index.jsp and the “/” path. Also, we specified that access to /secure.jsp should require authentication and an authenticated user should have, at least, the ROLE_USER level of authority.

在上面的片段中,我们将HttpSecurity配置为使用表单登录,并将/secure.jsp设为登录成功的URL。我们授予匿名访问/index.jsp“/”路径。此外,我们还规定,访问/secure.jsp应该需要认证,而且认证用户至少应该有ROLE_USER级别的权限。

Setting the auto-config attribute of the http tag to true instructs Spring Security to implement default behaviors that we don’t have to override in the configuration. Therefore, /login and /logout will be used for user login and logout respectively. A default login page is provided as well.

http标签的auto-config属性设置为true,指示Spring Security实现默认行为,我们不必在配置中重写。因此,/login/logout将分别用于用户登录和注销。同时还提供了一个默认的登录页面。

We can further customize the form-login tag with custom login and logout pages, URLs to handle both authentication failure and success. The Security Namespace appendix lists all the possible attributes for the form-login (and other) tags. Some IDEs also make inspection possible by clicking on a tag while pressing down the ctrl key.

我们可以进一步定制form-login标签,用自定义的登录和注销页面、URL来处理认证失败和成功。安全命名空间附录列出了form-login(和其他)标签的所有可能属性。一些IDE也可以通过点击标签同时按下ctrl键来进行检查。

Finally, for the security.xml config to be loaded during application startup, we need to add the following definitions to our web.xml:

最后,为了使security.xml配置在应用程序启动时被加载,我们需要在web.xml中添加以下定义。

<context-param>                                                                           
    <param-name>contextConfigLocation</param-name>                                        
    <param-value>                                                                         
      /WEB-INF/spring/*.xml                                                             
    </param-value>                                                                        
</context-param>                                                                          
                                                                                          
<filter>                                                                                  
    <filter-name>springSecurityFilterChain</filter-name>                                  
    <filter-class>
      org.springframework.web.filter.DelegatingFilterProxy</filter-class>     
</filter>                                                                                 
                                                                                          
<filter-mapping>                                                                          
    <filter-name>springSecurityFilterChain</filter-name>                                  
    <url-pattern>/*</url-pattern>                                                         
</filter-mapping>                                                                         
                                                                                          
<listener>                                                                                
    <listener-class>
        org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

Note that trying to use both XML and Java based configurations in the same JEE application may cause errors.

注意,试图在同一个JEE应用程序中同时使用基于XML和Java的配置可能会导致错误。

6. Conclusion

6.结论

In this article, we have seen how to secure a Jakarta EE application with Spring Security and demonstrated both Java-based and XML-based configurations.

在这篇文章中,我们看到了如何用Spring Security保护Jakarta EE应用程序,并演示了基于Java和基于XML的配置。

We also discussed ways to grant or revoke access to specific resources based on a user’s role.

我们还讨论了如何根据用户的角色授予或撤销对特定资源的访问。

The complete source code and XML definitions are available over on GitHub.

完整的源代码和XML定义可在GitHub上获得