Form login vs stateless APIs

Spring Security · lesson 6 of 31 · 3 min read

Pick the right login style for browsers and for machines, and run both in one app.

Open this lesson in the learning hub

Key points

  • Browser app? Use formLogin. The session holds your identity; the JSESSIONID cookie carries it after login.
  • Machine client? No session. Send a token on every request and keep the server stateless so any instance can serve it.
  • httpBasic resends user:password (base64, not encrypted) on every call. Acceptable over TLS internally, weak for public APIs.
  • One app, both styles: two SecurityFilterChain beans — /api/** stateless first, the UI chain second.
  • Always add logout for the session half. Killing the session is what actually logs a browser user out.

Example

@Bean
@Order(1)
SecurityFilterChain api(HttpSecurity http) throws Exception {
    return http
        .securityMatcher("/api/**")
        .csrf(csrf -> csrf.disable())
        .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(a -> a.anyRequest().authenticated())
        .httpBasic(Customizer.withDefaults())
        .build();
}

@Bean
@Order(2)
SecurityFilterChain web(HttpSecurity http) throws Exception {
    return http
        .authorizeHttpRequests(a -> a
            .requestMatchers("/", "/css/**", "/js/**", "/login").permitAll()
            .anyRequest().authenticated())
        .formLogin(form -> form
            .loginPage("/login")
            .defaultSuccessUrl("/dashboard", true)
            .failureUrl("/login?error"))
        .logout(out -> out
            .logoutSuccessUrl("/")
            .deleteCookies("JSESSIONID"))
        .build();
}

Cookies and sessions for browsers, tokens and STATELESS for APIs — never mix them on one path.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.