'springboot war working with embedded tomcat but not with external tomcat

I have been working on a springboot 2.3.4 application with spring security. It's working with embedded tomcat. We want to make it work with external application servers like tomcat and jboss. I have tried to deploy the war file in external tomcat server. when hitting the endpoints I receive 404 error code. Here is my pom file:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.teradata</groupId>
    <artifactId>modelstudio</artifactId>
    <version>1</version>
    <name>modelstudio</name>
    <packaging>war</packaging>
    <description>ModelStudio Application</description>

    <properties>
       <java.version>13</java.version>
       <start-class>
            com.teradata.modelstudio.ModelStudioApplication
       </start-class>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.skyscreamer</groupId>
                    <artifactId>jsonassert</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.teradata.jdbc</groupId>
            <artifactId>terajdbc4</artifactId>
            <version>17</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.json</groupId>
            <artifactId>json</artifactId>
            <version>20200518</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.json</groupId>
                    <artifactId>json</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
    <finalName>modelstudio</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>static/**</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <excludes>
                    <exclude>ModelStudioUI/**</exclude>
                    <exclude>static/**</exclude>
                </excludes>
            </resource>
        </resources>
    </build>

</project>

My JWT Entry point is:

package com.teradata.modelstudio.filter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.teradata.modelstudio.service.CustomAuthenticationProviderService;
import com.teradata.modelstudio.utils.JwtUtil;
import com.teradata.modelstudio.utils.Logger;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.regex.Pattern;

@Component
public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private CustomAuthenticationProviderService service;
    
    @Autowired JwtUserDetailsService jwtUserDetailsService;


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        
        String requestURI = httpServletRequest.getRequestURI();
        Logger.info("==================="+requestURI);
        Pattern mega = Pattern.compile(
                "\\/*.css|\\/*.js|\\/*.png|\\/*.jpg|\\/assets\\/*.*|\\/*.woff2|\\/*.woff|\\/*.ttf|\\/swagger-resources\\/*|/servicemanagement/configuration/*|/servicemanagement/etlframework/*|/servicemanagement/refreshCache/*");

        // System.out.println(requestURI);

        if (httpServletRequest.getMethod().equals("OPTIONS")// || requestURI.equalsIgnoreCase("/index.html")
                || requestURI.equalsIgnoreCase("/favicon.ico") || mega.matcher(requestURI).find() || requestURI.equalsIgnoreCase("/")) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        // TODO Auto-generated method stub
        String url = httpServletRequest.getRequestURL().toString();
//      System.out.println("+++URL in jwt request filter:"+url);
        if (url.contains("auth") || url.contains("dbcontent") || url.contains("favico")) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        String requestTokenHeader = httpServletRequest.getHeader("Authorization");

        String username = null;
        String password = null;
        String serverUrl = null;
        String dbName = null;
        String authType = null;
        String jwtToken = null;

        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtUtil.getUsernameFromJWT(jwtToken);
                password = jwtUtil.getPasswordFromJWT(jwtToken);
                serverUrl = jwtUtil.getServerUrlFromJWT(jwtToken);
                dbName = jwtUtil.getDbNameFromJWT(jwtToken);
                authType = jwtUtil.getAuthTypeFromJWT(jwtToken);
                jwtUtil.validateToken(jwtToken, username);
            } catch (Exception e) {
                throw e;
            }

        } else {
            throw new IOException("Invalid Authorization");
        }
        
        
        SecurityContextHolder.getContext().setAuthentication(null);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = jwtUserDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwtToken, username)) {
                
                CustomUserPrincipal customUserPrincipal = new CustomUserPrincipal(username, password, serverUrl, dbName, authType);

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                        new UsernamePasswordAuthenticationToken(customUserPrincipal, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}


My Security config file:

package com.teradata.modelstudio.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.teradata.modelstudio.filter.JwtFilter;
import com.teradata.modelstudio.payload.JwtAuthenticationResponse;
import com.teradata.modelstudio.security.JwtAuthenticationEntryPoint;
import com.teradata.modelstudio.service.CustomAuthenticationProviderService;

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomAuthenticationProviderService authenticationProviderService;

    @Autowired
    private JwtFilter jwtFilter;
    
    @Autowired
    JwtAuthenticationEntryPoint unauthorizedHandler;
    
    @Autowired
    private com.teradata.modelstudio.filter.SimpleCORSFilter simpleCORSFilter;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
        .antMatchers(
                HttpMethod.GET,
              "/",
              "/*.html",
              "/*.svg",
              "/*.png",
              "/*.woff",
              "/*.woff2",
              "/*.svg",
              "/*.jpg",
              "/*.json",
              "/favicon.ico",
              "/**/*.html",
              "/**/*.css",
              "/**/*.js",
              "/**/*.svg",
              "/**/*.png",
              "/**/*.jpg",
              "/**/*.ico",
              "/**/*.icons",
              "/resources/**",
              "/assets/",
              "/static/**",
              "/h2-console/**"
        ).permitAll()
        .antMatchers("/auth/**", "/dbcontent/**")
                .permitAll()
                .anyRequest().authenticated()
                .and().exceptionHandling().and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(simpleCORSFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProviderService);
    }
    
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

One of my controller classes are:

package com.teradata.modelstudio.controllers;

import java.io.File;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.teradata.modelstudio.exception.ModelStudioException;
import com.teradata.modelstudio.utils.Logger;
import org.apache.commons.io.FileUtils;
import org.json.JSONObject;

@RestController
@RequestMapping("dbcontent")
public class DBContentController {
    
    @GetMapping(path="servers", produces = "application/json")
    public ResponseEntity<?> getServers() {
        try {
            String dbConFilePath = System.getProperty("user.dir")+File.separator+"db-con";
            File file = new File(dbConFilePath);
            String content = FileUtils.readFileToString(file, "utf-8");
            // Convert JSON string to JSONObject
            JSONObject fileContents = new JSONObject(content);
            return ResponseEntity.ok(fileContents.toString());
        } catch(Exception e) {
            Logger.error("Unable to get servers: "+e.getMessage());
            throw new ModelStudioException(e.getMessage());
        }
    }
    
}

Here is my main class

package com.teradata.modelstudio;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication(
          exclude = { DataSourceAutoConfiguration.class, 
             HibernateJpaAutoConfiguration.class,
             DataSourceTransactionManagerAutoConfiguration.class, 
             })
public class ModelstudioApplication extends SpringBootServletInitializer{

    public static void main(String[] args) {
        SpringApplication.run(ModelstudioApplication.class, args);
    }
    
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(ModelstudioApplication.class);
    }

}

With embedded tomcat serever, I hit:

localhost:8080/dbcontent/servers

and I get the results while on external I do the following:

localhost:8080/modelstudio/dbcontent/servers

and I get 404.

Can anyone please help and advise me how to resolve this issue with external server like tomcat.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source