Spring Security - 注销



注销是一个重要的功能,它要求用户在注销或退出,或其当前会话因任何原因失效后,必须登录才能访问任何受保护的资源。

Spring Security 提供了默认的注销功能,正如我们在Spring Security - 表单登录章节中所看到的。

注销功能执行以下重要功能。

  • 使 Http 会话失效,并解除绑定到会话的对象。

  • 它清除“记住我”cookie。

  • 从 Spring 的 Security 上下文中删除身份验证。

注销可以明确配置如下:

protected void configure(HttpSecurity http) throws Exception {
http 
   // ... 
   .logout(config -> config  
      .logoutUrl("/logout") 
      .logoutSuccessUrl("/login")) 
   .build();
}

重要方法

以下是我们可以在 logout() 方法中配置的重要方法。

  • logoutUrl ("/logout") − 这将用于注销我们的应用程序。它的默认值为 logout,可以更改为任何其他自定义值。

  • logoutSuccessUrl ("/login") − 用户成功注销后,将使用此 URL 加载登录页面。

  • invalidateHttpSession ("true") − 用于使会话失效。默认情况下,它为 true,因此当用户注销时,其会话将失效。我们可以将其标记为 false 以便即使在注销后也保持会话活动。

  • deleteCookies ("JSESSIONID") − 用于清除“记住我”cookie。

  • logoutSuccessHandler(logoutSuccessHandler()); − 当我们需要在用户注销应用程序时执行某些操作时,使用此方法。

让我们开始使用 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")
            .invalidateHttpSession(true)
            .deleteCookies("JSESSIONID"))
         .build();
   }   
}

在这里,我们提到了要用于注销的 logout url,这是 Spring Security 提供的默认 url。我们不需要为此创建一个特殊的注销页面。同样,一旦用户注销,用户将看到登录页面,这是标准做法。

控制器类

在这个类中,我们为 "/" 端点和 "/login" 端点创建了映射,分别用于该应用程序的索引页面和登录页面。

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"; 
   }
}

视图

让我们在/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 <span sec:authentication="name"></span>!</h1> 
      <form th:action="@{/logout}" method="post"> 
         <input type="submit" value="Sign Out"/> 
      </form>
   </body> 
<html> 

在这里,我们使用了一个带有提交按钮的表单来注销用户。

<form th:action="@{/logout}" method="post"> 
   <input type="submit" value="Sign Out"/> 
</form>

我们也可以使用链接,如下所示:

<a href="/logout" alt="logout">Sign Out</a>

login.html

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

<!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">
         <div> 
            <h1>Please sign in</h1> 
            <label for="username">
               <b>Username</b>
            </label> 
            <input type="text" placeholder="Enter Username" name="username" id="username" required> 
            <label for="password"><b>Password</b></label> 
            <input type="password" placeholder="Enter Password" name="password" id="password" required> 
            <input type="submit"  value="Sign In" /> 
         </div> 
      </form> 
   </body>
</html>

在 login.html 中,我们使用${param.error}读取请求参数error。如果为真,则打印错误消息凭据错误。类似地,我们使用${param.logout}读取请求参数logout。如果为真,则打印注销消息。

在登录表单中,我们使用 POST 方法登录,同时使用名称和 ID 为usernamepassword的输入字段。

运行应用程序

由于我们所有组件都已准备好,让我们运行应用程序。右键单击项目,选择以...运行,然后选择Spring Boot 应用

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

输出

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

登录页面

Custom Login Form

主页

输入有效的凭据,它将加载主页。

Custom Login Form Success

注销后

现在单击注销按钮,它将再次加载登录页面。

Custom Login Form
广告
© . All rights reserved.