Spring Boot + Spring Security + Thymeleaf example - LearnHowToCode SarkariResult.com Interview Questions and Answers LearnHowToCodeOnline

Spring Boot + Spring Security + Thymeleaf example

A Spring Boot Thymeleaf example, uses Spring Security to protect path /admin and /user
Technologies used :
  1. Spring Boot 1.5.3.RELEASE
  2. Spring 4.3.8.RELEASE
  3. Spring Security 4.2.2
  4. Thymeleaf 2.1.5.RELEASE
  5. Thymeleaf extras Spring Security4 2.1.3
  6. Tomcat Embed 8.5.14
  7. Maven 3
  8. Java 8

1. Project Directory

2. Project Dependencies

Declares spring-boot-starter-security, it will get anything you need to develop a Spring Boot + Spring Securityweb application.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"
>

<modelVersion>4.0.0</modelVersion>

<artifactId>spring-boot-web-spring-security</artifactId>
<packaging>jar</packaging>
<name>Spring Boot Web Spring Security</name>
<description>Spring Boot Web Spring Security Example</description>
<url>https://www.mycareerrepublic.com</url>
<version>1.0</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
</parent>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>

<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- do you like thymeleaf? -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- optional, it brings userful tags to display spring security stuff -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

<!-- hot swapping, disable cache for template, enable live reload -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>

<!-- Optional, for bootstrap -->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>

</dependencies>
<build>
<plugins>
<!-- Package as an executable jar/war -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Display project dependencies :
$ mvn dependency:tree

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Spring Boot Web Spring Security 1.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.10:tree (default-cli) @ spring-boot-web-spring-security ---
[INFO] org.springframework.boot:spring-boot-web-spring-security:jar:1.0
[INFO] +- org.springframework.boot:spring-boot-starter-thymeleaf:jar:1.5.3.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:1.5.3.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.3.RELEASE:compile
[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.1.11:compile
[INFO] | | | | \- ch.qos.logback:logback-core:jar:1.1.11:compile
[INFO] | | | +- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile
[INFO] | | | +- org.slf4j:jul-to-slf4j:jar:1.7.25:compile
[INFO] | | | \- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile
[INFO] | | +- org.springframework:spring-core:jar:4.3.8.RELEASE:compile
[INFO] | | \- org.yaml:snakeyaml:jar:1.17:runtime
[INFO] | +- org.springframework.boot:spring-boot-starter-web:jar:1.5.3.RELEASE:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-tomcat:jar:1.5.3.RELEASE:compile
[INFO] | | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:8.5.14:compile
[INFO] | | | +- org.apache.tomcat.embed:tomcat-embed-el:jar:8.5.14:compile
[INFO] | | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:8.5.14:compile
[INFO] | | +- org.hibernate:hibernate-validator:jar:5.3.5.Final:compile
[INFO] | | | +- javax.validation:validation-api:jar:1.1.0.Final:compile
[INFO] | | | +- org.jboss.logging:jboss-logging:jar:3.3.1.Final:compile
[INFO] | | | \- com.fasterxml:classmate:jar:1.3.3:compile
[INFO] | | +- com.fasterxml.jackson.core:jackson-databind:jar:2.8.8:compile
[INFO] | | | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.8.0:compile
[INFO] | | | \- com.fasterxml.jackson.core:jackson-core:jar:2.8.8:compile
[INFO] | | +- org.springframework:spring-web:jar:4.3.8.RELEASE:compile
[INFO] | | \- org.springframework:spring-webmvc:jar:4.3.8.RELEASE:compile
[INFO] | +- org.thymeleaf:thymeleaf-spring4:jar:2.1.5.RELEASE:compile
[INFO] | | \- org.thymeleaf:thymeleaf:jar:2.1.5.RELEASE:compile
[INFO] | | +- ognl:ognl:jar:3.0.8:compile
[INFO] | | +- org.javassist:javassist:jar:3.21.0-GA:compile
[INFO] | | \- org.unbescape:unbescape:jar:1.1.0.RELEASE:compile
[INFO] | \- nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:jar:1.4.0:compile
[INFO] | \- org.codehaus.groovy:groovy:jar:2.4.10:compile
[INFO] +- org.springframework.boot:spring-boot-starter-security:jar:1.5.3.RELEASE:compile
[INFO] | +- org.springframework:spring-aop:jar:4.3.8.RELEASE:compile
[INFO] | | \- org.springframework:spring-beans:jar:4.3.8.RELEASE:compile
[INFO] | +- org.springframework.security:spring-security-config:jar:4.2.2.RELEASE:compile
[INFO] | | +- org.springframework.security:spring-security-core:jar:4.2.2.RELEASE:compile
[INFO] | | \- org.springframework:spring-context:jar:4.3.8.RELEASE:compile
[INFO] | \- org.springframework.security:spring-security-web:jar:4.2.2.RELEASE:compile
[INFO] | \- org.springframework:spring-expression:jar:4.3.8.RELEASE:compile
[INFO] +- org.thymeleaf.extras:thymeleaf-extras-springsecurity4:jar:2.1.3.RELEASE:compile
[INFO] | \- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] +- org.springframework.boot:spring-boot-devtools:jar:1.5.3.RELEASE:compile
[INFO] | +- org.springframework.boot:spring-boot:jar:1.5.3.RELEASE:compile
[INFO] | \- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.3.RELEASE:compile
[INFO] \- org.webjars:bootstrap:jar:3.3.7:compile
[INFO] \- org.webjars:jquery:jar:1.11.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.072 s
[INFO] Finished at: 2017-05-04T10:13:05+08:00
[INFO] Final Memory: 19M/309M
[INFO] ------------------------------------------------------------------------
3. Spring Security
3.1 Extends WebSecurityConfigurerAdapter, and defined the security rules in the configure method.
For user “admin” :
  1. Able to access /admin page
  2. Unable to access /user page, redirect to 403 access denied page.
