Spring Security - 标签库



在使用 JSP 的 Spring MVC 应用程序中,我们可以使用 Spring Security 标签来应用安全约束以及访问安全信息。Spring Security 标签库为这些操作提供了基本支持。使用这些标签,我们可以根据用户的角色或权限控制向用户显示的信息。此外,我们还可以将 CSRF 保护功能包含到我们的表单中。

要使用 Spring Security 标签,我们必须在 JSP 文件中声明 security 标签库。

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

现在,我们可以使用前缀为 sec 的 Spring Security 标签。现在让我们看看标签的使用方法。

authorize 标签

我们将讨论的第一个标签是 authorize 标签。让我们查看一些使用示例。

<sec:authorize access="!isAuthenticated()"> Login </sec:authorize> 
<sec:authorize access="isAuthenticated()"> Logout </sec:authorize> 
<sec:authorize access="hasRole('ADMIN')"> Hello Admin. </sec:authorize>

如我们所见,我们可以使用此标签根据访问权限或角色隐藏或显示信息部分。

  • hasRole(ADMIN) − 如果当前用户具有 admin 角色,则评估结果为 true。

  • hasAnyRole(‘ADMIN’,’USER’) − 如果当前用户具有任何列出的角色,则评估结果为 true

  • isAnonymous() − 如果当前用户是匿名用户,则评估结果为 true

  • isRememberMe() − 如果当前用户是“记住我”用户,则评估结果为 true

  • isFullyAuthenticated() − 如果用户已通过身份验证,并且既不是匿名用户也不是“记住我”用户,则评估结果为 true

如我们所见,access 属性是指定 Web 安全表达式的属性。然后,Spring Security 会评估该表达式。评估通常委托给 SecurityExpressionHandler<FilterInvocation>,该处理器在应用程序上下文中定义。如果它返回 true,则用户可以访问该部分中给出的信息。

如果我们将 authorize 标签与 Spring Security 的权限评估器一起使用,我们还可以检查用户权限,如下所示:

<p sec:authorize="hasPermission(#domain,'read') or hasPermission(#domain,'write')">
   This content is visible to users who have read or write permission.
</p>

我们还可以允许或限制用户点击内容中的某些链接。

<a sec:authorize href="/admin">
   This content will only be visible to users who are authorized to send requests to the "/admin" URL. 
</agt;

authentication 标签

当我们想要访问存储在 Spring Security 上下文中的当前 Authentication 对象时,可以使用 authentication 标签。然后,我们可以使用它直接在 JSP 页面中呈现对象的属性。例如,如果我们想在页面中呈现 Authentication 对象的 principal 属性,可以按如下方式进行:

<p sec:authentication="name" />

csrfInput 标签

当启用 CSRF 保护时,可以使用 csrfInput 标签插入一个隐藏的表单字段,其中包含 CSRF 保护令牌的正确值。如果未启用 CSRF 保护,则此标签不会输出任何内容。

可以将该标签与其他输入字段一起放置在 HTML <form></form> 块中。但是,我们不能将该标签放置在 <form:form></form:form> 块中,因为 Spring Security 会自动在这些标签中插入 CSRF 表单字段,并且还会自动处理 Spring 表单。

<form method="post" action="/do/something"> 
   <sec:csrfInput /> 
   Username:<br /> 
   <input type="text" username="username" /> 
   ... 
</form>

csrfMetaTags 标签

可以使用此标签插入包含 CSRF 保护令牌表单字段和标头名称以及 CSRF 保护令牌值的元标记。这些元标记可用于在应用程序的 Javascript 中使用 CSRF 保护。但是,此标签仅在我们已在应用程序中启用 CSRF 保护时才有效,否则,此标签不会输出任何内容。

<html> 
   <head> 
      <title>CSRF Protection in Javascript</title> 
      <sec:csrfMetaTags /> 
      <script type="text/javascript" language="javascript"> 
         var csrfParam = $("meta[name='_csrf_param']").attr("content"); 
         var csrfToken = $("meta[name='_csrf']").attr("content");
      </script> 
   </head> 
   <body> 
      ... 
   </body> 
</html>

