Information Security 7 min read

How Spring Security Authenticates Users: A Step‑by‑Step Deep Dive

This tutorial walks through Spring Security's authentication flow in a Spring Boot 2.2.11 application, detailing filter execution, token creation, provider verification, custom DaoAuthenticationProvider configuration, and session management with code examples and diagrams.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How Spring Security Authenticates Users: A Step‑by‑Step Deep Dive

Environment: Spring Boot 2.2.11.RELEASE + JPA2

Security Process Flow

Security core is Filter; the diagram shows execution flow.

Detailed steps:

1.1 UsernamePasswordAuthenticationFilter inherits from AbstractAuthenticationProcessingFilter, first executes the parent doFilter method.

1.2 attemptAuthentication method is executed.

Here a UsernamePasswordAuthenticationToken object is instantiated with username and password for validation.

1.3 Authentication manager authenticates the request using ProviderManager.

The crucial for‑loop checks each AuthenticationProvider.

It first checks whether the AuthenticationProvider supports the authentication class.

<code>Class&lt;? extends Authentication&gt; toTest = authentication.getClass();</code>

The toTest corresponds to the token class used by UsernamePasswordAuthenticationFilter.

1.4 Provide a DaoAuthenticationProvider subclass to handle UsernamePasswordAuthenticationToken.

The parent class defines the supports method:

<code>public boolean supports(Class&lt;?&gt; authentication) {
    return (UsernamePasswordAuthenticationToken.class
            .isAssignableFrom(authentication));
}</code>

This shows only a DaoAuthenticationProvider subclass is needed for user verification.

1.5 Custom DaoAuthenticationProvider bean definition:

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

1.6 The for‑loop invokes provider.authenticate(authentication).

<code>result = provider.authenticate(authentication);</code>

This enters AbstractUserDetailsAuthenticationProvider.authenticate method in DaoAuthenticationProvider.

retrieveUser method is implemented in DaoAuthenticationProvider subclass.

If UserDetails is returned, the process continues.

1.7 Password verification step.

DaoAuthenticationProvider handles password checking.

After successful authentication, events are processed; exceptions are handled uniformly.

Security Login Authorization Configuration

Entity class

<code>@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;
}</code>

DAO

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

Security configuration

<code>@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());
    }
}</code>

Controller interfaces

<code>@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";
    }
}

// Interface to get online users count
@RestController
@RequestMapping("/sessions")
public class SessionController {

    @Resource
    private SessionRegistry sessionRegistry;

    @GetMapping("")
    public Object list() {
        return sessionRegistry.getAllPrincipals();
    }
}
</code>

Testing: login with user zs in Chrome, then in 360 browser, then refresh Chrome; the session is invalidated, demonstrating the maximum concurrent login limit works.

End of tutorial.

backendJavaAuthenticationSession ManagementSpring Securityspring-boot
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

login 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.