Trang chủHướng dẫnVí Dụ về Phân Quyền Truy Cập theo Vai Trò trong Spring Security

Ví Dụ về Phân Quyền Truy Cập theo Vai Trò trong Spring Security

CyStack blog 6 phút để đọc
CyStack blog27/10/2025
Locker Avatar

Chris Pham

Technical Writer

Locker logo social
Reading Time: 6 minutes

Hôm nay, chúng ta sẽ tìm hiểu cách phân quyền truy cập ****theo vai trò người dùng (role-based access) trong Spring Security. Tuy nhiên, trước khi đọc bài viết này, bạn nên tham khảo bài viết trước: Spring 4 Security MVC Login Logout Example để nắm được những kiến thức cơ bản về Spring Security.

Vai trò (Role) trong Spring Security

Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách định nghĩa, sử dụng và quản lý các vai trò bảo mật (security roles) như "USER", "ADMIN" trong ứng dụng web sử dụng Spring Security.

Tương tự như ví dụ ở bài trước, bài viết này cũng sử dụng Spring 4 MVC Security, kết hợp với In-Memory StoreSpring Java Configuration để xây dựng ứng dụng.

Điều này có nghĩa là chúng ta sẽ:

  • Không cần sử dụng web.xml
  • Không cần viết bất kỳ dòng cấu hình XML nào của Spring

Thay vào đó, chúng ta sẽ sử dụng In-Memory Store để quản lý tài khoản và quyền truy cập.

Công nghệ sử dụng trong ví dụ:

  • Spring 4.0.2.RELEASE
  • Spring Tool Suite (STS) 3.7
  • Spring TC Server 3.1
  • Java 1.8
  • Maven làm công cụ build

Ví dụ phân quyền truy cập theo vai trò trong Spring Security

  1. Bắt đầu bằng cách tạo một dự án Maven Web đơn giản:
  • Tên dự án: SpringMVCSecruityMavenRolesApp
  1. Sử dụng lại file pom.xml từ bài viết trước, với một vài chỉnh sửa nhỏ để phù hợp với ví dụ hiện tại.
<artifactId>SpringMVCSecruityMavenRolesApp</artifactId>

<build>
  <finalName>SpringMVCSecruityMavenRolesApp</finalName>
</build>
</project>
  1. Sử dụng lại toàn bộ các file Java và JSP từ bài viết trước. Trong phần này, chúng ta chỉ tập trung vào các nội dung được cập nhật hoặc thêm mới.
  2. Cập nhật file LoginSecurityConfig.java để cấu hình vai trò người dùng "USER""ADMIN"
package com.journaldev.spring.secuity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity
public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
		authenticationMgr.inMemoryAuthentication()
			.withUser("jduser").password("jdu@123").authorities("ROLE_USER")
			.and()
			.withUser("jdadmin").password("jda@123").authorities("ROLE_USER","ROLE_ADMIN");
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {

		
		http.authorizeRequests()
			.antMatchers("/homePage").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
			.antMatchers("/userPage").access("hasRole('ROLE_USER')")
			.antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")
			.and()
				.formLogin().loginPage("/loginPage")
				.defaultSuccessUrl("/homePage")
				.failureUrl("/loginPage?error")
				.usernameParameter("username").passwordParameter("password")				
			.and()
				.logout().logoutSuccessUrl("/loginPage?logout"); 
		
	}
}

Giải thích mã nguồn

  • Trong phương thức configureGlobal(), chúng ta đã thêm hai người dùng:

    Một người dùng có vai trò "ROLE_USER"

    Một người dùng khác có cả "ROLE_USER""ROLE_ADMIN"

Điều này có nghĩa là người dùng thứ hai sẽ đóng vai trò quản trị viên (Admin).

  • Chúng ta có thể sử dụng một trong hai phương thức authorities(ROLE) hoặc roles(ROLE) để cấu hình vai trò trong ứng dụng.
  • Sự khác nhau giữa authorities()roles()

    Phương thức authorities() yêu cầu viết đầy đủ tên role, ví dụ: "ROLE_USER"

    Phương thức roles() chỉ cần tên vai trò như "USER". Spring sẽ tự động thêm tiền tố ROLE_.

  • Trong phương thức configure(), chúng ta đã cấu hình các đường dẫn URL với role yêu cầu tương ứng.
antMatchers("/homePage")
   .access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")

Đoạn mã này quy định rằng đường dẫn /homePage có thể được truy cập bởi cả người dùng có vai trò "USER""ADMIN".

.antMatchers("/userPage").access("hasRole('ROLE_USER')")
 .antMatchers("/adminPage").access("hasRole('ROLE_ADMIN')")

Đoạn mã trên cấu hình như sau:

  • /userPage: chỉ những người dùng có vai trò "USER" mới được truy cập.
  • /adminPage: chỉ những người dùng có vai trò "ADMIN" mới được truy cập.

Nếu người dùng không có vai trò phù hợp mà cố truy cập vào những đường dẫn này, hệ thống sẽ trả về lỗi: “403 Access is Denied”

  1. Hãy cập nhật Controller LoginController.java để định nghĩa các đường dẫn mới (/userPage/adminPage) như sau:
