Setting Up the Frontend

 <div class="container bg-secondary py-4">
        <div class="p-5 mb-4 bg-light text-dark rounded-3">
            <h1>Login</h1>
            <label for="email">Username:</label><br>
            <input type="text" id="username" name="username"><br>
            <label for="password">Password:</label><br>
            <input type="text" id="password" name="password"><br><br>
            <input type="submit" value="Login" onclick="login()">
            <p id="message"></p>
        </div>
    </div>
    <script>
        function login() {
            var email = document.getElementById('username').value;
            var password = document.getElementById('password').value;
            var data = {email:email, password:password};
            fetch("/authenticate", {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data)}).then((data) => {
                if (data.status == 200) {
                    window.location.replace("/mvc/person/read");
                } else {
                    document.getElementById('message').innerHTML = "Invalid email or password"
                }
            });
        }
    </script>

The login page calls the authenticate endpoint, and allows the user to log in and generate the JWT cookie that can be used to authenticate whether the user has the right to perform certain operations on the system.

Update

<tr th:each="person : ${list}">
    <td th:text="${person.id}">Person ID</td>
    <td th:text="${person.email}">Birth Date</td>
    <td th:text="${person.name}">Name</td>
    <td th:if="${person.getAge() != -1}" th:text="${person.getAge()}">Age</td>
    <td th:unless="${person.getAge() != -1}" th:text="Unknown">Unknown Age</td>
    <td>
        <!--- <a th:href="@{/mvc/notes/{id}(id = ${person.id})}">Notes</a> -->
        <a th:href="@{/mvc/person/update/{id}(id = ${person.id})}">Update</a>
        <a th:href="@{/mvc/person/delete/{id}(id = ${person.id})}">Delete</a>
    </td>
</tr>

The thymeleaf here checks if, when the user presses on the Update or Delete tags, they have appropriate roles by calling the API. It gets the user ID for each user in the table and runs the restricted endpoints /update and /delete. If the user has the appropriate role (ROLE_ADMIN), then the user is allowed to continue the operation. Otherwise it throws the 403 error.