让我们开始使用 Spring Security 进行实际编程。在开始使用 Spring 框架编写示例之前,您必须确保已正确设置 Spring 环境,如 Spring Security - 环境设置 章节中所述。我们还假设您对 Spring Tool Suite IDE 有一定的了解。

现在让我们继续编写一个基于 Spring MVC 的应用程序,该应用程序由 Maven 管理,它将要求用户登录、对用户进行身份验证,然后提供使用 Spring Security 表单登录功能注销的选项。

使用 Spring Initializr 创建项目

Spring Initializr 是开始使用 Spring Boot 项目的好方法。它提供了一个易于使用的用户界面来创建项目、添加依赖项、选择 Java 运行时等。它生成一个骨架项目结构,下载后可以导入到 Spring Tool Suite 中,然后我们可以继续使用我们的现成项目结构。

我们选择一个 Maven 项目,将项目命名为 formlogin,Java 版本为 21。添加以下依赖项:

  • Spring Web

  • Spring Security

  • Thymeleaf

  • Spring Boot DevTools

Spring Initializr

Thymeleaf 是 Java 的模板引擎。它允许我们快速开发静态或动态网页以在浏览器中呈现。它具有极强的可扩展性,允许我们详细定义和自定义模板的处理过程。此外,我们可以通过点击此 链接 了解更多关于 Thymeleaf 的信息。

让我们继续生成项目并下载它。然后将其解压缩到我们选择的文件夹中,并使用任何 IDE 打开它。我将使用 Spring Tools Suite 4。它可以从 https://springframework.org.cn/tools 网站免费下载,并且针对 Spring 应用程序进行了优化。

包含所有相关依赖项的 pom.xml

让我们看一下我们的 pom.xml 文件。它应该看起来类似于以下内容:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>3.3.1</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.tutorialspoint.security</groupId>
   <artifactId>formlogin</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>formlogin</name>
   <description>Demo project for Spring Boot</description>
   <url/>
   <licenses>
      <license/>
   </licenses>
   <developers>
      <developer/>
   </developers>
   <scm>
      <connection/>
      <developerConnection/>
      <tag/>
      <url/>
   </scm>
   <properties>
      <java.version>21</java.version>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
         <groupId>org.thymeleaf.extras</groupId>
         <artifactId>thymeleaf-extras-springsecurity6</artifactId>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-devtools</artifactId>
         <scope>runtime</scope>
         <optional>true</optional>
      </dependency>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
      <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>
</project>

Spring Security 配置类

在我们的 config 包中,我们创建了 WebSecurityConfig 类。我们将使用此类进行我们的安全配置,因此让我们使用 @Configuration 注解和 @EnableWebSecurity 对其进行注释。因此,Spring Security 知道将此类视为一个配置类。如我们所见,Spring 使应用程序配置变得非常容易。

WebSecurityConfig

package com.tutorialspoint.security.formlogin.config; 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain; 

@Configuration 
@EnableWebSecurity
public class WebSecurityConfig  { 

   @Bean 
   protected UserDetailsService userDetailsService() {
      UserDetails user = User.builder()
         .username("user")
         .password(passwordEncoder().encode("user123"))
         .roles("USER")
         .build();
      UserDetails admin = User.builder()
         .username("admin")
         .password(passwordEncoder().encode("admin123"))
         .roles("USER", "ADMIN")
         .build();
      return new InMemoryUserDetailsManager(user, admin);
   }

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

   @Bean
   protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 
      return http
         .csrf(AbstractHttpConfigurer::disable)
         .authorizeHttpRequests(
            request -> request.requestMatchers("/login").permitAll()
            .requestMatchers("/**").authenticated()
         )
         .formLogin(form -> form.loginPage("/login")
            .defaultSuccessUrl("/")
            .failureUrl("/login?error=true")
            .permitAll())          
         .logout(config -> config  
         .logoutUrl("/logout") 
         .logoutSuccessUrl("/login")) 
         .build();
   }   
}

控制器类

在此类中,我们为 "/" 端点、“/login”和“/admin”分别创建了索引页面、登录页面和此应用程序的管理页面映射。

AuthController.java