package com.journaldev.spring.web.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {

	@RequestMapping(value = { "/"}, method = RequestMethod.GET)
	public ModelAndView welcomePage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("welcomePage");
		return model;
	}

	@RequestMapping(value = { "/homePage"}, method = RequestMethod.GET)
	public ModelAndView homePage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("homePage");
		return model;
	}
	
	@RequestMapping(value = {"/userPage"}, method = RequestMethod.GET)
	public ModelAndView userPage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("userPage");
		return model;
	}
	
	@RequestMapping(value = {"/adminPage"}, method = RequestMethod.GET)
	public ModelAndView adminPage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("adminPage");
		return model;
	}
	
	@RequestMapping(value = "/loginPage", method = RequestMethod.GET)
	public ModelAndView loginPage(@RequestParam(value = "error",required = false) String error,
	@RequestParam(value = "logout",	required = false) String logout) {
		
		ModelAndView model = new ModelAndView();
		if (error != null) {
			model.addObject("error", "Invalid Credentials provided.");
		}

		if (logout != null) {
			model.addObject("message", "Logged out from JournalDEV successfully.");
		}

		model.setViewName("loginPage");
		return model;
	}

}

Giải thích

Bên cạnh ví dụ từ bài viết trước, lần này chúng ta đã thêm hai đường dẫn URL mới:

  • /userPage: dành cho người dùng có vai trò "USER" để truy cập và thực hiện các chức năng thông thường.
  • /adminPage: dành riêng cho người dùng có vai trò "ADMIN" để truy cập và thực hiện các chức năng quản trị. Người dùng có vai trò "ADMIN" cũng có thể truy cập vào /userPage.
  • File homePage.jsp đã được cập nhật để cung cấp các hoạt động riêng biệt dành cho người dùng thuộc vai trò User và Admin**.**
<%@taglib prefix="c" uri="<https://java.sun.com/jsp/jstl/core>"%>
<a href="${pageContext.request.contextPath}/userPage">JD User</a> | <a href="${pageContext.request.contextPath}/adminPage">JD Admin</a> | <a href="javascript:document.getElementById('logout').submit()">Logout</a>

<h3>Welcome to JournalDEV Tutorials</h3>
<ul>
   <li>Java 8 tutorial</li>
   <li>Spring tutorial</li>
   <li>Gradle tutorial</li>
   <li>BigData tutorial</li>
</ul>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>

Trong phần đầu của trang, chúng ta đã thêm ba tùy chọn menu. Liên kết "Logout" đã được trình bày trong bài viết trước. Hai liên kết mới được thêm vào gồm:

JD User – Có thể được truy cập bởi cả người dùng có vai trò "USER""ADMIN".

JD Admin – Chỉ có người dùng có vai trò ****"ADMIN" mới có quyền truy cập.

Lưu ý:

  • Trong các ứng dụng thực tế, chúng ta nên chỉ hiển thị liên kết "JD User" đối với người dùng có vai trò ****"USER", và ẩn liên kết "JD Admin" nếu người dùng không có quyền quản trị. Tuy nhiên, để kiểm tra xem người dùng "USER" có bị chặn đúng cách hay không khi truy cập vào liên kết "JD Admin", cũng như để xem rõ thông báo lỗi, trong ví dụ này chúng ta không ẩn liên kết đó.
  1. Tạo file adminPage.jsp – Trang chủ dành riêng cho vai trò "ADMIN"
<%@taglib prefix="c" uri="<https://java.sun.com/jsp/jstl/core>"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<h3>Admin Page</h3>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
	<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>
  1. Tạo file userPage.jsp – Trang chủ dành cho vai trò "USER"
<%@taglib prefix="c" uri="<https://java.sun.com/jsp/jstl/core>"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<h3>User Page</h3>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
	<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>

Chúng ta đã hoàn tất quá trình phát triển ứng dụng. Bây giờ là lúc kiểm tra cấu trúc cuối cùng của dự án và chạy thử.

  1. Cấu trúc hoàn chỉnh của dự án:

image.png

Kiểm thử ứng dụng Spring Security Roles

  1. Chạy ứng dụng từ Spring STS → Nhấp chuột phải vào dự án trong Spring STS IDE, chọn: Run As → Run on Server
  2. Thao tác này sẽ khởi động ứng dụng trên máy chủ và trình duyệt sẽ tự động mở trang chào mừng mặc định của ứng dụng, như minh họa bên dưới:

image.png

  1. Nhấp vào liên kết Login to JournalDEV.
  2. Lúc này, bạn sẽ được chuyển đến trang đăng nhập của ứng dụng.

image.png

  1. Đầu tiên, đăng nhập bằng tài khoản có vai trò "USER":
  • Tên đăng nhập: jdadmin
  • Mật khẩu: jda@123

image.png

  1. Bây giờ bạn sẽ thấy trang chủ của ứng dụng với 3 tùy chọn menu: "JD Admin", "JD User","Logout"
  2. Nhấp vào liên kết "JD User"

Vì bạn đã đăng nhập bằng tài khoản có vai trò "USER", nên bạn có thể truy cập thành công liên kết này như hình minh họa bên dưới.

image.png

  1. Hãy sử dụng nút Back trong trình duyệt của Spring STS IDE, sau đó nhấp vào liên kết "JD Admin".

image.png

Vì bạn đã đăng nhập bằng tài khoản có vai trò "USER", nên không thể truy cập vào liên kết "JD Admin".

→ Kết quả là bạn sẽ thấy thông báo lỗi: 403 Access is denied.

  1. Đăng xuất khỏi tài khoản hiện tại và đăng nhập lại bằng tài khoản ADMIN:
  • Tên đăng nhập: jdadmin
  • Mật khẩu: jda@123

Lần này, bạn có thể truy cập thành công vào liên kết "JD Admin" như hình minh họa bên dưới.

image.png

  1. Kiểm tra liên kết "Logout" để đăng xuất khỏi ứng dụng.

Như vậy là bạn đã xem hoàn tất ví dụ về phân quyền truy cập theo vai trò trong Spring Security, giúp kiểm soát quyền truy cập vào các trang trong ứng dụng web một cách hiệu quả và an toàn.

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.

Đăng ký nhận Newsletter

Nhận các nội dung hữu ích mới nhất