Spring MVC – Security dùng nhiều trang login (multiple form login)

Spring MVC – Security dùng nhiều trang login (multiple form login).

(Xem thêm: Code ví dụ Spring Boot Multiple Login Page (Annotation Config))

Trong một số trường hợp, ứng dụng web của bạn sẽ cần nhiều hơn 1 trang login, có thể là một trang login riêng biệt dành cho người quản trị hệ thống hoặc một trang login đơn giản dành cho người dùng…

Ở bài này mình sẽ thực hiện code ví dụ Spring MVC – Security sử dụng 2 trang login. 1 trang login cho đường dẫn /admin/**. Một trang cho các đường dẫn còn lại.

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

Tạo Maven Project

Spring MVC - Security dùng nhiều trang login (multiple form login)

Thư viện sử dụng:

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>stackjava.com</groupId>
  <artifactId>SpringSecurityMultipleFormLogin</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <properties>
    <spring.version>5.0.2.RELEASE</spring.version>
    <spring.security.version>5.0.2.RELEASE</spring.security.version>
    <jstl.version>1.2</jstl.version>

  </properties>

  <dependencies>

    <!-- Spring MVC -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!-- Spring Security -->
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <version>${spring.security.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <version>${spring.security.version}</version>
    </dependency>

    <!-- JSP/Servlet lib -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.5</version>
      <scope>provided</scope>
    </dependency>

    <!-- jstl for jsp page -->
    <dependency>
      <groupId>jstl</groupId>
      <artifactId>jstl</artifactId>
      <version>${jstl.version}</version>
    </dependency>

  </dependencies>
</project>

File cấu hình spring mvc

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">

  <context:component-scan base-package="stackjava.com.ssmultipleformlogin" />

  <bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
      <value>/WEB-INF/views/jsp/</value>
    </property>
    <property name="suffix">
      <value>.jsp</value>
    </property>
  </bean>

</beans>

File cấu hình spring security

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

  <http pattern="/admin/**">
    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
    <form-login
        login-page="/login1"
        login-processing-url="/admin/j_spring_security_login"
        default-target-url="/admin/admin"
      authentication-failure-url="/login1?message=error"
      username-parameter="username"
      password-parameter="password" />
    <logout logout-url="/admin/j_spring_security_logout"
      logout-success-url="/login1?message=logout" delete-cookies="JSESSIONID" />
  </http>
  
  
  <http>
    <intercept-url pattern="/user**" access="hasRole('ROLE_USER')" />
    <form-login
        login-page="/login2"
        login-processing-url="/j_spring_security_login"
        default-target-url="/user"
      authentication-failure-url="/login2?message=error"
      username-parameter="username"
      password-parameter="password" />
    <logout logout-url="/j_spring_security_logout"
      logout-success-url="/login2?message=logout" delete-cookies="JSESSIONID" />
  </http>

  <authentication-manager>
    <authentication-provider>
      <user-service>
        <user name="kai" password="{noop}123456" authorities="ROLE_ADMIN" />
        <user name="sena" password="{noop}123456" authorities="ROLE_USER" />
      </user-service>
    </authentication-provider>
  </authentication-manager>
</beans:beans>

Ở file config spring security mình sử dụng hai thẻ <http> thẻ thứ nhất để định nghĩa form login cho url /admin/** với trang login là /login1. Thẻ <http> thứ hai định nghĩa form login và quyền truy cập cho tất cả các url còn lại với trang login là /login2

File controller

package stackjava.com.ssmultipleformlogin.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";
  }

}

Các file views:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>login</title>
</head>
<body>
  <h2>Form Login1</h2>
  <h4>${message}</h4>

  <form name='loginForm' action="<c:url value='/admin/j_spring_security_login' />" method='POST'>
    <table>
      <tr>
        <td>Username:</td>
        <td><input type='text' name='username'></td>
      </tr>
      <tr>
        <td>Password:</td>
        <td><input type='password' name='password' /></td>
      </tr>
      <tr>
        <td colspan='2'><input name="submit" type="submit" value="login" /></td>
      </tr>
    </table>
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

  </form>
</body>
</html>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>login</title>
</head>
<body>
  <h2>Form Login2</h2>
  <h4>${message}</h4>

  <form name='loginForm' action="<c:url value='/j_spring_security_login' />" method='POST'>
    <table>
      <tr>
        <td>Username:</td>
        <td><input type='text' name='username'></td>
      </tr>
      <tr>
        <td>Password:</td>
        <td><input type='password' name='password' /></td>
      </tr>
      <tr>
        <td colspan='2'><input name="submit" type="submit" value="login" /></td>
      </tr>
    </table>
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />

  </form>
</body>
</html>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>login</title>
</head>
<body>
  <h2>Demo Spring MVC Security - Multiple Login Form</h2>
  <a href="admin/admin">admin page</a> <br/>
  <a href="user">user page</a>
</body>
</html>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Admin Page</title>
</head>
<body>
  <h1>Admin Page</h1>
  <h2>Welcome: ${pageContext.request.userPrincipal.name}</h2>

  <form action="<c:url value="/admin/j_spring_security_logout" />" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="submit" value="Logout" />
  </form>

</body>
</html>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>User Page</title>
</head>
<body>
  <h1>User Page</h1>
  <h2>Welcome: ${pageContext.request.userPrincipal.name}</h2>

  <form action="<c:url value="/j_spring_security_logout" />" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="submit" value="Logout" />
  </form>

</body>
</html>

Demo:

Truy cập trang /admin/admin

Hệ thống sẽ hiển thị form login1. Login với tài khoản kai/123456

Spring MVC - Security dùng nhiều trang login (multiple form login)

Spring mvc multiple login page

Sau khi logout sẽ trở về trang login1

spring security multiple login page

Bây giờ ta truy cập trang /user với tài khoản sena/12345

spring security two login page

Hệ thống sẽ hiển thị trang login2 thay vì trang login1

spring security two form login

Spring MVC - Security dùng nhiều trang login (multiple form login)

Truy cập trang /admin/admin với tài khoản sena/123456 sẽ bị lỗi 403 vì tài khoản sena không có role admin.

Spring MVC - Security dùng nhiều trang login (multiple form login)

Spring MVC – Security dùng nhiều trang login (multiple form login) stackjava.com

Okay, Done!

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

 

References:

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

stackjava.com