Вопрос или проблема
Я реализовывал механизм аутентификации для своего приложения. Поэтому, если пользователь является администратором и предоставлено правильное имя пользователя и пароль, то пользователь должен быть аутентифицирован, и на главный экран должен быть отображен администратору. Но в моем случае, даже когда предоставлены правильные имя пользователя и пароль, контроль перенаправляется на страницу входа, а не на главную страницу.
Ниже приведены дальнейшие детали:
страница входа:
<!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 может быть сложной, и каждое изменение может повлиять на другое. Убедитесь, что все части вашей системы поддержки аутентификации согласованы и правильно связаны.