Весна не вызывает пользовательскую аутентификацию для моего метода loadUserByUsername.

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

Я реализовывал механизм аутентификации для своего приложения. Поэтому, если пользователь является администратором и предоставлено правильное имя пользователя и пароль, то пользователь должен быть аутентифицирован, и на главный экран должен быть отображен администратору. Но в моем случае, даже когда предоставлены правильные имя пользователя и пароль, контроль перенаправляется на страницу входа, а не на главную страницу.

Ниже приведены дальнейшие детали:

страница входа:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Форма входа и регистрации | CoderGirl</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <input type="checkbox" id="check">
    <div class="login form">
      <header>Вход</header>
      <form action="/home" method="POST">
        <input type="text" name="username" placeholder="Введите ваш email" required>
        <input type="password" name="password" placeholder="Введите ваш пароль" required>
        <a href="#">Забыли пароль?</a>
        <input type="submit" class="button" value="Войти">
      </form>
      <div class="signup">
        <span class="signup">Нет аккаунта?
         <label for="check">Зарегистрироваться</label>
        </span>
      </div>
    </div>
    <div class="registration form">
      <header>Регистрация</header>
      <form action="/register" method="POST">
        <input type="text" name="username" placeholder="Введите ваш email" required>
        <input type="password" name="password" placeholder="Создайте пароль" required>
        <input type="password" name="confirmPassword" placeholder="Подтвердите ваш пароль" required>
        <input type="submit" class="button" value="Зарегистрироваться">
      </form>
      <div class="signup">
        <span class="signup">Уже есть аккаунт?
         <label for="check">Войти</label>
        </span>
      </div>
    </div>
  </div>
</body>
</html>

главная страница:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Управление продуктами</title>
  <link rel="stylesheet" href="home_style.css">
</head>
<body>
  <div class="container">
    <h1>Добро пожаловать в систему управления продуктами</h1>
    <div class="button-container">
      <button class="action-button" onclick="window.location.href='https://stackoverflow.com/add-product'">Добавить продукт</button>
      <button class="action-button" onclick="window.location.href='http://stackoverflow.com/remove-product'">Удалить продукт</button>
      <button class="action-button" onclick="window.location.href='http://stackoverflow.com/modify-product'">Изменить продукт</button>
      <button class="action-button" onclick="window.location.href='http://stackoverflow.com/view-product'">Просмотреть продукт</button>
    </div>
    <a href="/logout" class="logout-button">Выйти</a>
  </div>
</body>
</html>

контроллер:

@Controller
public class LoginController {

    @Autowired
    private UserService userService;

    @Autowired
    private CustomUserDetailsService customservice;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @GetMapping("/login")
    public String loginPage() {
        return "index"; // Отобразить страницу login.html
    }

    @GetMapping("/logout")
    public String logoutPage() {
        return "index"; // Отобразить страницу logout.html
    }

    @PostMapping("/home")
    public String login(@RequestParam String username, @RequestParam String password) {
        // Получить пользователя из базы данных
        System.out.println("точка входа входа");
        User user = userService.findByUsername(username);

        Iterator<Role> iterator = user.getRoles().iterator();
        while (iterator.hasNext()) {
            Role fruit = iterator.next();
            System.out.println(fruit.getRoleName());
        }

        if (user != null && passwordEncoder.matches(password, user.getPassword())) {
            System.out.println("аутентификация завершена");
            return "home";  // Пароль совпадает, перенаправить на главную
        } else {
            System.out.println("аутентификация не завершена");
            return "index";  // Неверный вход, вернуться на страницу входа
        }
    }
}

