Understanding Roles - spring-boot-in-practice/repo GitHub Wiki
In this article, let us quickly see the concept and purpose of a role in a Spring Security application. In a Spring Security application, you use the notion of ROLE and GRANTED_AUTHORITIES to control what a logged-in user is authorized to view and perform in the application. You can think of a role or authority as the permission or right of a user.
You'll use the CourseTracker application to implement roles. In the application, you'll use two users - user and admin. The user user is created with the USER role and the user admin is created with the ADMIN role. The admin is additionally allowed to view an admin page along with all other pages. The user is not authorized to access the admin page. You can find the complete application here.
Following are the changes:
We are using Thymeleaf to manage the UI. Thymeleaf provides additional dependency to manage the security in the application UI. Let us add this dependency in the pom.xml file:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
On the index page we've included the following changes:
- Included the **sec **namespace in the page with xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5"
- Added an additional menu option in the navigation bar named /admin and ensured that it is accessible by the logged-in user with role as admin with sec:authorize="hasRole('ROLE_ADMIN')"
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Courses</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
<link rel="stylesheet" type="text/css" th:href="@{/css/custom.css}" />
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<nav class="navbar navbar-dark bg-dark navbar-expand-sm">
<a class="navbar-brand brand-text" href="#">
<img src="/images/logo.png" width="30" height="30" alt="logo">
Course Tracker
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar-list" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-between" id="navbar-list">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#" th:href="@{/index}">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{/addcourse}">Add Course</a>
</li>
<li class="nav-item" sec:authorize="hasRole('ROLE_ADMIN')">
<a class="nav-link" href="#" th:href="@{/admin}">Admin</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Welcome <span sec:authentication="name"></span></a>
</li>
<li class="nav-item">
<form th:action="@{/logout}" method="post">
<button class="btn btn-danger" type="submit">logout</button>
</form>
</li>
</ul>
</div>
</nav>
<div th:switch="${#lists.size(courses)}" class="container my-5">
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-10">
<div th:case="'0'">
<h2>You haven't added any course yet!</h2>
<p class="text-success">Add a course by clicking below!</p>
</div>
<div th:case="*">
<h2 class="my-5">Your Courses</h2>
<table class="table table-striped table-responsive-md">
<thead>
<tr>
<th>Course Name</th>
<th>Course Category</th>
<th>Course Rating</th>
<th>Course Description</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr th:each="course : ${courses}">
<td th:text="${course.name}"></td>
<td th:text="${course.category}"></td>
<td th:text="${course.rating}"></td>
<td th:text="${course.description}"></td>
<td><a th:href="@{/update/{id}(id=${course.id})}" class="btn btn-dark"><i class="fas fa-edit"></i></a></td>
<td>
<form action="#" th:action="@{/delete/{id}(id=${course.id})}" th:method="delete">
<button type="submit" class="btn btn-danger">
<i class="fas fa-trash"></i>
</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
<p class="my-5"><a href="/addcourse" class="btn btn-dark"><i class="fas fa-plus-square"></i></a></p>
</div>
<div class="col-md-1"></div>
</div>
</div>
</body>
</html>
We've included an HTTP GET endpoint for the /admin link. It redirects to the admin page
package com.manning.sbip.ch05.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/admin")
public String admin() {
return "admin";
}
}
We've included a dummy admin page that shows the Hello Admin if a user with an admin role logs in. For a user with role user, we show the message You are not authorized to view this page.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Admin Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.1/css/all.css">
<link rel="stylesheet" type="text/css" th:href="@{/css/custom.css}" />
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/css/bootstrap.min.css}" />
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" th:src="@{/webjars/jquery/jquery.min.js}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" th:src="@{/webjars/bootstrap/js/bootstrap.min.js}"></script>
</head>
<body>
<nav class="navbar navbar-dark bg-dark navbar-expand-sm">
<a class="navbar-brand brand-text" href="#">
<img src="/images/logo.png" width="30" height="30" alt="logo">
Course Tracker
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar-list" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-between" id="navbar-list">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#" th:href="@{/index}">Home <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#" th:href="@{/addcourse}">Add Course</a>
</li>
<li class="nav-item" sec:authorize="hasRole('ROLE_ADMIN')">
<a class="nav-link" href="#" th:href="@{/admin}">Admin</a>
</li>
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<form th:action="@{/logout}" method="post">
<button class="btn btn-danger" type="submit">logout</button>
</form>
</li>
</ul>
</div>
</nav>
<div class="container my-5">
<div class="row">
<div class="col-md-1"></div>
<div class="col-md-10">
<div sec:authorize="hasRole('ROLE_USER')">
<h2>You are not authorized to view this page</h2>
</div>
<div sec:authorize="hasRole('ROLE_ADMIN')">
<h2>Hello Admin</h2>
</div>
</div>
<div class="col-md-1"></div>
</div>
</div>
</body>
</html>
You can now start the application from your IDE or using the mvn spring-boot:run command. If you log-in with the admin user and access the http://localhost:8080/index page, you'll find the following page:
If the user with username user tries to access the HTTP://localhost:8080/admin page, they get the following error: