Code ví dụ Spring Boot Multiple Login Page (Form Login)

Code ví dụ Spring Boot Multiple Login Page (Form Login)

(Xem lại: Spring MVC – Security dùng nhiều trang login (XML Config))

Các công nghệ sử dụng:

Tạo Spring Boot Project

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Code ví dụ Spring Boot Nhiều form login

Ví dụ Spring Boot nhiều trang login

Cấu trúc Project

Code ví dụ Spring Boot Multiple Login Page (Form Login)

File Controller:

package stackjava.com.sbmultipleformlogin.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class BaseController {

  @RequestMapping("/login1")
  public String login1(@RequestParam(required = false) String message, final Model model) {
    if (message != null && !message.isEmpty()) {
      if (message.equals("logout")) {
        model.addAttribute("message", "Logout!");
      }
      if (message.equals("error")) {
        model.addAttribute("message", "Login Failed!");
      }
    }
    return "login1";
  }

  @RequestMapping("/login2")
  public String login2(@RequestParam(required = false) String message, final Model model) {
    if (message != null && !message.isEmpty()) {
      if (message.equals("logout")) {
        model.addAttribute("message", "Logout!");
      }
      if (message.equals("error")) {
        model.addAttribute("message", "Login Failed!");
      }
    }
    return "login2";
  }

  @RequestMapping("/")
  public String index() {
    return "index";
  }

  @RequestMapping("/admin/admin")
  public String admin() {
    return "admin";
  }

  @RequestMapping("/user")
  public String user() {
    return "user";
  }
  
  @RequestMapping("/403")
  public String accessDenied403() {
    return "403";
  }

}

Các file config security:

package stackjava.com.sbmultipleformlogin.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
@Order(1)
public class AdminSecurityConfig extends WebSecurityConfigurerAdapter {

  @Bean
  public BCryptPasswordEncoder passwordEncoder() {
    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    return bCryptPasswordEncoder;
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    // account kai/admin1234
    auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()).withUser("kai")
        .password("$2a$04$PQtbtSAHZgBXLN.K/gZ3/eomQtZkB8R7x03KqZJxOTAbLRkxD9jgC").roles("ADMIN");

    // account sena/123456
    auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()).withUser("sena")
        .password("$2a$04$Q2Cq0k57zf2Vs/n3JXwzmerql9RzElr.J7aQd3/Sq0fw/BdDFPAj.").roles("USER");
    // auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance()).withUser("sena").password("123456").roles("USER");
  }

  protected void configure(HttpSecurity http) throws Exception {

    // Cấu hình security/form login cho url /admin/**
    // Cấu hình cho Login Form.
    http.antMatcher("/admin/**").authorizeRequests().antMatchers("/admin/**").hasRole("ADMIN").and().formLogin()//
        .loginProcessingUrl("/admin/j_spring_security_login")//
        .loginPage("/login1")//
        .defaultSuccessUrl("/admin/admin")//
        .failureUrl("/login1?message=error")//
        .usernameParameter("username")//
        .passwordParameter("password")
        .and().exceptionHandling().accessDeniedPage("/403")
        .and().logout().logoutUrl("/admin/j_spring_security_logout").logoutSuccessUrl("/login1?message=logout");
  }

}
package stackjava.com.sbmultipleformlogin.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
@Order(2)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Bean
  public BCryptPasswordEncoder passwordEncoder() {
    BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
    return bCryptPasswordEncoder;
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    
    // account kai/123456
    auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()).withUser("kai")
        .password("$2a$04$Q2Cq0k57zf2Vs/n3JXwzmerql9RzElr.J7aQd3/Sq0fw/BdDFPAj.").roles("ADMIN");

    // account sena/123456
    auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()).withUser("sena")
        .password("$2a$04$Q2Cq0k57zf2Vs/n3JXwzmerql9RzElr.J7aQd3/Sq0fw/BdDFPAj.").roles("USER");
    // auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance()).withUser("sena").password("123456").roles("USER");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // Cấu hình cho Login Form.
    http.authorizeRequests().antMatchers("/user/**").hasRole("USER").and()
        .formLogin()//
        .loginProcessingUrl("/j_spring_security_login")//
        .loginPage("/login2")//
        .defaultSuccessUrl("/user")//
        .failureUrl("/login2?message=error")//
        .usernameParameter("username").passwordParameter("password")
        .and().exceptionHandling().accessDeniedPage("/403")
        .and().logout().logoutUrl("/j_spring_security_logout").logoutSuccessUrl("/login2?message=logout");
  }
}

