skip to Main Content

I have spring config, where i am saving spring session in redis.

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    return http
            .csrf().disable()
            .cors(Customizer.withDefaults())
            .authorizeHttpRequests(auth -> {
                auth.requestMatchers("/api/v1/auth/register/**", "/api/v1/auth/login" , "/api/v1/profile/**").permitAll();
                auth.requestMatchers("/api/v1/profile").authenticated();
                auth.anyRequest().authenticated();
            })
            .sessionManagement(sessionManagement -> sessionManagement
                    .sessionCreationPolicy(IF_REQUIRED) //
                    .sessionFixation(SessionManagementConfigurer.SessionFixationConfigurer::newSession) //
                    .maximumSessions(1) //
                    .sessionRegistry(sessionRegistry())
            )
            //.exceptionHandling((ex) -> ex.authenticationEntryPoint(this.authEntryPoint))
            .logout(out -> out
                    .logoutUrl("/api/v1/auth/logout")
                    .invalidateHttpSession(true) // Invalidate all sessions after logout
                    .deleteCookies("JSESSIONID")
                    .addLogoutHandler(new CustomLogoutHandler(this.redisIndexedSessionRepository))
                    .logoutSuccessHandler((request, response, authentication) ->
                            SecurityContextHolder.clearContext()
                    )
            )
            .build();
}

I want "/api/v1/auth/profile/**" route to be accessible for anyone, without authentication, however i want "/api/v1/auth/profile" to be accessible only for authenticated users.

Using this config, however i am able to still access /api/v1/auth/profile` authenticated, what i have noticed, that if i use

SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
authentication.getPrincipal();

The authentication.isAuthenticated() results in true and authentication.getPrincipal() returns "anonymousUser" , could that be the reason why it passes to the route even tho authentication is required?

Thanks for help

2

Answers


  1. The reason why "/api/v1/auth/profile" is accessible even without authentication is because Spring Security treats it as a wildcard path that includes "/api/v1/auth/profile/**", thus permitting access to any sub-paths under "/api/v1/auth/profile".

    To resolve this issue, you can rearrange your "authorizeHttpRequests" configuration to explicitly permit access to "/api/v1/auth/profile" only for authenticated users, and allow "/api/v1/auth/profile/**" for anyone. Here’s how you can modify your configuration:

        .authorizeHttpRequests(auth -> {
        auth.requestMatchers("/api/v1/auth/register/**", "/api/v1/auth/login", "/api/v1/auth/profile/**").permitAll();
        auth.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll(); // Allow static resources without authentication
        auth.anyRequest().authenticated();
    })
    

    With this change, "/api/v1/auth/profile/**" will remain accessible to anyone, while "/api/v1/auth/profile" will only be accessible to authenticated users.

    Regarding your observation about the "Authentication" object having "isAuthenticated()" as "true" and the "principal" being "anonymousUser", this is the default behavior when a request is not authenticated. Spring Security automatically creates an anonymous authentication token to represent unauthenticated users. So even though "isAuthenticated()" returns "true", it doesn’t necessarily mean that the user is authenticated in the context of your application’s authentication mechanism.

    Login or Signup to reply.
  2. This minimal configuration works for me and should work for you as well.

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests( (requests) -> requests
                        .requestMatchers("/profile").authenticated()
                        .requestMatchers("/profile/**").permitAll()
                        .anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .logout(LogoutConfigurer::permitAll);
        return http.build();
    }
    

    To access /profile itself I need to be authenticated. To access /profile/nested I don’t.

    If I swap the order to

    .requestMatchers("/profile/**").permitAll()
    .requestMatchers("/profile").authenticated()
    

    then even the /profile endpoint is accessible by everyone. So the order matters.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search