[Thực hành] Phân quyền dựa trên vai trò
NỘI DUNG BÀI VIẾT
Mục tiêu
Luyện tập triển khai cơ chế phân quyền dựa trên vai trò (role-based authorization).
Nội dung
Trong phần này, chúng ta sẽ phát triển một ứng dụng để luyện tập cơ chế phân quyền dựa trên vai trò của người dùng.
Ứng dụng sẽ cho phép tạo ra các vai trò khác nhau và gán vai trò cho từng người dùng. Đối với các vai trò khác nhau thì có thể thực hiện các chức năng khác nhau.
– User
– Admin
– DBA
Hướng dẫn
Bước 1: Tạo project Gradle mang tên spring-role-based-authorization
Bước 2: Thêm các dependencie cho project
compile group: 'org.springframework', name: 'spring-webmvc', version: '4.3.10.RELEASE'
compile group: 'org.springframework.security', name: 'spring-security-web', version: '4.2.3.RELEASE'
compile group: 'org.springframework.security', name: 'spring-security-config', version: '4.2.3.RELEASE'
compile group: 'org.springframework.security', name: 'spring-security-taglibs', version: '4.2.3.RELEASE'
providedCompile group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1'
compile group: 'org.thymeleaf', name: 'thymeleaf-spring4', version: '3.0.4.RELEASE'
compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity4', version: '3.0.2.RELEASE'
Tạo cấu trúc thư mục cho project
Bước 3: Cấu hình cho project trong thư mục configuration
– class AppInitializer
package com.codegym.configuration;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{ ApplicationConfiguration.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[0];
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
– class ApplicationConfiguration
package com.codegym.configuration;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.codegym")
public class ApplicationConfiguration extends WebMvcConfigurerAdapter implements ApplicationContextAware {
@Bean(name="HelloWorld")
public SpringResourceTemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setApplicationContext(applicationContext);
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
@Bean
public TemplateEngine templateEngine(){
TemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new SpringSecurityDialect());
return templateEngine;
}
@Bean
public ViewResolver viewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/*
* Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
*
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
– Cấu hình sercurity cho project trong class SecurityWebApplicationInitializer
package com.codegym.configuration;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
– Cấu hình các tài khoản và quyền tương ứng
package com.codegym.configuration;
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 SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
CustomSuccessHandler customSuccessHandler;
@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("root123").roles("ADMIN");
auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DBA");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").access("hasRole('USER')")
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/dba/**").access("hasRole('ADMIN') and hasRole('DBA')")
.and().formLogin().successHandler(customSuccessHandler)
.usernameParameter("ssoId").passwordParameter("password")
.and().csrf()
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
}
}
– Điều hướng các tài khoản đến các view tương ứng trong class CustomSuccessHandler
package com.codegym.configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
@Override
protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException {
String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
System.out.println("Can't redirect");
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
/*
* This method extracts the roles of currently logged-in user and returns
* appropriate URL according to his/her role.
*/
protected String determineTargetUrl(Authentication authentication) {
String url = "";
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> roles = new ArrayList<String>();
for (GrantedAuthority a : authorities) {
roles.add(a.getAuthority());
}
if (isDba(roles)) {
url = "/dba";
} else if (isAdmin(roles)) {
url = "/admin";
} else if (isUser(roles)) {
url = "/home";
} else {
url = "/accessDenied";
}
return url;
}
private boolean isUser(List<String> roles) {
if (roles.contains("ROLE_USER")) {
return true;
}
return false;
}
private boolean isAdmin(List<String> roles) {
if (roles.contains("ROLE_ADMIN")) {
return true;
}
return false;
}
private boolean isDba(List<String> roles) {
if (roles.contains("ROLE_DBA")) {
return true;
}
return false;
}
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
}
Bước 4: Tạo controller SercurityController vào trang welcome cho user
package com.codegym.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class SercurityController {
private String getPrincipal(){
String userName = null;
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
userName = ((UserDetails)principal).getUsername();
} else {
userName = principal.toString();
}
return userName;
}
@GetMapping(value = {"/", "/home"})
public String Homepage(Model model){
model.addAttribute("user", getPrincipal());
return "welcome";
}
}
Tạo view welcome.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1> Dear <span th:text="${user}"></span> Welcome to User Page</h1>
<div sec:authorize="isAuthenticated()">
<a th:href="@{/}">Home</a> | <a th:href="@{/logout}">Logout</a>
</div>
</body>
</html>
– Chạy thử ứng dụng

– Sau đó đăng nhập tài khoản user: bill, password: abc123 như đã được cấu hình

Bước 5: Thêm controller và view admin.html cho admin
– Controller:
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String adminPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "admin";
}
– admin.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>admin</title>
</head>
<body>
<h1> Dear <span th:text="${user}"></span> Welcome to Admin Page</h1>
<div sec:authorize="isAuthenticated()">
<a th:href="@{/logout}">Logout</a>
</div>
</body>
</html>
– Chạy thử ứng dụng đăng nhập với tài khoản user: admin, password: root123

– Chuyển đường dẫn sang / báo lỗi

Ta cần thêm controller accessDeniedPage và view cho thông báo không có quyền truy cập trang
– Controller
@RequestMapping(value = "/Access_Denied", method = RequestMethod.GET)
public String accessDeniedPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "accessDenied";
}
– views accessDenied.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
>
<head>
<meta charset="UTF-8">
<title>AccessDenied page</title>
</head>
<body>
Dear <strong th:text="${user}"></strong>, You are not authorized to access this page
<p><a th:href="@{/logout}">Logout</a></p>
</body>
</html>
Chạy ứng dụng với các bước trên ta có:

Bước 6: Thêm controller và view dba.html cho dba
– Controller
@RequestMapping(value = "/dba", method = RequestMethod.GET)
public String dbaPage(ModelMap model) {
model.addAttribute("user", getPrincipal());
return "dba";
}
– Views dba.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>admin</title>
</head>
<body>
<h1> Dear <span th:text="${user}"></span> Welcome to DBA Page</h1>
<div sec:authorize="isAuthenticated()">
<a th:href="@{/admin}">admin</a> | <a th:href="@{/logout}">Logout</a>
</div>
</body>
</html>
– Chạy thử ứng dụng và đăng nhập tài khoản user: dba, password: root123

Chuyển sang trang admin ta có

Mã nguồn tham khảo tại: https://github.com/codegym-vn/java-spring-role-based-authorization
Trả lời