Master Spring Security: Step‑by‑Step Authentication Flow in Spring Boot

This article walks through the Spring Security authentication flow in a Spring Boot 2.2.11 application, detailing filter execution, token creation, provider selection, custom DaoAuthenticationProvider configuration, and session management, complemented by code snippets and diagrams for each step.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring Security: Step‑by‑Step Authentication Flow in Spring Boot

Environment: Spring Boot 2.2.11.RELEASE + JPA2

Security Process Handling

The core of Spring Security is the Filter chain; the diagram below shows the execution flow.

Security filter chain diagram
Security filter chain diagram

Detailed steps:

1.1 The parent class of UsernamePasswordAuthenticationFilter is AbstractAuthenticationProcessingFilter, which first executes the doFilter method from the parent class.

UsernamePasswordAuthenticationFilter parent execution
UsernamePasswordAuthenticationFilter parent execution

1.2 The attemptAuthentication method of UsernamePasswordAuthenticationFilter is invoked.

Attempt authentication
Attempt authentication

Here a UsernamePasswordAuthenticationToken object is instantiated with the username and password for subsequent verification.

1.3 Authentication proceeds with this.getAuthenticationManager().authenticate(authRequest), using the system‑provided ProviderManager.

ProviderManager authentication
ProviderManager authentication

The crucial part is the following for‑loop that iterates over AuthenticationProviders.

AuthenticationProvider loop
AuthenticationProvider loop

It first checks whether each AuthenticationProvider supports the authentication type.

Class<? extends Authentication> toTest = authentication.getClass();

The variable toTest corresponds to the class used by UsernamePasswordAuthenticationFilter.

Authentication class check
Authentication class check

1.4 To validate username and password we need an AuthenticationProvider that supports UsernamePasswordAuthenticationToken, such as a custom DaoAuthenticationProvider subclass.

DaoAuthenticationProvider subclass
DaoAuthenticationProvider subclass

The parent class defines the supports method:

public boolean supports(Class<?> authentication) {
    return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}

Thus providing a DaoAuthenticationProvider subclass is sufficient for user verification.

1.5 Custom DaoAuthenticationProvider bean definition:

@Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
    DaoAuthenticationProvider daoAuthen = new DaoAuthenticationProvider();
    daoAuthen.setPasswordEncoder(passwordEncoder());
    daoAuthen.setUserDetailsService(userDetailsService());
    daoAuthen.setHideUserNotFoundExceptions(false);
    return daoAuthen;
}

1.6 The for‑loop then calls: result = provider.authenticate(authentication); This invokes the authenticate method of the parent class AbstractUserDetailsAuthenticationProvider.

AbstractUserDetailsAuthenticationProvider authenticate
AbstractUserDetailsAuthenticationProvider authenticate

The retrieveUser method is implemented in the DaoAuthenticationProvider subclass.

retrieveUser implementation
retrieveUser implementation

If a UserDetails object is returned, the process proceeds to password verification.

1.7 Password verification is performed by DaoAuthenticationProvider.

Password verification
Password verification

The subsequent steps handle successful authentication events or unified exception handling.

Security Login Authorization Configuration

Entity class

@Entity
@Table(name = "T_USERS")
public class Users implements UserDetails, Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name = "system-uuid", strategy = "uuid")
    private String id;
    private String username;
    private String password;
}

DAO

public interface UsersRepository extends JpaRepository<Users, String>, JpaSpecificationExecutor<Users> {
    Users findByUsernameAndPassword(String username, String password);
    Users findByUsername(String username);
}

Security configuration

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UsersRepository ur;
    @Resource
    private LogoutSuccessHandler logoutSuccessHandler;

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            Users user = ur.findByUsername(username);
            if (user == null) {
                throw new UsernameNotFoundException("用户名不存在");
            }
            return user;
        };
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                return rawPassword.equals(encodedPassword);
            }
            @Override
            public String encode(CharSequence rawPassword) {
                return rawPassword.toString();
            }
        };
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthen = new DaoAuthenticationProvider();
        daoAuthen.setPasswordEncoder(passwordEncoder());
        daoAuthen.setUserDetailsService(userDetailsService());
        daoAuthen.setHideUserNotFoundExceptions(false);
        return daoAuthen;
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    public HttpSessionEventPublisher httpSessionEventPublisher() {
        return new HttpSessionEventPublisher();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
            .antMatchers("/pos/**").authenticated()
            .and()
            .formLogin()
            .loginPage("/sign/login")
            .and()
            .logout()
            .logoutSuccessHandler(logoutSuccessHandler)
            .logoutUrl("/sign/logout");

        http.sessionManagement()
            .maximumSessions(1)
            .expiredUrl("/sign/login?expired")
            .sessionRegistry(sessionRegistry());
    }
}

Controller interfaces

@Controller
public class LoginController {
    @RequestMapping("/sign/login")
    public String login() {
        return "login";
    }
}

@RestController
@RequestMapping("/sign")
public class LogoutController {
    @GetMapping("/logout")
    public Object logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(null);
        SecurityContextHolder.clearContext();
        return "success";
    }
}

@RestController
@RequestMapping("/pos")
public class PosController {
    @GetMapping("")
    public Object get() {
        return "pos success";
    }
}

// Get online user count
@RestController
@RequestMapping("/sessions")
public class SessionController {
    @Resource
    private SessionRegistry sessionRegistry;

    @GetMapping("")
    public Object list() {
        return sessionRegistry.getAllPrincipals();
    }
}

Testing shows that when a user logs in from different browsers, the configured maximum concurrent session limit (1) is enforced, and subsequent logins invalidate the previous session.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentSpring BootAuthenticationSession Managementspring-security
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.