二、Spring Reactive Security自定义登录页

发布时间 2023-06-05 20:58:38作者: shigp1

添加配置类:

@Configuration
public class MyReactiveSecurityConfig {

    @Bean
    public ReactiveUserDetailsService reactiveUserDetailsService() {
        UserDetails user = User.withUsername("user")
                .password("12345")
                .roles("USER")
                .build();
        return new MapReactiveUserDetailsService(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers("/doLogin").permitAll()
                        .anyExchange().authenticated()
                )
                .formLogin()
                .loginPage("/doLogin")
                .and()
                .csrf()
                .disable();
        return http.build();
    }
}

loginPage是配置登录页面和登录接口的,我找了下api,没看到单独设置登录接口的api。同时pathMatchers("/doLogin").permitAll()设置doLogin允许任何人访问。

 

添加thymeleaf依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

在application.properties配置thymeleaf:

spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#设置thymeleaf页面的后缀
spring.thymeleaf.suffix=.html
#设置thymeleaf页面的存储路径
spring.thymeleaf.prefix=classpath:/templates/

 
在resources目录templates下新建doLogin.html:

  <!DOCTYPE html>
  <html lang="en">
  <!-- https://codepen.io/danielkvist/pen/LYNVyPL -->
  <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <style>
          :root {
              /* COLORS */
              --white: #e9e9e9;
              --gray: #333;
              --blue: #0367a6;
              --lightblue: #008997;

              /* RADII */
              --button-radius: 0.7rem;

              /* SIZES */
              --max-width: 758px;
              --max-height: 420px;

              font-size: 16px;
              font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
              Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
          }

          body {
              align-items: center;
              background-color: var(--white);
              background: url("https://res.cloudinary.com/dbhnlktrv/image/upload/v1599997626/background_oeuhe7.jpg");
              /* 决定背景图像的位置是在视口内固定,或者随着包含它的区块滚动。 */
              /* https://developer.mozilla.org/zh-CN/docs/Web/CSS/background-attachment */
              background-attachment: fixed;
              background-position: center;
              background-repeat: no-repeat;
              background-size: cover;
              display: grid;
              height: 100vh;
              place-items: center;
          }

          .form__title {
              font-weight: 300;
              margin: 0;
              margin-bottom: 1.25rem;
          }

          .link {
              color: var(--gray);
              font-size: 0.9rem;
              margin: 1.5rem 0;
              text-decoration: none;
          }

          .container {
              background-color: var(--white);
              border-radius: var(--button-radius);
              box-shadow: 0 0.9rem 1.7rem rgba(0, 0, 0, 0.25),
              0 0.7rem 0.7rem rgba(0, 0, 0, 0.22);
              height: var(--max-height);
              max-width: var(--max-width);
              overflow: hidden;
              position: relative;
              width: 100%;
          }

          .container__form {
              height: 100%;
              position: absolute;
              top: 0;
              transition: all 0.6s ease-in-out;
          }

          .container--signin {
              left: 0;
              width: 50%;
              z-index: 2;
          }

          .container.right-panel-active .container--signin {
              transform: translateX(100%);
          }

          .container--signup {
              left: 0;
              opacity: 0;
              width: 50%;
              z-index: 1;
          }

          .container.right-panel-active .container--signup {
              animation: show 0.6s;
              opacity: 1;
              transform: translateX(100%);
              z-index: 5;
          }

          .container__overlay {
              height: 100%;
              left: 50%;
              overflow: hidden;
              position: absolute;
              top: 0;
              transition: transform 0.6s ease-in-out;
              width: 50%;
              z-index: 100;
          }

          .container.right-panel-active .container__overlay {
              transform: translateX(-100%);
          }

          .overlay {
              background-color: var(--lightblue);
              background: url("https://cdn.pixabay.com/photo/2018/08/14/13/23/ocean-3605547_1280.jpg");
              background-attachment: fixed;
              background-position: center;
              background-repeat: no-repeat;
              background-size: cover;
              height: 100%;
              left: -100%;
              position: relative;
              transform: translateX(0);
              transition: transform 0.6s ease-in-out;
              width: 200%;
          }

          .container.right-panel-active .overlay {
              transform: translateX(50%);
          }

          .overlay__panel {
              align-items: center;
              display: flex;
              flex-direction: column;
              height: 100%;
              justify-content: center;
              position: absolute;
              text-align: center;
              top: 0;
              transform: translateX(0);
              transition: transform 0.6s ease-in-out;
              width: 50%;
          }

          .overlay--left {
              transform: translateX(-20%);
          }

          .container.right-panel-active .overlay--left {
              transform: translateX(0);
          }

          .overlay--right {
              right: 0;
              transform: translateX(0);
          }

          .container.right-panel-active .overlay--right {
              transform: translateX(20%);
          }

          .btn {
              background-color: var(--blue);
              background-image: linear-gradient(90deg, var(--blue) 0%, var(--lightblue) 74%);
              border-radius: 20px;
              border: 1px solid var(--blue);
              color: var(--white);
              cursor: pointer;
              font-size: 0.8rem;
              font-weight: bold;
              letter-spacing: 0.1rem;
              padding: 0.9rem 4rem;
              text-transform: uppercase;
              transition: transform 80ms ease-in;
          }

          .form>.btn {
              margin-top: 1.5rem;
          }

          .btn:active {
              transform: scale(0.95);
          }

          .btn:focus {
              outline: none;
          }

          .form {
              background-color: var(--white);
              display: flex;
              align-items: center;
              justify-content: center;
              flex-direction: column;
              padding: 0 3rem;
              height: 100%;
              text-align: center;
          }

          .input {
              background-color: #fff;
              border: none;
              padding: 0.9rem 0.9rem;
              margin: 0.5rem 0;
              width: 100%;
          }

          @keyframes show {

              0%,
              49.99% {
                  opacity: 0;
                  z-index: 1;
              }

              50%,
              100% {
                  opacity: 1;
                  z-index: 5;
              }
          }
      </style>
  </head>

  <body>
  <div class="container right-panel-active">

      <!-- Sign In -->
      <div class="container__form container--signin">
          <form action="/doLogin" class="form" id="form2" method="post">
              <h2 class="form__title">Sign In</h2>
              <input type="text" placeholder="username" class="input" name="username"/>
              <input type="password" placeholder="Password" class="input" name="password"/>
              <a href="#" class="link">Forgot your password?</a>
              <button class="btn">Sign In</button>
          </form>
      </div>

      <!-- Overlay -->
      <div class="container__overlay">
          <div class="overlay">
              <div class="overlay__panel overlay--left">
                  <button class="btn" id="signIn">Sign In</button>
              </div>
              <div class="overlay__panel overlay--right">
                  <button class="btn" id="signUp">Sign Up</button>
              </div>
          </div>
      </div>
  </div>

  <script>
      const signInBtn = document.getElementById("signIn");
      const signUpBtn = document.getElementById("signUp");
      const fistForm = document.getElementById("form1");
      const secondForm = document.getElementById("form2");
      const container = document.querySelector(".container");

      signInBtn.addEventListener("click", () => {
        container.classList.remove("right-panel-active");
      });

      signUpBtn.addEventListener("click", () => {
        container.classList.add("right-panel-active");
      });

      fistForm.addEventListener("submit", (e) => e.preventDefault());
      secondForm.addEventListener("submit", (e) => e.preventDefault());

    </script>
  </body>

  </html>

登录页面来自于:https://blog.csdn.net/bhegi_seg/article/details/123394504。注意:表单提交的地址是/doLogin

 
新增controller处理doLogin地址到页面的映射:

@Controller
public class LoginController {

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