6

We have a web application built using spring boot and we are using spring security to manage authentication. Authentication is right now happening using basic mode.

We are using UserDetailsService to handle authentication. When we are running the application directly from STS using the embedded tomcat, when the user is not enabled, we receive the following error with status code 401.

{"timestamp":1485512173312,"status":401,"error":"Unauthorized","message":"User is disabled","path":"/trex/user"}

But, when I package the application into a war and deploy it to Tomcat we no longer receive the json error. Instead, we receive a html error similar to the following.

<html><head><title>Apache Tomcat/7.0.75 - Error report</title><style><!--H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}--></style> </head><body><h1>HTTP Status 401 - User is disabled</h1><HR size="1" noshade="noshade"><p><b>type</b> Status report</p><p><b>message</b> <u>User is disabled</u></p><p><b>description</b> <u>This request requires HTTP authentication.</u></p><HR size="1" noshade="noshade"><h3>Apache Tomcat/7.0.75</h3></body></html>

Is it possible to stop handling the standard error codes and let the json data pass back as it is?

Edited: Spring security config

/**
 * Security configuration. Defines configuration methods and security
 * constraints. Autowired bean definitions can be found in {@link AuthAppConfig}
 * 

 *
 */
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    Logger logger = LoggerFactory.getLogger(getClass());

    /*userDetailsService
     * @Autowired TRexUserDetailsService customAuthenticationEntryPoint;
     * 
     */
    @Autowired
    TRexUserDetailsService userDetailsService;

    @Autowired
    private AmazonProperties amazonProperties;

    /**
     * Configuration related to authentication and authorization
     * 
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        AuthenticationEntryPoint entryPoint = new CustomAuthenticationEntryPoint();
        LogoutHandler myLogoutHandler = new MyLogoutSuccessHandler();

        logger.info("Configuring the security of the application");

        http

                // Basic Authentication
                .httpBasic().authenticationEntryPoint(entryPoint)
                .and().authorizeRequests()
                // Allowing index.html, login.html and / and
                // resetforgotpassword.html and all forgot user mappings since
                // we are using 2 way handshake for this purpose
                .antMatchers("/activate/user","/activate/token/**"
                        ,"/validate","/validateEmail","/user/resetpassword/**","/user/forgotpassword"
                        ,"/admin/masters/*.js","/admin/modeller/*.js", "/login.html", "/index.html","/activateuser.html","/savepassword.html","/forgotpassword.html","/","/lib/**"
                        ,"/directives/paginated-control.js"
                        , "/expense/js/summary.js"
                        ,"/travel/js/directive/uploadDirecetive.js"
                        ,"/travel/js/travelData.js"
                        ,"/travel/js/summary.js"
                        ,"/directives/paginated-control.js"
                        ,"/loginform.html"
                        ,"/infoDirective.html"
                        )
                .permitAll()
                // Admin ADMIN,
                .antMatchers("/admin/**").access("hasRole('ADMIN')")
                // Allow SA
                .antMatchers("/SA/**").access("hasRole('SUPER')")
                //Allow REPORTS
                .antMatchers("/reports/**").access("hasRole('REPORT')")
                // All other requests are to be authenticated.
                .anyRequest().authenticated().and()

                // Filter to prevent CSRF attacks.
                .addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class).csrf()
                // Used to check which requests are to be checked for csrf
                // protection
                .requireCsrfProtectionMatcher(new CsrfRequestMatcher())
                // Add the renamed CSRF token header repository
                .csrfTokenRepository(csrfTokenRepository())
                .and()
                // Configuration for /logout
                .logout()
                // Logout requires form submit. Bypassing the same.
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/#/login")
                .addLogoutHandler(myLogoutHandler)
                //Session management to store session registry
                .and().sessionManagement()
                .maximumSessions(100)
                .maxSessionsPreventsLogin(false)
                .expiredUrl("/#/login")
                .sessionRegistry(sessionRegistry());

    }

    /**
     * Session Registry to store sessions
     * @return
     */
    @Bean
    SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    /**
     * Session event publisher.
     * @return
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Bean
    public static ServletListenerRegistrationBean httpSessionEventPublisher() {        
        return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
    }
    /**
     * This method specifies that the header name to be used is XSRF token
     * instead of the default CSRF cause Angular names the token as XSRF instead
     * of CSRF
     * 
     * @return
     */
    private CsrfTokenRepository csrfTokenRepository() {
        logger.info("Configuring the CSRF token name as XSRF-TOKEN");
        HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
        repository.setHeaderName("X-XSRF-TOKEN");
        return repository;
    }

    /**
     * Configuration for Custom Authentication
     * 
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth, BCryptPasswordEncoder passwordEncoder)
            throws Exception {
        logger.info("Configuring the authentication to custom User Details Service");

        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);

        logger.info("Configuring the authentication to custom User Details Service");
    }

    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        // TODO Auto-generated method stub
        return super.authenticationManagerBean();
    }

    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() throws Exception {
        BasicAuthenticationEntryPoint entryPoint = new CustomAuthenticationEntryPoint();
        entryPoint.setRealmName("Spring");

        return entryPoint;

    }
}
Pavan Andhukuri
  • 1,547
  • 3
  • 23
  • 49

1 Answers1

4

You should be able to fix this within your CustomAuthenticationEntryPoint. See this answer on how to avoid Tomcat's default error pages.

Assuming your custom EntryPoint is an extension of BasicAuthenticationEntryPoint (as the bean would suggest), see here: org/springframework/security/web/authentication/www/BasicAuthenticationEntryPoint.java/#59

You'll need to replace:

response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());

with:

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Community
  • 1
  • 1
mikeapr4
  • 2,830
  • 16
  • 24