Code ví dụ Spring MVC Security Hello World – XML Config

Code ví dụ Spring MVC Security Hello World – XML Config

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

Code ví dụ Spring MVC Security Hello World – XML Config. Hướng dẫn cấu hình config ví dụ Spring MVC Security với XML: Tạo account, phân quyền…

Tạo Maven Project

Code ví dụ Spring MVC Security Hello World - XML Config

Thư viện sử dụng:

Ở đây mình sử dụng Spring Security 5.0.2.RELEASE

<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>SpringMvcSecurityHello</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>

    <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 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  version="2.5">
  <display-name>SpringMvcSecurityHello</display-name>

  <!-- SPRING MVC -->
  <servlet>
    <servlet-name>spring-mvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/spring-mvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring-mvc</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <!-- Creates the Spring Container shared by all Servlets and Filters -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
  </listener>


  <!-- Loads Spring Security config file -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      /WEB-INF/spring-security.xml
    </param-value>
  </context-param>

  <!-- Spring Security -->
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

File Spring Config:

<?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.springmvcsecurity" />

  <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 Spring Security Config:

<?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 auto-config="true">
    <intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/user**" access="hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')" />
    <logout logout-url="/j_spring_security_logout"
      logout-success-url="/logout" delete-cookies="JSESSIONID" />
  </http>

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

      <!-- <user-service>
        <user name="admin" password="$2a$12$gmFXC74IRBODfkGcWhVUGelfT7hb3yeObUe.swjwMhAuwqRwv6Ori" authorities="ROLE_ADMIN" />
        <user name="user" password="$2a$12$gmFXC74IRBODfkGcWhVUGelfT7hb3yeObUe.swjwMhAuwqRwv6Ori" authorities="ROLE_USER" />
      </user-service>
      <password-encoder hash="bcrypt" /> -->
      
      
    </authentication-provider>
  </authentication-manager>
</beans:beans>
<intercept-url pattern="/admin**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/user**" access="hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')" />

Chỉ user có role là “ROLE_ADMIN” mới được truy cập các đường dẫn bắt đầu bằng /admin

Các đường dẫn bắt đầu bằng /user thì chi cho phép các user có role là “ROLE_ADMIN” hoặc “ROLE_USER” truy cập.

<user-service>
  <user name="admin" password="{noop}123456" authorities="ROLE_ADMIN" />
  <user name="user" password="{noop}123456" authorities="ROLE_USER" />
</user-service>

Ở đây mình tạo hai đối tượng user là admin (password = ‘123456’ và role = ‘ROLE_ADMIN’) và user (password = ‘123456’ và role = ‘ROLE_USER’).

Lưu ý, từ Spring Security 5, Mặc định PasswordEncoder là DelegatingPasswordEncoder. Khi bạn lưu trữ user trong bộ nhớ, thông tin mật khẩu ở dạng plain text sẽ được mã hóa bằng DelegatingPasswordEncoder, ở đây mình không mã hóa mật khẩu nên sẽ cần dùng tiền tố {noop} trước các mật khẩu nếu không Spring Security sẽ không hiểu mật khẩu được mã hóa theo thuật toán nào.

Bạn có thể thực hiện mã hóa mật khẩu bằng bcrypt, ta mã hóa mật khẩu bằng bcrypt và đặt nó vào thuộc tính password, ví dụ:

<user-service>
  <user name="admin" password="$2a$12$gmFXC74IRBODfkGcWhVUGelfT7hb3yeObUe.swjwMhAuwqRwv6Ori" authorities="ROLE_ADMIN" />
  <user name="user" password="$2a$12$gmFXC74IRBODfkGcWhVUGelfT7hb3yeObUe.swjwMhAuwqRwv6Ori" authorities="ROLE_USER" />
</user-service>
<password-encoder hash="bcrypt" />

File Controller:

package stackjava.com.springmvcsecurity.controller;

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

@Controller
public class BaseController {

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

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

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

  @RequestMapping("/logout")
  public String logout(Model model) {
    model.addAttribute("message", "Logout success!");
    return "index";
  }
}

File view:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>index</title>
</head>
<body>
  <h1>Spring MVC - Security </h1>
  <h2>${message}</h2>
  <a href='<c:url value="/admin" />'>admin</a>
  <a href='<c:url value="/user" />'>user</a>
</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>
<%@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="/j_spring_security_logout" />" method="post">
    <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
    <input type="submit" value="Logout" />
  </form>

</body>
</html>

Trang login

Ở đây mình không tạo trang login nên Spring Security sẽ tự động sử dụng trang login mặc định của nó.

Ở bài sau mình sẽ hướng dẫn cách định nghĩa và cài đặt trang login cho Spring Security.

Demo

Truy cập vào đường dẫn /admin

Vì user chưa đăng nhập nên Spring Security sẽ tự động chuyển hướng ta tới trang đăng nhập. (Ở đây mình không tạo trang đăng nhập nên Spring Security sẽ chuyển tới trang đăng nhập mặc định của nó)

Trường hợp nhập user hoặc mật khẩu sai:

Trường hợp nhập mật khẩu đúng (admin-123456)

Vì ‘admin’ có cả role = “ROLE_ADMIN” được phép truy cập cả đường dẫn /user

Thực hiện logout

Thực hiện login với tài khoản user-123456

Vì tài khoản ‘user’ không có quyền truy cập đường dẫn /admin nên khi truy cập /admin nó sẽ báo lỗi 403 tức là không có quyền.

Code ví dụ Spring MVC Security Hello World – XML Config

Okay, Done!

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

 

References:

https://docs.spring.io/spring-security/site/docs/current/guides/html5/helloworld-xml.html#security-config-xml

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

https://spring.io/blog/2017/11/01/spring-security-5-0-0-rc1-released#password-encoding

stackjava.com