Ở đây mình sử dụng 2 file config security để cấu hình cho 2 dạng url khác nhau.
File AdminSecurityConfig.java cấu hình security cho đường dẫn /admin/**; File SecurityConfig.java cấu hình cho các đường dẫn còn lại.

Để sử dụng nhiều file cấu hình security khác nhau ta extends class WebSecurityConfigurerAdapter  và sử dụng annotation @Order ở đầu mỗi class. Tham số trong annotation @Order sẽ thể hiện thứ tự được filter khi thực hiện security. Ví dụ AdminSecurityConfig.java được đánh dấu là @Order(1) còn SecurityConfig.java được đánh dấu là @Order(2) nên khi thực hiện filter security nó sẽ kiểm tra bằng class AdminSecurityConfig.java trước.

*Lưu ý: class AdminSecurityConfig.java chỉ áp dụng cho url /admin/** nên ta sẽ dùng http.antMatcher(“/admin/**”)  (antMatcher chứ không phải là antMatchers nhé) nó tương đương với thẻ <http pattern=“/admin/**”>

Các file views:

<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security</title>
</head>
<body>
  <h2>Demo Spring Boot - Multiple Form Login</h2>
  <a th:href="@{/admin/admin}">admin page</a> <br />
  <a th:href="@{/user}">user page</a>

</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Security</title>
</head>
<body>
  <h2>Form Login1</h2>
  <form name='login-form' th:action="@{/admin/j_spring_security_login}" method='POST'>
    <table>
      <tr>
        <td>Username:</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>
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
  </form>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Security</title>
</head>
<body>
  <h2>Form Login2</h2>
  <form name='login-form' th:action="@{/j_spring_security_login}" method='POST'>
    <table>
      <tr>
        <td>Username:</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>
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
  </form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security</title>
</head>
<body>
  <h2>User Page</h2>
  <h3>
    Welcome : <span th:utext="${#request.userPrincipal.name}"></span>
  </h3>
  <br/>
  <form th:action="@{/j_spring_security_logout}" method="post">
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
    <input type="submit" value="Logout" />
  </form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security</title>
</head>
<body>
  <h2>Admin Page</h2>
  <h3>
    Welcome : <span th:utext="${#request.userPrincipal.name}"></span>
  </h3>
  <br/>
  <form th:action="@{/admin/j_spring_security_logout}" method="post">
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
    <input type="submit" value="Logout" />
  </form>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<title>Spring Boot Security</title>
</head>
<body>
  <h2>Access Denied - 403</h2>
  <p>You do not have permission to access this page</p>
  <a th:href="@{/}">Back To Home Page</a>

</body>
</html>

Demo:

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Đăng nhập trang /admin/admin bằng tài khoản kai/123456 hoặc kai/admin1234 (hệ thống sẽ hiện trang login1)

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Logout thì sẽ trở về trang login1,  sau đấy mình đăng nhập bằng tài khoản sena/123456

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Vì trang login1 sau khi login thành công sẽ chuyển tới trang /admin/admin mà tài khoản sena/123456 không có role admin nên sẽ không thể truy cập.

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Truy cập trang /user bằng tài khoản sena/123456 và logout (sẽ trở về trang login2 chứ không phải là trang login1)

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Code ví dụ Spring Boot Multiple Login Page (Form Login)

Code ví dụ Spring Boot Multiple Login Page (Form Login) stackjava.com

Okay, Done!

Download code ví dụ trên tại đây.

 

References:

https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle

stackjava.com