package com.tutorialspoint.security.formlogin.controllers; 

import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.GetMapping; 

@Controller 
public class AuthController { 
   @GetMapping("/") 
   public String home() { 
      return "index"; 
   }
   @GetMapping("/login") 
   public String login() { 
      return "login"; 
   }
   @GetMapping("/admin") 
   public String admin() { 
      return "admin"; 
   }
}

视图

/src/main/resources/templates 文件夹中创建 index.html,其内容如下,作为主页。

index.html

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml" 
   xmlns:th="https://www.thymeleaf.org" 
   xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> 
   <head> 
      <title>
         Hello World!
      </title> 
   </head>
   <body> 
      <h1 th:inline="text">Hello World!</h1> 
      <form th:action="@{/logout}" method="post"> 
         <input type="submit" value="Sign Out"/> 
      </form>
   </body> 
<html> 

让我们在 /src/main/resources/templates 文件夹中创建 login.html,其内容如下,作为登录页面。我们使用默认名称 usernamepasswordremember-me 作为文本字段。如果使用其他名称,则需要在 Spring Security 配置类中设置相同的名称。

login.html

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml"      
   xmlns:th="https://www.thymeleaf.org" 
   xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3"> 
   <head> 
      <title>Spring Security Example</title> 
   </head> 
   <body> 
      <div th:if="${param.error}"> 
         <p>Bad Credentials</p>
      </div> 
      <div th:if="${param.logout}">You have been logged out.</div> 
      <form th:action="@{/login}" method="post">
         <h1>Please sign in</h1>
         <table>
            <tr>
               <td><label for="username"><b>Username</b></label></td>
               <td><input type="text" placeholder="Enter Username" name="username" id="username" required></td>
            </tr>
            <tr>
               <td><label for="password"><b>Password</b></label></td>
               <td><input type="password" placeholder="Enter Password" name="password" id="password" required></td>
            </tr>
            <tr>
               <td><label for="remember-me"><b>Remember Me</b></label> </td>
               <td><input type="checkbox" name="remember-me" /></td>
            </tr>
            <tr>
               <td> </td>
               <td><input type="submit"  value="Sign In" /></td>
            </tr>		
         </table>       
      </form> 
   </body>
</html>

让我们创建 admin.html 来使用 authorize 标签。在这里,我们添加了 <%@ taglib uri="http://www.springframework.org/security/tags" prefix="sec"%>。这将允许我们使用前面讨论的 Spring Security 标签库。

admin.html

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml" 
xmlns:th="https://www.thymeleaf.org" 
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6"> 
   <head> 
      <title>
         Hello Admin!
      </title> 
   </head>
   <body> 
      <p>Admin Console</p>
      <h1 th:inline="text">Welcome <span sec:authentication="name"></span>!</h1> 
      <p sec:authorize="hasRole('ADMIN')"> Control Panel </p> 
      <a href="/logout" alt="logout">Sign Out</a>
   </body> 
<html> 

如我们所见,我们在内容周围添加了 authorize 标签。此内容仅对我们的管理员用户可见。任何其他访问此页面的用户都将无法查看此内容。另一个标签 authentication 用于获取 principal 以检索已登录用户的用户名。

运行应用程序

一旦我们准备好所有组件,让我们运行应用程序。右键单击项目,选择 Run As,然后选择 Spring Boot App

它将启动应用程序,并且应用程序启动后,我们可以运行 localhost:8080 以检查更改。

输出

现在打开 localhost:8080,您可以看到我们的登录页面。

输入管理员详细信息的登录页面

Login Form with Admin credential

管理员页面

当我们输入管理员的有效凭据时,它将加载主页。现在打开 localhost:8080/admin。您可以看到管理员用户可以看到“控制面板”文本。

Admin Home Page

输入用户详细信息的登录页面

现在通过点击注销链接注销应用程序,然后使用用户凭据登录。

Login Form with User credential

用户的管理员页面

当我们输入用户的有效凭据时,它将加载主页。现在加载 localhost:8080/admin 以打开管理员页面。您可以看到普通用户看不到“控制面板”文本。

User Home Page
广告

© . All rights reserved.