For user “user” :
  1. able to access /user page
  2. unable to access /admin page, redirect to 403 access denied page.
SpringSecurityConfig.java
package com.mycareerrepublic.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.WebSecurityConfigurerAdapter;
import org.springframework.security.web.access.AccessDeniedHandler;

@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private AccessDeniedHandler accessDeniedHandler;

// roles admin allow to access /admin/**
// roles user allow to access /user/**
// custom 403 access denied handler
@Override
protected void configure(HttpSecurity http) throws Exception {

http.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/home", "/about").permitAll()
.antMatchers("/admin/**").hasAnyRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll()
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}

// create two users, admin and user
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("password").roles("ADMIN");
}
}
3.2 Custom 403 Access denied handler, logs the request and redirect to /403
WelcomeController.java
package com.mycareerrepublic.error;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

// handle 403 page
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {

private static Logger logger = LoggerFactory.getLogger(MyAccessDeniedHandler.class);

@Override
public void handle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
AccessDeniedException e) throws IOException, ServletException {

Authentication auth
= SecurityContextHolder.getContext().getAuthentication();

if (auth != null) {
logger.info("User '" + auth.getName()
+ "' attempted to access the protected URL: "
+ httpServletRequest.getRequestURI());
}

httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + "/403");

}
}

4. Spring Boot

4.1 A controller class, to define the http request and view name.
DefaultController.java
package com.mycareerrepublic.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class DefaultController {

@GetMapping("/")
public String home1() {
return "/home";
}

@GetMapping("/home")
public String home() {
return "/home";
}

@GetMapping("/admin")
public String admin() {
return "/admin";
}

@GetMapping("/user")
public String user() {
return "/user";
}

@GetMapping("/about")
public String about() {
return "/about";
}

@GetMapping("/login")
public String login() {
return "/login";
}

@GetMapping("/403")
public String error403() {
return "/error/403";
}

}
4.2 Start Spring Boot application.
DefaultController.java
package com.mycareerrepublic;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringBootWebApplication {

public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootWebApplication.class, args);
}

}

5. Thymeleaf + Resources + Static files

5.1 For Thymeleaf files, put in src/main/resources/templates/ folder.
5.2 Thymeleaf fragments, for template layout – header.
src/main/resources/templates/fragments/header.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:fragment="header-css">
<!-- this is header-css -->
<link rel="stylesheet" type="text/css"
href="webjars/bootstrap/3.3.7/css/bootstrap.min.css" />


<link rel="stylesheet" th:href="@{/css/main.css}"
href="../../css/main.css" />

</div>
</head>
<body>
<div th:fragment="header">
<!-- this is header -->
<nav class="navbar navbar-inverse">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" th:href="@{/}">Spring Boot</a>
</div>
<div id="navbar" class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li class="active"><a th:href="@{/}">Home</a></li>
</ul>
</div>
</div>
</nav>
</div>

</body>
</html>
5.3 Thymeleaf fragments, for template layout – footer. Review the sec tag, it is a useful tag to display the Spring Security stuff, refer to this Thymeleaf extra Spring Security for detail.
src/main/resources/templates/fragments/footer.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">

<head>
</head>
<body>
<div th:fragment="footer">

<div class="container">

<footer>
<!-- this is footer -->
© 2017 mycareerrepublic.com
<span sec:authorize="isAuthenticated()">
| Logged user: <span sec:authentication="name"></span> |
Roles: <span sec:authentication="principal.authorities"></span> |
<a th:href="@{/logout}">Sign Out</a>
</span>

<script type="text/javascript"
src="webjars/bootstrap/3.3.7/js/bootstrap.min.js">
</script>

</footer>
</div>

</div>
</body>
</html>
5.4 List of the Thymeleaf files, nothing special, self-explanatory.
home ~
src/main/resources/templates/home.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Boot Thymeleaf + Spring Security</title>

<div th:replace="fragments/header :: header-css"/>

</head>
<body>

<div th:replace="fragments/header :: header"/>

<div class="container">

<div class="starter-template">
<h1>Spring Boot Web Thymeleaf + Spring Security</h1>
<h2>1. Visit <a th:href="@{/admin}">Admin page (Spring Security protected, Need Admin Role)</a></h2>
<h2>2. Visit <a th:href="@{/user}">User page (Spring Security protected, Need User Role)</a></h2>
<h2>3. Visit <a th:href="@{/about}">Normal page</a></h2>
</div>

</div>
<!-- /.container -->

<div th:replace="fragments/footer :: footer"/>

</body>
</html>
admin ~
src/main/resources/templates/admin.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>

<div th:replace="fragments/header :: header"/>

<div class="container">

<div class="starter-template">
<h1>Admin page (Spring Security protected)</h1>

<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>

</div>

</div>
<!-- /.container -->

<div th:replace="fragments/footer :: footer"/>

</body>
</html>
user ~
src/main/resources/templates/user.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>

<div th:replace="fragments/header :: header"/>

<div class="container">

<div class="starter-template">
<h1>User page (Spring Security protected)</h1>

<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>

</div>

</div>
<!-- /.container -->
<div th:replace="fragments/footer :: footer"/>

</body>
</html>
about ~
src/main/resources/templates/about.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>

<div th:replace="fragments/header :: header"/>

<div class="container">

<div class="starter-template">
<h1>Normal page (No need login)</h1>
</div>

</div>
<!-- /.container -->

<div th:replace="fragments/footer :: footer"/>

</body>
</html>
login ~
src/main/resources/templates/login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
>

<head>
<title>Spring Security Example </title>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>

<div th:replace="fragments/header :: header"/>

<div class="container">

<div class="row" style="margin-top:20px">
<div class="col-xs-12 col-sm-8 col-md-6 col-sm-offset-2 col-md-offset-3">
<form th:action="@{/login}" method="post">
<fieldset>
<h1>Please Sign In</h1>

<div th:if="${param.error}">
<div class="alert alert-danger">
Invalid username and password.
</div>
</div>
<div th:if="${param.logout}">
<div class="alert alert-info">
You have been logged out.
</div>
</div>

<div class="form-group">
<input type="text" name="username" id="username" class="form-control input-lg"
placeholder="UserName" required="true" autofocus="true"/>

</div>
<div class="form-group">
<input type="password" name="password" id="password" class="form-control input-lg"
placeholder="Password" required="true"/>

</div>

<div class="row">
<div class="col-xs-6 col-sm-6 col-md-6">
<input type="submit" class="btn btn-lg btn-primary btn-block" value="Sign In"/>
</div>
<div class="col-xs-6 col-sm-6 col-md-6">
</div>
</div>
</fieldset>
</form>
</div>
</div>

</div>

<div th:replace="fragments/footer :: footer"/>

</body>
</html>
403 ~
src/main/resources/templates/error/403.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<div th:replace="fragments/header :: header-css"/>
</head>
<body>

<div th:replace="fragments/header :: header"/>

<div class="container">

<div class="starter-template">
<h1>403 - Access is denied</h1>
<div th:inline="text">Hello '[[${#httpServletRequest.remoteUser}]]',
you do not have permission to access this page.</div>
</div>

</div>
<!-- /.container -->

<div th:replace="fragments/footer :: footer"/>

</body>
</html>
5.5 For static files like CSS or Javascript, put in /src/main/resources/static/
/src/main/resources/static/css/main.css
h1{
color:#0000FF;
}

h2{
color:#FF0000;
}

footer{
margin-top:60px;
}
Note
Read this Spring Boot Serving static content to understand the resource mapping.

6. Demo

6.1 Start the Spring Boot web app. This /admin/** is protected, you need login as admin to access it.
Terminal
$ mvn spring-boot:run

//...
6.2 Access http://localhost:8080

About Mariano

I'm Ethan Mariano a software engineer by profession and reader/writter by passion.I have good understanding and knowledge of AngularJS, Database, javascript, web development, digital marketing and exploring other technologies related to Software development.

0 comments:

Featured post

Political Full Forms List

Acronym Full Form MLA Member of Legislative Assembly RSS Really Simple Syndication, Rashtriya Swayamsevak Sangh UNESCO United Nations E...

Powered by Blogger.