05 Spring Boot Rest Security
05 Spring Boot Rest Security
Security Overview
You will learn how to …
• Secure Spring Boot REST APIs
www.luv2code.com
Practical Results
• Cover the most common Spring Security tasks that you will need on daily projects
• Not an A to Z reference … for that you can see Spring Security Reference Manual
http://www.luv2code.com/spring-security-reference-manual
www.luv2code.com
Spring Security Model
www.luv2code.com
Spring Security with Servlet Filters
www.luv2code.com
Spring Security Overview
/mytopsecretstuff
Protected
Web
Resource
Web Spring
Browser Security
Filters
my app users
security passwords
configuration roles
www.luv2code.com
Spring Security in Action
Spring
Security
Filters
www.luv2code.com
Spring Security in Action
Does user have
authorized role?
Spring
Security
Filters
Check if user id and
password are valid
www.luv2code.com
Security Concepts
• Authentication
• Authorization
www.luv2code.com
Declarative Security
www.luv2code.com
Programmatic Security
www.luv2code.com
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>
www.luv2code.com
Secured Endpoints
• Now when you access your application
www.luv2code.com
Spring Security configuration
• You can override default user name and generated password
File: src/main/resources/application.properties
spring.security.user.name=scott
spring.security.user.password=test123
www.luv2code.com
Authentication and Authorization
• In-memory
• JDBC users
passwords
• LDAP roles
• Custom / Pluggable
• others …
www.luv2code.com
Configuring Basic Security
m
Our Users
www.luv2code.com
Development Process
Step-
By-S
tep
www.luv2code.com
Step 1: Create Spring Security Configuration
File: DemoSecurityConfig.java
import org.springframework.context.annotation.Configuration;
@Configuration
public class DemoSecurityConfig {
www.luv2code.com
Spring Security Password Storage
• In Spring Security, passwords are stored using a specific format
{id}encodedPassword
ID Description
noop Plain text passwords
… …
www.luv2code.com
Password Example
The encoding
The password
algorithm id
{noop}test123
www.luv2code.com
Step 2: Add users, passwords and roles
File: DemoSecurityConfig.java
@Configuration
public class DemoSecurityConfig {
@Bean
public InMemoryUserDetailsManager userDetailsManager() {
www.luv2code.com
Restrict Access Based on Roles
m
Our Example
www.luv2code.com
Restricting Access to Roles
• General Syntax Restrict access to a
given path
“/api/employees”
www.luv2code.com
Restricting Access to Roles
Restrict access to a
Specify HTTP method: given path
GET, POST, PUT, DELETE … “/api/employees”
requestMatchers(<< add HTTP METHOD to match on >>, << add path to match on >>)
.hasRole(<< authorized roles >>)
Single role
www.luv2code.com
Restricting Access to Roles
requestMatchers(<< add HTTP METHOD to match on >>, << add path to match on >>)
.hasAnyRole(<< list of authorized roles >>)
www.luv2code.com
Authorize Requests for EMPLOYEE role
requestMatchers(HttpMethod.GET, "/api/employees").hasRole("EMPLOYEE")
requestMatchers(HttpMethod.GET, "/api/employees/**").hasRole("EMPLOYEE")
The ** syntax:
match on all sub-paths
www.luv2code.com
Authorize Requests for MANAGER role
requestMatchers(HttpMethod.POST, "/api/employees").hasRole("MANAGER")
requestMatchers(HttpMethod.PUT, "/api/employees").hasRole("MANAGER")
www.luv2code.com
Authorize Requests for ADMIN role
requestMatchers(HttpMethod.DELETE, "/api/employees/**").hasRole("ADMIN")
www.luv2code.com
Pull It Together
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(configurer ->
configurer
.requestMatchers(HttpMethod.GET, "/api/employees").hasRole("EMPLOYEE")
.requestMatchers(HttpMethod.GET, "/api/employees/**").hasRole("EMPLOYEE")
Use HTTP Basic .requestMatchers(HttpMethod.POST, "/api/employees").hasRole("MANAGER")
Authentication .requestMatchers(HttpMethod.PUT, "/api/employees").hasRole("MANAGER")
.requestMatchers(HttpMethod.DELETE, "/api/employees/**").hasRole("ADMIN"));
return http.build();
}
www.luv2code.com
Cross-Site Request Forgery (CSRF)
• Spring Security can protect against CSRF attacks
www.luv2code.com
When to use CSRF Protection?
• The Spring Security team recommends
www.luv2code.com
Pull It Together
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(configurer ->
configurer
.requestMatchers(HttpMethod.GET, "/api/employees").hasRole("EMPLOYEE")
.requestMatchers(HttpMethod.GET, "/api/employees/**").hasRole("EMPLOYEE")
.requestMatchers(HttpMethod.POST, "/api/employees").hasRole("MANAGER")
.requestMatchers(HttpMethod.PUT, "/api/employees").hasRole("MANAGER")
.requestMatchers(HttpMethod.DELETE, "/api/employees/**").hasRole("ADMIN"));
// disable Cross Site Request Forgery (CSRF) In general, CSRF is not required for
http.csrf(csrf -> csrf.disable());
stateless REST APIs that use
return http.build(); POST, PUT, DELETE and/or PATCH
}
www.luv2code.com
Spring Security
User Accounts Stored in Database
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
Spring JDBC
Security Code
www.luv2code.com
Customize Database Access with Spring Security
• You will be responsible for developing the code to access the data
www.luv2code.com
Database Support in Spring Security Out-o
f-the-
box
• Follow Spring Security’s predefined table schemas
Spring JDBC
Security Code
www.luv2code.com
Development Process
Step-
By-S
tep
1. Develop SQL Script to set up database tables
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 id
The password
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”
www.luv2code.com
Step 2: Add Database Support to Maven POM file
www.luv2code.com
Step 3: Create JDBC Properties File
File: application.properties
#
# JDBC connection properties
#
spring.datasource.url=jdbc:mysql://localhost:3306/employee_directory
spring.datasource.username=springstudent
spring.datasource.password=springstudent
www.luv2code.com
Step 4: Update Spring Security to use JDBC
Inject data source
Auto-configured by Spring Boot
@Configuration
public class DemoSecurityConfig {
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
www.luv2code.com
Spring Security
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 :-(
• bcrypt
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
('mary','{bcrypt}$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K',1),
('susan','{bcrypt}$2a$04$eFytJDGtjbThXa80FyOOBuFdK2IwjyWefYkMpiBEFlpBwDH.5PM0K',1);
User enters
plaintext password
Spring
REST
Security
Client
Filters
Note:
The password from db is
NEVER decrypted
Because bcrypt is a
1. Retrieve password from db for the user one-way
encryption algorithm
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
© luv2code LLC
Default Spring Security Database Schema
@Configuration
public class DemoSecurityConfig {
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager theUserDetailsManager = new JdbcUserDetailsManager(dataSource);
theUserDetailsManager
.setUsersByUsernameQuery("select user_id, pw, active from members where user_id=?");
theUserDetailsManager
.setAuthoritiesByUsernameQuery("select user_id, role from roles where user_id=?");
return theUserDetailsManager;
}
@Configuration
public class DemoSecurityConfig {
@Bean
public UserDetailsManager userDetailsManager(DataSource dataSource) {
JdbcUserDetailsManager theUserDetailsManager = new JdbcUserDetailsManager(dataSource);
theUserDetailsManager
.setAuthoritiesByUsernameQuery("select user_id, role from roles where user_id=?");
How to find roles
return theUserDetailsManager;
}
…
Question mark “?”
}
Parameter value will be the
user name from login