Spring-приложение работает в IntelliJ IDE, но не в папке webapps Tomcat.

Вопрос или проблема

Мой код работает, когда я запускаю его обычно в IntelliJ IDE с сервером tomcat, предоставленным spring на порту 8080. Но когда я изменяю файл pom.xml, чтобы создать WAR-файл, и загружаю его на свой локальный сервер tomcat, который я настроил на порт 8081, он просто показывает код 403 для самого простого API, который даже не имеет безопасности. WAR-файл успешно создаётся и также загружается через приложение tomcat manager.
Я изменил имя своего WAR-файла на “logindemo”.

Когда я разворачиваю свой WAR-файл в webapps Tomcat, я запускаю этот URL в Postman с GET:-
http://localhost:8081/logindemo/auth/welcome

Ниже показан мой файл pom.xml, который я использую для создания WAR-файла.

<?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>3.0.8</version>
        <relativePath/>
    </parent>

    <groupId>com.gfg</groupId>
    <artifactId>springboot3-security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot3-security</name>
    <description>Демо-проект для Spring Boot 3 Security</description>

    <properties>
        <java.version>17</java.version>
        <jjwt.version>0.11.5</jjwt.version>
    </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-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>${jjwt.version}</version>
        </dependency>

    </dependencies>
    <packaging>war</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Ниже приведён мой SecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Autowired
    private JwtAuthFilter authFilter;

    @Bean
    public UserDetailsService userDetailsService() {
        return new UserInfoService(); // Убедитесь, что UserInfoService реализует UserDetailsService
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable()) // Отключить CSRF для безсостояних API
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/auth/welcome", "/auth/addNewUser", "/auth/generateToken").permitAll()
                        .requestMatchers("/auth/user/**").hasAuthority("ROLE_USER")
                        .requestMatchers("/auth/admin/**").hasAuthority("ROLE_ADMIN")
                        .anyRequest().authenticated() // Защитить все другие конечные точки
                )
                .sessionManagement(sess -> sess
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Без сессий
                )
                .authenticationProvider(authenticationProvider()) // Пользовательский поставщик аутентификации
                .addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class); // Добавить JWT фильтр

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // Кодирование паролей
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService());
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }
}

Ниже приведён мой Controller

@RestController
@RequestMapping("/auth")
public class UserController {

    @Autowired
    private UserInfoService service;

    @Autowired
    private JwtService jwtService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @GetMapping("/welcome")
    public String welcome() {
        return "Добро пожаловать, эта конечная точка не защищена";
    }

    @PostMapping("/addNewUser")
    public String addNewUser(@RequestBody UserInfo userInfo) {
        return service.addUser(userInfo);
    }

    @GetMapping("/user/userProfile")
    @PreAuthorize("hasAuthority('ROLE_USER')")
    public String userProfile() {
        return "Добро пожаловать в профиль пользователя";
    }

    @GetMapping("/admin/adminProfile")
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    public String adminProfile() {
        return "Добро пожаловать в профиль администратора";
    }

    @PostMapping("/generateToken")
    public String authenticateAndGetToken(@RequestBody AuthRequest authRequest) {

        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
        );
        if (authentication.isAuthenticated()) {
            return jwtService.generateToken(authRequest.getUsername());
        } else {
            throw new UsernameNotFoundException("Недействительный пользовательский запрос!");
        }
    }
}

Ниже приведён мой LoginApplication.java

@SpringBootApplication
public class LoginApplication extends SpringBootServletInitializer {

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

Ответ или решение

Когда ваше Spring-приложение работает в IntelliJ IDE, но не запускается в каталоге webapps вашего сервера Tomcat, необходимо обратить внимание на несколько важных аспектов. Давайте проанализируем вашу ситуацию, исходя из предоставленных данных.

1. Возвращаемый код 403

Ошибка 403 обычно указывает на то, что доступ к запрашиваемому ресурсу запрещен. Это может быть связано с настройками безопасности вашего приложения. В вашем SecurityConfig.java описаны правила доступа, которые могут блокировать определенные URL. Вы явно разрешаете доступ к /auth/welcome, однако, стоит проверить настройки, которые могут применяться ко всем запросам.

2. Параметры конфигурации

Обратите внимание на следующее:

  • CSRF: Вы отключили защиту от CSRF, что может быть уместно для статeless API, но также помните, что если у вас есть другие защищенные ресурсы, их доступ к ним также может быть заблокирован.
.csrf(csrf -> csrf.disable()) // Отключение CSRF для статeless API
  • Настройки авторизации: Убедитесь, что URL, к которому вы обращаетесь, действительно разрешён. Например, если поменяете структуру вашего приложения или добавите путь, не забудьте обновить правила в securityFilterChain.

3. Контекстный путь

При развертывании WAR-файла в Tomcat, ваше приложение может потребовать особого контекстного пути. Проверьте, что вы действительно используете корректный базовый URL. Убедитесь, что вы вызываете правильный URL:

http://localhost:8081/logindemo/auth/welcome

Это важный момент, потому что неправильный контекстный путь может вызвать проблемы с доступом.

4. Проверка структуры WAR-файла

Не забудьте убедиться, что WAR-файл, который вы загружаете, действительно содержит все нужные файлы и зависимости. Вы можете проверить содержимое WAR-файла, распаковав его, и убедиться, что WEB-INF и другие нужные директории присутствуют.

5. Логи Tomcat

Просмотрите логи Tomcat, чтобы узнать, почему доступ к этому ресурсу может быть запрещён. Логи могут содержать полезную информацию о том, какие именно ошибки происходят во время выполнения.

6. Dependencies в POM.xml

У вас в pom.xml есть зависимости, такие как spring-boot-starter-security и другие, которые могут быть отключены или работать неправильно в среде Tomcat, если они неправильно настроены или если существуют конфликты версий.

7. Spring Boot и развертывание в Tomcat

Убедитесь, что вы правильно наследуете SpringBootServletInitializer в вашем основном классе LoginApplication. У вас это сделано корректно, и это позволяет Tomcat знать, как обрабатывать Spring-приложение.

@SpringBootApplication
public class LoginApplication extends SpringBootServletInitializer { 
    // ваш код
}

8. Доступ к файловой системе

Убедитесь, что пользователь, под которым запущен сервер Tomcat, имеет соответствующие права доступа к файловой системе, чтобы запускать и читать ваш WAR-файл.

Резюме

Проанализировав перечисленные аспекты, вы сможете более точно определить причину ошибки 403. Убедитесь, что все элементы вашего проекта соответствующим образом настроены как для разработки, так и для продакшн-среды. Если все вышеперечисленные шаги не помогут, рассмотрите возможность создания минимального тестового приложения для изоляции проблемы.

Оцените материал
Добавить комментарий

Капча загружается...