Newer
Older
teacher-diary / src / main / java / ru / mcs / diary / auth / SecurityConfig.java
package ru.mcs.diary.config;

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import ru.mcs.diary.auth.CustomUserDetailsService;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

    private final CustomUserDetailsService userDetailsService;
    private final AuthenticationSuccessHandler authSuccessHandler;

    @Value("${app.security.remember-me-key}")
    private String rememberMeKey;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // CSRF защита (включена по умолчанию)
                .csrf(csrf -> csrf
                        .ignoringRequestMatchers("/api/**") // Отключаем для API если понадобится
                )

                // Правила авторизации
                .authorizeHttpRequests(auth -> auth
                        // Публичные ресурсы
                        .requestMatchers(
                                "/",
                                "/css/**",
                                "/js/**",
                                "/img/**",
                                "/webjars/**",
                                "/favicon.ico"
                        ).permitAll()

                        // Аутентификация
                        .requestMatchers(
                                "/auth/login",
                                "/auth/register",
                                "/auth/forgot-password",
                                "/auth/reset-password/**"
                        ).permitAll()

                        // Регистрация родителя по приглашению
                        .requestMatchers("/auth/parent/invite/**").permitAll()

                        // Маршруты для преподавателей
                        .requestMatchers("/teacher/**").hasRole("TEACHER")
                        .requestMatchers("/students/**").hasRole("TEACHER")
                        .requestMatchers("/parents/**").hasRole("TEACHER")
                        .requestMatchers("/groups/**").hasRole("TEACHER")
                        .requestMatchers("/schedule/**").hasRole("TEACHER")
                        .requestMatchers("/subjects/**").hasRole("TEACHER")

                        // Маршруты для родителей
                        .requestMatchers("/parent/**").hasRole("PARENT")

                        // Всё остальное требует аутентификации
                        .anyRequest().authenticated()
                )

                // Форма входа
                .formLogin(form -> form
                        .loginPage("/auth/login")
                        .loginProcessingUrl("/auth/login")
                        .successHandler(authSuccessHandler)
                        .failureUrl("/auth/login?error=true")
                        .usernameParameter("email")
                        .passwordParameter("password")
                        .permitAll()
                )

                // Выход
                .logout(logout -> logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/auth/logout"))
                        .logoutSuccessUrl("/auth/login?logout=true")
                        .invalidateHttpSession(true)
                        .deleteCookies("JSESSIONID", "remember-me")
                        .permitAll()
                )

                // Запомнить меня
                .rememberMe(remember -> remember
                        .key(rememberMeKey)
                        .tokenValiditySeconds(604800) // 7 дней
                        .userDetailsService(userDetailsService)
                        .rememberMeParameter("remember-me")
                )

                // Обработка ошибок доступа
                .exceptionHandling(ex -> ex
                        .accessDeniedPage("/error/403")
                )

                // Провайдер аутентификации
                .authenticationProvider(authenticationProvider());

        return http.build();
    }

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }

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