Spring Security 自定义登陆页面

原文链接: Spring Security Custom Login Page - 原文作者: Ramesh Fadatare
本文采用的是意译的方式

在这个 Spring Security 教程中,我们将学到怎么创建一个自定义登陆页面来实现 Spring Security 基于表单的验证。

译者验证的环境如下:

  • macOs Monterey 12.4 (Apple M1)

  • IntelliJ IDEA 2021.2.2(Ultimate Edition)

  • java --version (17.0.7)

  • maven version 3.9.2

  • Google Chrome 版本 119.0.6045.123(正式版本)(arm64)

默认的,Spring Security 提供了默认的登陆表单来保证应用的安全。但是,大多数时候,我们需要根据自身需求来自定义登陆页面。

Maven 依赖

添加下面的两个 Maven 依赖到你的 Spring Boot 项目中:

xml 复制代码
<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>

译者补充:是的,你的项目确保你安装了依赖 spring web,如下👇

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Spring Security 配置

很多生产环境的应用程序都需要自定义登陆表单。下面的配置案例展示了怎么设定一个自定义的表单。

java 复制代码
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.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;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
public class SpringSecurityConfig {

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

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeHttpRequests((authorize) ->
                        authorize.anyRequest().authenticated()
                ).formLogin(
                        form -> form
                                .loginPage("/login")
                                .loginProcessingUrl("/login")
                                .defaultSuccessUrl("/welcome")
                                .permitAll()
                ).logout(
                        logout -> logout
                                .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
                                .permitAll()
                );
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(){

        UserDetails ramesh = User.builder()
                .username("ramesh")
                .password(passwordEncoder().encode("password"))
                .roles("USER")
                .build();

        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("admin"))
                .roles("ADMIN")
                .build();

        return new InMemoryUserDetailsManager(ramesh, admin);
    }
}

当在 Spring Security 配置中设定登陆页面,那么我们就需要提供相应的渲染(登陆)页面。

Thymeleaf 模版 - 自定义登陆页面

下面的 Thymeleaf 模版提供了份登陆表单,整合在登陆页面的 /login

Login Form - src/main/resources/templates/login.html

html 复制代码
<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
>
<head>
    <meta charset="UTF-8">
    <title>Login System</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
          crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container-fluid">
        <a class="navbar-brand" th:href="@{/index}">Spring Security Custom Login Example</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
    </div>
</nav>
<br /><br />
<div class="container">
    <div class="row">
        <div class="col-md-6 offset-md-3">

            <div th:if="${param.error}">
                <div class="alert alert-danger">Invalid Email or Password</div>
            </div>
            <div th:if="${param.logout}">
                <div class="alert alert-success"> You have been logged out.</div>
            </div>

            <div class="card">
                <div class="card-header">
                    <h2 class="text-center">Login Form</h2>
                </div>
                <div class="card-body">
                    <form
                            method="post"
                            role="form"
                            th:action="@{/login}"
                            class="form-horizontal"
                    >
                        <div class="form-group mb-3">
                            <label class="control-label"> Email</label>
                            <input
                                    type="text"
                                    id="username"
                                    name="username"
                                    class="form-control"
                                    placeholder="Enter email address"
                            />
                        </div>

                        <div class="form-group mb-3">
                            <label class="control-label"> Password</label>
                            <input
                                    type="password"
                                    id="password"
                                    name="password"
                                    class="form-control"
                                    placeholder="Enter password"
                            />
                        </div>
                        <div class="form-group mb-3">
                            <button type="submit" class="btn btn-primary" >Submit</button>
                            <span> Not registered ?
                                <a th:href="@{/register}">Register/Signup here</a>
                            </span>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

关于这个自定义登陆表单,有几个关键点,如下:

  • 表单应该触发 /loginpost 接口
  • 表单应该在参数中指定名为 username 的用户名
  • 表单应该在参数中指定名为 password 的密码
  • 如果 HTTP 参数名为 error 出现,则表明用户提供的用户名或者密码无效
  • 如果 HTTP 参数名为 logout 出现,则表明用户成功退出
  • 很多用户不需要太多自定义用户登陆页面。然而,如果需要,我们可以使用额外的配置自定义想要的内容

Spring MVC Controller

让我们在 Spring MVC 中创建一个 /loginGET 方法来渲染登陆模版:

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class WelComeController {

    @GetMapping("/welcome")
    public String greeting() {
        return "welcome";
    }

    @GetMapping("/login")
    public String login(){
        return "login";
    }
}

当然,我们也需要创建一个方法来处理 welcomeThymeleaf 模版页面。

Thymeleaf 模版 - welcome.html

一旦用户成功登陆,那么欢迎页面将会展示出来:

html 复制代码
<!DOCTYPE html>
<html lang="en"
      xmlns:th="http://www.thymeleaf.org"
>
<head>
    <meta charset="UTF-8">
    <title>Registration and Login System</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
          crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <div class="container-fluid">
        <a class="navbar-brand" th:href="@{/index}">Spring Security Custom Login Example</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                    <a class="nav-link active" aria-current="page" th:href="@{/logout}">Logout</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<br /><br />

<body>
<div class="container">
    <div class="row">
        <h1> Welcome to Spring Security world!</h1>
    </div>
</div>

</body>
</html>

使用浏览器测试自定义登陆页面

在浏览器中输入 http://localhost:8080URL 地址,它会自动导航到登陆页面。接下来,我们输入用户名/密码 admin/admin,然后点击登陆按钮:

登陆成功后,你将看到下面的网页:

内置的退出特性

Spring Security 也提供了内置的 logout 退出特性。我们点击应用中的退出按钮以登出应用:

总结

在这篇 Spring Security 教程中,我们学到了如何应用 Spring Security 基于表单的验证来自定义登陆页面。

相关推荐
日升_rs1 分钟前
Chrome 134 版本新特性
前端·chrome·浏览器
前端卧龙人2 分钟前
前端如何最小化重绘和回流
前端
南蓝3 分钟前
【React】React 19 新特性
前端
银之夏雪4 分钟前
深入理解 GPU 渲染加速与合成层(Composite Layers)
前端·javascript·浏览器
机巧咸鱼不会受伤5 分钟前
为什么浏览器的渲染进程不适用于多个线程来处理
前端·浏览器
前端日常开发5 分钟前
轻松合并远端两个Commit,代码历史秒变整洁!
前端
Au_ust5 分钟前
React:类组件(上)
前端·javascript·react.js
法欧特斯卡雷特5 分钟前
Kotlin Sequence 真的如此不堪吗?
android·后端·编程语言
神秘的t6 分钟前
javaEE初阶————多线程进阶(2)
java·开发语言
前端日常开发6 分钟前
前端并发请求太多?教你几招轻松搞定!
前端