Это моя конфигурация безопасности:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private CustomUserDetailsService customUserDetailsService; // Внедрение службы пользовательских деталей

    @Bean
    public PasswordEncoder passwordEncoder() {
        System.out.println("внутри кодировщика паролей");
        return new BCryptPasswordEncoder(); // Пароли должны быть захешированы с использованием BCrypt
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        System.out.println("внутри поставщика аутентификации");
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(customUserDetailsService); // Использовать внедрённую службу пользовательских деталей
        authProvider.setPasswordEncoder(passwordEncoder()); // Установить кодировщик паролей
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        System.out.println("внутри менеджера аутентификации");
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        System.out.println("внутри securityFilterChain");
        http
            .authorizeHttpRequests()
            .requestMatchers("/css/**", "/js/**", "/images/**", "/static/**").permitAll()
            .requestMatchers("/login", "/register", "*.css").permitAll() // Публичный доступ к входу и регистрации
            .requestMatchers("/add-product", "/modify-product", "/remove-product").hasRole("ADMIN") // Ограничить модификацию продукта для ролей ADMIN
            .requestMatchers("/view-product").authenticated() // Требуется, чтобы пользователи были аутентифицированы
            .anyRequest().authenticated() // Все другие запросы должны быть аутентифицированы
            .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home", true) // Перенаправить на главную после входа
                .failureUrl("/login") 
            .and()
            .logout()
                .logoutSuccessUrl("/logout") // Перенаправить после выхода
                .permitAll()
            .and()
            .sessionManagement() // Настроить управление сессиями
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // Политика по умолчанию
                .maximumSessions(1) // Ограничить количество одновременных сессий на пользователя
                .expiredUrl("/login?expired") // Перенаправить на страницу входа, если сессия истекла
            .and()
            .csrf().disable(); // Отключить CSRF для клиентов, не использующих браузер или доступ к API

        return http.build();
    }
}

служба пользовательских деталей:

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    public CustomUserDetailsService() {
        System.out.println("Служба пользовательских деталей инициализирована.");
    }

    // Соответствие ролей полномочиям, добавляя префикс 'ROLE_'
    private Collection<? extends GrantedAuthority> mapRolesToAuthorities(Collection<Role> roles) {
        return roles.stream()
                    .map(role -> new SimpleGrantedAuthority(role.getRoleName()))
                    .collect(Collectors.toList());
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("внутри loaduserbyusername пользовательский ");
        User user = userRepository.findByUsername(username);
        System.out.println("Пользователь - "+user.getUsername() +" и пароль - "+user.getPassword());
        if (user == null) {
            throw new UsernameNotFoundException("Пользователь не найден");
        }
        org.springframework.security.core.userdetails.User springUser = new org.springframework.security.core.userdetails.User(
                user.getUsername(),
                user.getPassword(),
                mapRolesToAuthorities(user.getRoles())
        );
        System.out.println("Детали пользователя Spring Security: ");
        System.out.println("Имя пользователя: " + springUser.getUsername());
        System.out.println("Пароль: " + springUser.getPassword());
        System.out.println("Полномочия: " + springUser.getAuthorities());
        return springUser;

    }
}

логи:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.0)

