08 Spring Boot 3 Spring MVC Security
08 Spring Boot 3 Spring MVC Security
Security Overview
© luv2code LLC
You will learn how to …
• Secure Spring MVC Web App
• Not an A to Z reference … for that you can see Spring Security Reference Manual
http://www.luv2code.com/spring-security-reference-manual
fi
• Implemented using Servlet lters in the background
fi
• Two methods of securing an app: declarative and programmatic
fi
www.luv2code.com © luv2code LLC
Spring Security Overview
/mytopsecretstuff
Protected
Web
Resource
Web Spring
Browser Security
Filters
my app users
security passwords
configuration roles
Spring
Security
Filters
Spring
Security
Filters
Check if user id and
password are valid
• Authorizatio
n
fi
fi
• Provides separation of concerns between application code and security
fi
www.luv2code.com © luv2code LLC
Enabling Spring Security
1. Edit pom.xml and add spring-boot-starter-security
<dependency
>
<groupId>org.springframework.boot</groupId
>
<artifactId>spring-boot-starter-security</artifactId
>
</dependency>
for password
Built-in Dialog
from browser
bleh!
© luv2code LLC
Recap of our Game Plan
• Secure Spring MVC Web App
Leadership
Retreat
Page
/ role: MANAGER
Home Page
Admin
Holiday
Cruise
role: ADMIN
www.luv2code.com © luv2code LLC
Spring MVC Security
Project Setup
© luv2code LLC
Development Process
Step-
By-S
tep
1. Create project at Spring Initializr websit
1. Add Maven dependencies for Spring MVC Web App, Security, Thymeleaf
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> Spring MVC web support
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId> Thymeleaf view support
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> Spring Security support
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId> Thymeleaf Security support
</dependency>
...
@Controlle
@GetMapping("/"
return "home"
;
View name
}
<html>
<body>
</body>
</html>
• Option 1:
• Option 2:
www.luv2code.com
Configuring Basic Security
© luv2code LLC
Our Users
fi
fi
2. Add users, passwords and roles
fi
import org.springframework.context.annotation.Configuration
@Configuratio
n
fi
{id}encodedPasswor
ID Description
noop Plain text passwords
… …
{noop}test123
fi
@Configuratio
@Bea
n
.username("john"
.password("{noop}test123"
.roles("EMPLOYEE"
.build() ;
.username("mary"
)
.password("{noop}test123"
.roles("EMPLOYEE", "MANAGER"
.build()
;
.username("susan"
)
.password("{noop}test123"
)
© luv2code LLC
Spring Security - Default Login Form
Protected
Web
Resource
Web Spring
Browser Security
Filters
my app users
security passwords
configuration roles
Spring
Security
Filters
Our custom
login form
fi
2. Develop a Controller to show the custom login form
fi
@Bea
n
http.authorizeHttpRequests(configurer -
>
configure
Configure security of web paths
.anyRequest().authenticated(
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
n
http.authorizeHttpRequests(configurer -
Any request to the app
>
configure
.anyRequest().authenticated(
must be authenticated
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
n
http.authorizeHttpRequests(configurer -
>
configure
.anyRequest().authenticated(
We are customizing the form
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
n
>
.anyRequest().authenticated(
“/showMyLoginPage”
)
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
n
http.authorizeHttpRequests(configurer -
Login form should POST data to this
>
configure
URL for processing
.anyRequest().authenticated(
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
You can give ANY values
n
http.authorizeHttpRequests(configurer -
for this configuration.
>
configure
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
n
http.authorizeHttpRequests(configurer -
>
configure
.anyRequest().authenticated(
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
Allow everyone to see login page.
;
}
No need to be logged in.
fi
@Bea
n
http.authorizeHttpRequests(configurer -
>
configure
.anyRequest().authenticated(
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
Allow everyone to see login page.
;
}
No need to be logged in.
fi
@Bea We need to create a controller
n
http.authorizeHttpRequests(configurer -
>
configure “/showMyLoginPage”
.anyRequest().authenticated(
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
;
fi
@Bea
n
http.authorizeHttpRequests(configurer -
>
configure
.anyRequest().authenticated(
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
;
return http.build()
No Controller Request Mapping
;
}
required for this.
We get this for free :-)
@Controller
public class LoginController {
@GetMapping("/showMyLoginPage")
public String showMyLoginPage() {
return "plain-login";
}
}
@Controller
public class LoginController {
@GetMapping("/showMyLoginPage")
public String showMyLoginPage() {
return "plain-login";
}
}
@Controller
public class LoginController {
@GetMapping("/showMyLoginPage")
public String showMyLoginPage() {
return "plain-login";
TO DO
} View name
}
We need to create this file
src/main/resources/templates/plain-login.html
/authenticateTheUser
Spring
Security
Filters
You can give ANY values
for this configuration.
This is
fi
fi
s
fi
will read the form data and
• Password eld: password
fi
authenticate the user
…
<form action="#" th:action="@{/authenticateTheUser}"
method="POST">
<p>
User name: <input type="text" name="username" />
</p>
<p>
Password: <input type="password" name="password" />
</p>
</form>
…
…
<form action="#" th:action="@{/authenticateTheUser}"
method="POST">
<p>
User name: <input type="text" name="username" />
</p>
<p>
Password: <input type="password" name="password" />
</p>
</form>
…
…
<form action="#" th:action="@{/authenticateTheUser}"
method="POST">
<p>
User name: <input type="text" name="username" />
</p>
<p>
Password: <input type="password" name="password" />
</p>
</form>
…
Spring
Security
Filters
The @ symbol
is same thing as
Context Root
• If you change context path of app, then links will still work
m
No error message for failed login - What???
No error message???
www.luv2code.com
We need an error message … ASAP!!!
www.luv2code.com
Failed Login
www.luv2code.com
Failed Login
www.luv2code.com
Failed Login
Failed Login
Send back to
Login Form
www.luv2code.com
Failed Login
http://localhost:8080/myapp/showMyLoginPage?error
Failed Login
www.luv2code.com
Failed Login
http://localhost:8080/myapp/showMyLoginPage?error
Failed Login
www.luv2code.com
Development Process
Step-
By-S
tep
www.luv2code.com
Step 1: Modify form - check for error
File: src/main/resources/templates/plain-login.html
If error param
… then show message
<form …>
<div th:if="${param.error}">
</div>
</form>
…
www.luv2code.com
Step 1: Modify form - check for error
File: src/main/resources/templates/plain-login.html
If error param
… then show message
<form …>
<div th:if="${param.error}">
</div>
</form>
http://localhost:8080/myapp/showMyLoginPage?error
…
www.luv2code.com
Spring Security - Bootstrap Login Page
© luv2code LLC
Let’s Make our Login Form Beautiful
Before After
Bootstrap
• Focused on front-end UI
• For our project, you do not need any experience with Bootstrap :-)
• We’ll modify the form to use Spring Security for our project
www.w3schools.com/bootstrap
© luv2code LLC
Logging Out
Clear user’s
session
fi
2. Add logout button to home page
fi
@Bea
n
http.authorizeHttpRequests(configurer -
>
configure
.anyRequest().authenticated(
.formLogin(form -
>
for
m
.loginPage("/showMyLoginPage"
.loginProcessingUrl("/authenticateTheUser"
.permitAll(
)
for default URL
;
This is
</form>
GET method is disabled
by default
…
<form … th:action="…" method="…">
</div>
</form>
…
…
<form … th:action="…" method="…">
</div>
</form> http://localhost:8080/showMyLoginPage?logout
…
© luv2code LLC
Display User ID and Roles
User ID Role
File: home.html
User ID
User
Roles
© luv2code LLC
Our Example /leaders
Leadership
Retreat
Page
/ role: MANAGER
Home Page
Admin
Holiday
Cruise
role: ADMIN
www.luv2code.com © luv2code LLC
Development Process
Step-
By-S
tep
fi
fi
)
• General Syntax
Single role
requestMatchers("/").hasRole("EMPLOYEE")
requestMatchers("/leaders/**").hasRole("MANAGER")
requestMatchers("/systems/**").hasRole("ADMIN")
http.authorizeHttpRequests(configurer -
>
configure
.requestMatchers("/").hasRole("EMPLOYEE"
.requestMatchers("/leaders/**").hasRole("MANAGER"
.requestMatchers("/systems/**").hasRole("ADMIN"
.anyRequest().authenticated(
© luv2code LLC
Default Access Denied Page
HTML + CSS
http.authorizeHttpRequests(configurer -
>
configure
.requestMatchers(“/").hasRole("EMPLOYEE"
.exceptionHandling(configurer -
>
configure
.accessDeniedPage("/access-denied"
)
;
}
Our request mapping path
© luv2code LLC
Why Show These Links?
Since John is an
employee,
he shouldn’t be able
to see this content / links
…
<div sec:authorize="hasRole('MANAGER')">
<p>
<a th:href="@{/leaders}">
Leadership Meeting
</a>
(Only for Manager peeps)
</p>
</div>
…
<div sec:authorize="hasRole('ADMIN')">
<p>
<a th:href="@{/systems}">
IT Systems Meeting
</a>
(Only for Admin peeps)
</p>
</div>
m
Database Access
• So far, our user accounts were hard coded in Java source code
www.luv2code.com
Recall Our User Roles
www.luv2code.com
Database Support in Spring Security Out-o
f-the-
box
• Spring Security can read user account info from database
• By default, you have to follow Spring Security’s prede ned table schemas
fi
Spring JDBC
Security Code
www.luv2code.com
Customize Database Access with Spring Security
fi
• You will be responsible for developing the code to access the dat
www.luv2code.com
Database Support in Spring Security Out-o
f-the-
box
• Follow Spring Security’s prede ned table schemas
fi
Spring JDBC
Security Code
www.luv2code.com
Development Process
Step-
By-S
tep
1. Develop SQL Script to set up database tables
fi
3.Create JDBC properties le
fi
4. Update Spring Security Con guration to use JDBC
fi
www.luv2code.com
Default Spring Security Database Schema
www.luv2code.com
Step 1: Develop SQL Script to setup database tables
www.luv2code.com
Step 1: Develop SQL Script to setup database tables
The encoding algorithm i
The passwor
www.luv2code.com
Step 1: Develop SQL Script to setup database tables
CONSTRAINT `authorities_ibfk_1`
FOREIGN KEY (`username`)
REFERENCES `users` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
www.luv2code.com
Step 1: Develop SQL Script to setup database tables
“authorities” same as “roles”
<groupId>com.mysql</groupId
>
<artifactId>mysql-connector-j</artifactId
>
<scope>runtime</scope
>
</dependency
>
www.luv2code.com
Step 3: Create JDBC Properties File
File: application.properties
#
# JDBC connection properties
#
spring.datasource.url=jdbc:mysql://localhost:3306/employee_director
spring.datasource.username=springstuden
spring.datasource.password=springstuden
www.luv2code.com
Step 4: Update Spring Security to use JDBC
Inject data source
fi
@Configuratio
n
@Bea
n
JDBC authentication
www.luv2code.com
Spring Securit
Password Encryption
© luv2code LLC
Password Storage
• So far, our user passwords are stored in plaintext … yikes!
• Ok for getting started … but not for production / real-time project :-(
• bcryp
t
www.luv2code.com/why-bcrypt
www.luv2code.com/bcrypt-wiki-page
www.luv2code.com/password-hashing-best-practices
You have a plaintext password and you want to encrypt using bcrypt
fi
THAT’S IT … no need to change Java source code :-)
fi
Password column must be at least 68 chars wide
{bcrypt}encodedPasswor
{bcrypt} - 8 char
encodedPassword - 60 chars
('mary','{bcrypt}$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K',1),
('susan','{bcrypt}$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K',1);
User enters
plaintext password
Spring
Web
Security
Browser
Filters
Note
Because bcrypt is a
encryption algorith
2. Read the encoding algorithm id (bcrypt etc)
3. For case of bcrypt, encrypt plaintext password from login form (using salt from db password)
4. Compare encrypted password from login form WITH encrypted password from db
Custom Tables
© luv2code LLC
Default Spring Security Database Schema
fi
n
@Configuratio
n
@Bea
n
theUserDetailsManage
r
theUserDetailsManage
r
return theUserDetailsManager
;
@Configuratio
n
@Bea
n
fi
.setUsersByUsernameQuery("select user_id, pw, active from members where user_id=?")
theUserDetailsManage
r
fi
return theUserDetailsManager
;
…
Question mark “?
}
Parameter value will be the