DEV Community

Salad Lam
Salad Lam

Posted on

Spring Security: Redirect to login page if access pages which is for authorized user only

Code is extracted from my notice board example application, which uses Spring Security 5.6.7

Spring Security configuration class

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // must put more restricted rule at first
                .antMatchers("/manage/*/approve").hasAuthority("ADMIN")
                .antMatchers("/manage/**").hasAuthority("USER")
            .and()
                .httpBasic().disable()
                .formLogin()
                    .loginPage("/login")
                    .loginProcessingUrl("/loginHandler")
                    .failureUrl("/login?error=true")
                    .permitAll()
            .and()
                .logout().logoutSuccessUrl("/");
    }
    // ...
}
Enter fullscreen mode Exit fullscreen mode

The chain in SecurityFilterChain

(begin)
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CsrfFilter
LogoutFilter
UsernamePasswordAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
AnonymousAuthenticationFilter
SessionManagementFilter
ExceptionTranslationFilter
FilterSecurityInterceptor
(end)
Enter fullscreen mode Exit fullscreen mode

Flow of anonymous user access /manage page by browser

  • AnonymousAuthenticationToken is created by AnonymousAuthenticationFilter
  • Permission check is performed in FilterSecurityInterceptor. The user does not have the required authority "USER", so org.springframework.security.access.AccessDeniedException is thrown
  • The exception is caught by ExceptionTranslationFilter. In org.springframework.security.web.access.ExceptionTranslationFilter#handleAccessDeniedException method
private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
        FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);
    if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {
        if (logger.isTraceEnabled()) {
            logger.trace(LogMessage.format("Sending %s to authentication entry point since access is denied",
                    authentication), exception);
        }
        sendStartAuthentication(request, response, chain,
                new InsufficientAuthenticationException(
                        this.messages.getMessage("ExceptionTranslationFilter.insufficientAuthentication",
                                "Full authentication is required to access this resource")));
    }
    else {
        if (logger.isTraceEnabled()) {
            logger.trace(
                    LogMessage.format("Sending %s to access denied handler since access is denied", authentication),
                        exception);
        }
        this.accessDeniedHandler.handle(request, response, exception);
    }
}
Enter fullscreen mode Exit fullscreen mode
  • sendStartAuthentication method is called, and an instance of org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint is passed into FilterSecurityInterceptor on instance creation.
  • LoginUrlAuthenticationEntryPoint then delegates the control to org.springframework.security.web.DefaultRedirectStrategy. Finally reply the browser, redirect to "/login" page

Flow of user access /manage/4/approve page without "ADMIN" authority

  • org.springframework.security.access.AccessDeniedException is throw by FilterSecurityInterceptor and caught by ExceptionTranslationFilter
  • In org.springframework.security.web.access.ExceptionTranslationFilter#handleAccessDeniedException method, this time this.accessDeniedHandler.handle method is called
  • Directly return 403 Forbidden status

Add extra handling rules for anonymous user

Modifying Spring Security configuration class to following

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // must put more restricted rule at first
                .antMatchers("/manage/*/approve").hasAuthority("ADMIN")
                .antMatchers("/manage/**").hasAuthority("USER")
            .and()
                .httpBasic().disable()
                .formLogin()
                    .loginPage("/login")
                    .loginProcessingUrl("/loginHandler")
                    .failureUrl("/login?error=true")
                    .permitAll()
            .and()
                .logout().logoutSuccessUrl("/")
            .and()
                .exceptionHandling()
                    .defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), new AntPathRequestMatcher("/manage/1"))
                    .defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.GONE), new AntPathRequestMatcher("/manage/2"));
    }
    // ...
}
Enter fullscreen mode Exit fullscreen mode
  • When accessing "/manage/1", HTTP status 403 will return
  • When accessing "/manage/2", HTTP status 410 will return
  • When accessing "/manage", will redirect to "/login"

The property authenticationEntryPoint in ExceptionTranslationFilter becomes org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint.

Reference

  1. Handling Security Exceptions

Billboard image

Imagine monitoring that's actually built for developers

Join Vercel, CrowdStrike, and thousands of other teams that trust Checkly to streamline monitor creation and configuration with Monitoring as Code.

Start Monitoring

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay