π Why Customize Authentication?
Spring Security provides powerful default authentication mechanisms. But real-world applications often need custom logic β such as validating users from a database, LDAP, or a third-party service. Thatβs where a Custom Authentication Provider and UserDetailsService shine.
With this setup, you gain full control over how credentials are verified and user data is retrieved.

π¦ Maven Dependencies
Add the following to your pom.xml
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> |
π§© Project Structure
com.kscodes.springboot.security.customauth
βββ config/
β βββ SecurityConfig.java
βββ controller/
β βββ AuthController.java
βββ model/
β βββ AppUser.java
βββ security/
β βββ CustomAuthenticationProvider.java
β βββ CustomUserDetailsService.java
βββ CustomAuthApplication.java
π§ Step 1: Create a User Entity
AppUser.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.kscodes.springboot.security.customauth.model; public class AppUser { private String username; private String password; private String role; public AppUser(String username, String password, String role) { this.username = username; this.password = password; this.role = role; } // Getters and Setters } |
You can later connect this to a database.
π Step 2: Implement UserDetailsService
CustomUserDetailsService.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.kscodes.springboot.security.customauth.security; import com.kscodes.springboot.security.customauth.model.AppUser; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.*; import org.springframework.stereotype.Service; import java.util.Collections; @Service public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // Mock user if (!username.equals("john")) { throw new UsernameNotFoundException("User not found"); } AppUser user = new AppUser("john", "pass123", "ROLE_USER"); return new User( user.getUsername(), user.getPassword(), Collections.singleton(new SimpleGrantedAuthority(user.getRole())) ); } } |
π‘οΈ Step 3: Create a Custom Authentication Provider
CustomAuthenticationProvider.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
package com.kscodes.springboot.security.customauth.security; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.*; import org.springframework.stereotype.Component; @Component public class CustomAuthenticationProvider implements AuthenticationProvider { private final UserDetailsService userDetailsService; public CustomAuthenticationProvider(CustomUserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { String username = authentication.getName(); String password = authentication.getCredentials().toString(); UserDetails user = userDetailsService.loadUserByUsername(username); if (!user.getPassword().equals(password)) { throw new BadCredentialsException("Invalid credentials"); } return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities()); } @Override public boolean supports(Class<?> authType) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authType); } } |
π Step 4: Configure Spring Security
SecurityConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
package com.kscodes.springboot.security.customauth.config; import com.kscodes.springboot.security.customauth.security.CustomAuthenticationProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { private final CustomAuthenticationProvider authProvider; public SecurityConfig(CustomAuthenticationProvider authProvider) { this.authProvider = authProvider; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf().disable() .authenticationProvider(authProvider) .authorizeHttpRequests(auth -> auth .requestMatchers("/login").permitAll() .anyRequest().authenticated() ) .formLogin(); // or httpBasic() return http.build(); } @Bean public AuthenticationManager authManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); } } |
π Step 5: Authentication Controller
AuthController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.kscodes.springboot.security.customauth.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class AuthController { @GetMapping("/home") public String home() { return "Welcome to the secured home endpoint!"; } } |
π Test It Out
Run the application and test login:
- Visit:
http://localhost:8080/home
- Browser will prompt for login
- Enter
john / pass123
β Youβll see the secured content if authentication succeeds using your Custom Authentication Provider and UserDetailsService.
β οΈ Best Practices
- Use
PasswordEncoder
(e.g.BCryptPasswordEncoder
) instead of plain password comparison. - Fetch users from a real database using repositories.
- Add logging and throttling to prevent brute force attacks.