2024-10-26T20:07:44.105+05:30  INFO 19060 --- [  restartedMain] com.example.webapp.Dia1Application       : Запуск Dia1Application с использованием Java 17.0.3 с PID 19060 (C:\Users\Admin\eclipse-workspace\dia-1\target\classes, запущенный Admin в рабочем пространстве\dia-1)
2024-10-26T20:07:44.162+05:30  INFO 19060 --- [  restartedMain] com.example.webapp.Dia1Application       : Нет активного профиля, возвращение к 1 профилю по умолчанию: "default"
2024-10-26T20:07:44.458+05:30  INFO 19060 --- [  restartedMain] o.s.b.devtools.restart.ChangeableUrls    : Атрибут манифеста Class-Path в C:\Users\Admin\.m2\repository\com\oracle\database\jdbc\ojdbc8\19.8.0.0\ojdbc8-19.8.0.0.jar ссылается на один или несколько файлов, которые не существуют: file:/C:/Userscom/oracle/database/jdbc/ojdbc8/19.8.0.0/oraclepki.jar
2024-10-26T20:07:44.459+05:30  INFO 19060 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Установлены активные значения по умолчанию для Devtools! Установите 'spring.devtools.add-properties' на 'false', чтобы отключить
2024-10-26T20:07:44.460+05:30  INFO 19060 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Для дополнительного веб-логирования рассмотрите возможность настройки свойства 'logging.level.web' на 'DEBUG'
2024-10-26T20:07:47.352+05:30  INFO 19060 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Инициализация репозиториев Spring Data JPA в РЕЖИМЕ ПО УМОЛЧАНИЮ.
2024-10-26T20:07:47.948+05:30  INFO 19060 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Завершено сканирование репозиториев Spring Data за 367 мс. Найдено 3 интерфейса репозитория JPA.
2024-10-26T20:07:51.595+05:30  INFO 19060 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat инициализирован с портами: 8080 (http)
2024-10-26T20:07:51.642+05:30  INFO 19060 --- [  restartedMain] o.apache.catalina.core.StandardService   : Запуск службы [Tomcat]
2024-10-26T20:07:51.643+05:30  INFO 19060 --- [  restartedMain] o.apache.catalina.core.StandardEngine    : Запуск сервлетного движка: [Apache Tomcat/10.1.8]
2024-10-26T20:07:51.957+05:30  INFO 19060 --- [  restartedMain] o.a.c.c.C.[Tomcat].[localhost].[/]       : Инициализация встроенного WebApplicationContext Spring
2024-10-26T20:07:51.959+05:30  INFO 19060 --- [  restartedMain] w.s.c.ServletWebServerApplicationContext : Корневой WebApplicationContext: инициализация завершена за 7497 мс
2024-10-26T20:07:52.995+05:30  INFO 19060 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Обработка PersistenceUnitInfo [имя: по умолчанию]
2024-10-26T20:07:53.414+05:30  INFO 19060 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Основная версия Hibernate ORM 6.2.2.Final
2024-10-26T20:07:53.428+05:30  INFO 19060 --- [  restartedMain] org.hibernate.cfg.Environment            : HHH000406: Использование оптимизатора рефлексии байт-кода
2024-10-26T20:07:54.294+05:30  INFO 19060 --- [  restartedMain] o.h.b.i.BytecodeProviderInitiator        : HHH000021: Имя поставщика байт-кода : bytebuddy
2024-10-26T20:07:55.194+05:30  INFO 19060 --- [  restartedMain] o.s.o.j.p.SpringPersistenceUnitInfo      : Установка LoadTimeWeaver не настроена: игнорировать трансформатор классов JPA
2024-10-26T20:07:55.295+05:30  INFO 19060 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Запуск...
2024-10-26T20:07:56.818+05:30  INFO 19060 --- [  restartedMain] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Добавлено соединение oracle.jdbc.driver.T4CConnection@567f88b3
2024-10-26T20:07:56.826+05:30  INFO 19060 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Запуск завершён.
2024-10-26T20:07:58.454+05:30  INFO 19060 --- [  restartedMain] org.hibernate.orm.dialect                : HHH035001: Использование диалекта: org.hibernate.dialect.OracleDialect, версия: 21.0
2024-10-26T20:07:59.957+05:30  INFO 19060 --- [  restartedMain] o.h.b.i.BytecodeProviderInitiator        : HHH000021: Имя поставщика байт-кода : bytebuddy
2024-10-26T20:08:04.301+05:30  INFO 19060 --- [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Использование реализации JtaPlatform: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2024-10-26T20:08:04.329+05:30  INFO 19060 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Инициализирован JPA EntityManagerFactory для единицы постоянства 'по умолчанию'
Служба пользовательских деталей инициализирована.
внутри кодировщика паролей
2024-10-26T20:08:06.758+05:30  WARN 19060 --- [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view включен по умолчанию. Поэтому, возможно, будут выполняться запросы к базе данных во время рендеринга представления. Явно настройте spring.jpa.open-in-view, чтобы отключить это предупреждение
внутри поставщика аутентификации
внутри securityFilterChain
2024-10-26T20:08:07.257+05:30  INFO 19060 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Добавление шаблона приветственной страницы: index
2024-10-26T20:08:08.083+05:30  INFO 19060 --- [  restartedMain] o.s.s.web.DefaultSecurityFilterChain     : Будет защищён любой запрос с [org.springframework.security.web.session.DisableEncodeUrlFilter@7beeb809, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1250ad26, org.springframework.security.web.context.SecurityContextHolderFilter@f1c53e8, org.springframework.security.web.header.HeaderWriterFilter@5e21c003, org.springframework.security.web.authentication.logout.LogoutFilter@6af304cf, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@47bc56f1, org.springframework.security.web.session.ConcurrentSessionFilter@576c970f, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2ad56bcf, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5378e4fd, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@377e7a43, org.springframework.security.web.session.SessionManagementFilter@1454bc00, org.springframework.security.web.access.ExceptionTranslationFilter@7b40484, org.springframework.security.web.access.intercept.AuthorizationFilter@107f250f]
внутри менеджера аутентификации
2024-10-26T20:08:09.118+05:30  INFO 19060 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : Сервер LiveReload работает на порту 35729
2024-10-26T20:08:09.257+05:30  INFO 19060 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat запущен на портах: 8080 (http) с контекстным путем ''
2024-10-26T20:08:09.300+05:30  INFO 19060 --- [  restartedMain] com.example.webapp.Dia1Application       : Запуск Dia1Application завершён за 27.13 секунд (процесс работает 30.667)
Hibernate: select r1_0.role_id,r1_0.role_name from roles r1_0 where r1_0.role_name=?
Hibernate: select u1_0.user_id,u1_0.password,u1_0.user_name from users u1_0 where u1_0.user_name=?
Hibernate: select r1_0.user_id,r1_1.role_id,r1_1.role_name from user_roles r1_0 join roles r1_1 on r1_1.role_id=r1_0.role_id where r1_0.user_id=?
2024-10-26T20:08:29.660+05:30  INFO 19060 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Инициализация Spring DispatcherServlet 'dispatcherServlet'
2024-10-26T20:08:29.661+05:30  INFO 19060 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Инициализация сервлета 'dispatcherServlet'
2024-10-26T20:08:29.671+05:30  INFO 19060 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Завершение инициализации за 7 мс
2024-10-26T20:08:29.708+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Защита POST /home
2024-10-26T20:08:29.747+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Установлен SecurityContextHolder в анонимный SecurityContext
2024-10-26T20:08:29.887+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.s.w.s.HttpSessionRequestCache        : Запрос http://localhost:8080/home сохранен в сессии
2024-10-26T20:08:29.892+05:30 DEBUG 19060 --- [nio-8080-exec-1] o.s.s.web.DefaultRedirectStrategy        : Перенаправление на http://localhost:8080/login
2024-10-26T20:08:29.974+05:30 DEBUG 19060 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Защита GET /login
2024-10-26T20:08:29.975+05:30 DEBUG 19060 --- [nio-8080-exec-2] o.s.s.w.a.AnonymousAuthenticationFilter  : Установлен SecurityContextHolder в анонимный SecurityContext
2024-10-26T20:08:29.981+05:30 DEBUG 19060 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy        : Защищён GET /login
2024-10-26T20:08:31.829+05:30 DEBUG 19060 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Защита GET /style.css
2024-10-26T20:08:31.829+05:30 DEBUG 19060 --- [nio-8080-exec-3] o.s.s.w.a.AnonymousAuthenticationFilter  : Установлен SecurityContextHolder в анонимный SecurityContext
2024-10-26T20:08:31.866+05:30 DEBUG 19060 --- [nio-8080-exec-3] o.s.security.web.FilterChainProxy        : Защищён GET /style.css

данные таблицы ролей:

ROLE_ID   ROLE_NAME
1         ROLE_ADMIN

Таким образом, я добавил логи внутри метода loaduserbyusername, но эти логи не генерируются во время запуска приложения или при успешном входе. Кто-нибудь может помочь понять, что идет не так.

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

Ваша проблема заключается в том, что при правильном введении имени пользователя и пароля происходит редирект на страницу входа вместо перехода на домашнюю страницу. Давайте взглянем на возможные причины этой ситуации и способы их устранения.

1. Ошибка в конфигурации безопасности

В вашем классе SecurityConfig указана конфигурация, которая определяет обработку входа:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests()
        .requestMatchers("/css/**", "/js/**", "/images/**", "/static/**").permitAll()
        .requestMatchers("/login", "/register", "*.css").permitAll()
        .requestMatchers("/add-product", "/modify-product", "/remove-product").hasRole("ADMIN")
        .requestMatchers("/view-product").authenticated()
        .anyRequest().authenticated()
        .and()
        .formLogin()
            .loginPage("/login")
            .defaultSuccessUrl("/home", true)
            .failureUrl("/login")
        .and()
        .logout()
            .logoutSuccessUrl("/logout")
            .permitAll()
        .and()
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
            .maximumSessions(1)
            .expiredUrl("/login?expired")
        .and()
        .csrf().disable();

    return http.build();
}

Обратите внимание на секцию formLogin(). Данная конфигурация предполагает, что при успешной аутентификации пользователи перенаправляются на "/home". Однако, если аутентификация не проходит (например, если вызов loadUserByUsername не выполняется), будет установлен редирект на страницу входа.

2. Проверка метода loadUserByUsername

Проверьте, вызывается ли метод loadUserByUsername в вашем CustomUserDetailsService. В вашем логе указано, что он не вызывает ожидаемые сообщения о том, что метод был выполнен. Это может означать, что Spring Security не использует вашу реализацию UserDetailsService.

Убедитесь, что в настройках безопасности вы указали свой CustomUserDetailsService:

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

Кроме того, убедитесь, что ваше приложение использует аутентификацию с formLogin() и что класс LoginController не переопределяет и не мешает общей логике аутентификации Spring Security.

3. Проблемы с данными пользователя

Проверьте, что пользователи и роли в вашей базе данных настроены корректно. Убедитесь, что:

  • В таблице users есть запись с корректным именем пользователя.
  • Пароль пользователя соответствует хэшу, который вы ожидаете (хранится в свойстве password).
  • В таблице roles для данного пользователя назначена правильная роль, и роли правильно сопоставляются в методе mapRolesToAuthorities.

4. Логи и отладка

Проверяйте системные логи во время выполнения вашего приложения. Убедитесь, что аутентификация происходит и не приводит к исключениям. Добавьте дополнительное логирование для отладки:

@PostMapping("/home")
public String login(@RequestParam String username, @RequestParam String password) {
    System.out.println("login endpoint");
    User user = userService.findByUsername(username);

    if (user != null) {
        System.out.println("User found: " + user.getUsername());
    } else {
        System.out.println("User not found.");
    }

    if (passwordEncoder.matches(password, user.getPassword())) {
        System.out.println("Password matches.");
        return "home";  // Password matches, redirect to home
    } else {
        System.out.println("Password does not match.");
    }

    return "index"; // Invalid login, return to login page
}

5. Перенос метода аутентификации в контроллер

Рекомендую перенести логику аутентификации из метода login в контроллер в Spring Security, чтобы использовать встроенные механизмы аутентификации:

@PostMapping("/login")
public String login(@RequestParam String username, @RequestParam String password, Model model) {
    // Используйте AuthenticationManager для аутентификации
}

6. Отладка конфигурации Spring Security

Попробуйте использовать уровень отладки (DEBUG) для логов Spring Security для более глубокого понимания происходящих событий в процессе аутентификации:

logging.level.org.springframework.security=DEBUG

Это даст вам больше информации о том, какие фильтры и аутентификаторы запускаются, и возможно, вы заметите что-то необычное.

Заключение

Если ни одно из предложенных выше решений не устраняет проблему, рекомендуем использовать отладчик для поэтапного прохождения через код аутентификации и настройки. Учтите, что интеграция Spring Security может быть сложной, и каждое изменение может повлиять на другое. Убедитесь, что все части вашей системы поддержки аутентификации согласованы и правильно связаны.

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

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