Spring Boot Course
Spring Security offers lots of capabilities to perform JDBC authentication using an existing DataSource configuration.
In JDBC based authentication user's authentication and authorization information are stored in the database.
Lets consider an example where we will create users with a different role and authenticate & authorized every request based on the logged-in user information stored in the PostgreSQL database.
Let's use the JdbcUserDetailsManager for authentication and also lets restrict the DELETE endpoint to ADMIN alone as a part of authorization.
package com.praveen.employee.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; 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.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.provisioning.JdbcUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import javax.sql.DataSource; import static org.springframework.security.config.Customizer.withDefaults; @Configuration @EnableWebSecurity public class SecurityConfig { private static final String ADMIN = "ADMIN"; private static final String USER = "USER"; private final DataSource dataSource; public SecurityConfig(DataSource dataSource) { this.dataSource = dataSource; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers("/", "/api/v1/auth/register").permitAll() .requestMatchers(HttpMethod.DELETE, "/api/v1/employees/{id}").hasRole(ADMIN) .requestMatchers("/api/v1/employees").hasAnyRole(USER, ADMIN) .anyRequest().authenticated() ) .httpBasic(withDefaults()); return http.build(); } @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public JdbcUserDetailsManager jdbcUserDetailsManager(DataSource dataSource) { return new JdbcUserDetailsManager(dataSource); } @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class); authenticationManagerBuilder .jdbcAuthentication() .dataSource(dataSource) .passwordEncoder(passwordEncoder()); return authenticationManagerBuilder.build(); } }
Setup Tables in PostgreSQL DB
Tables to be created
CREATE TABLE users ( username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, enabled TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (username) ); CREATE TABLE authorities ( username VARCHAR(50) NOT NULL, authority VARCHAR(50) NOT NULL, FOREIGN KEY (username) REFERENCES users(username) ); CREATE UNIQUE INDEX ix_auth_username on authorities (username,authority);
Create a User model class which holds all the attributes of a user.
package com.praveen.employee.bo; public record User(String userName, String password, String role) { }
AuthController class exposes the REST endpoint for the application user.
- /register-user is used to create a new user and it is accessed by everyone.
package com.praveen.employee.controller; import com.praveen.employee.bo.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.provisioning.JdbcUserDetailsManager; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; @RestController public class AuthController { private final JdbcUserDetailsManager jdbcUserDetailsManager; private final BCryptPasswordEncoder passwordEncoder; public AuthController(JdbcUserDetailsManager jdbcUserDetailsManager, BCryptPasswordEncoder passwordEncoder) { this.jdbcUserDetailsManager = jdbcUserDetailsManager; this.passwordEncoder = passwordEncoder; } @PostMapping(value = "/api/v1/auth/register") public void register(@RequestBody User user) { List<GrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_" + user.role())); String encodedPassword = passwordEncoder.encode(user.password()); org.springframework.security.core.userdetails.User securedUser = new org.springframework.security.core.userdetails.User(user.userName(), encodedPassword, authorities); jdbcUserDetailsManager.createUser(securedUser); } }
Let's invoke the endpoint URL http://localhost:8080/api/v1/auth/register to register user with ADMIN and USER roles.


Let's invoke the endpoint URL http://localhost:8080/api/v1/employees and we see 401 unauthorized error.

Now let's invoke the endpoint URL http://localhost:8080/api/v1/employees with credentials

