[Thực hành] Phân quyền dựa trên vai trò

15. Authorized

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

Leave a Reply

Your email address will not be published. Required fields are marked *