mirror of
https://github.com/LOBSTERVOVA/Tennis-Site.git
synced 2026-04-17 17:40:49 +03:00
Merge branch 'Morgan' of https://github.com/LOBSTERVOVA/DatePlanner into balashev_vk
# Conflicts: # src/main/java/com/example/dateplanner/configurations/SecurityConfig.java # src/main/resources/static/js/site/blocks/header.js
This commit is contained in:
5
pom.xml
5
pom.xml
@@ -138,6 +138,11 @@
|
|||||||
<artifactId>commons-io</artifactId>
|
<artifactId>commons-io</artifactId>
|
||||||
<version>2.11.0</version>
|
<version>2.11.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||||
|
<artifactId>jackson-datatype-jsr310</artifactId>
|
||||||
|
<version>2.20.1</version> <!-- или актуальная версия -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
@@ -12,16 +12,10 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
|
||||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
|
||||||
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
|
||||||
import org.springframework.security.config.Customizer;
|
|
||||||
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
|
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
|
||||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||||
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
|
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
|
||||||
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
import org.springframework.security.config.web.server.ServerHttpSecurity;
|
||||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsPasswordService;
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||||
@@ -30,8 +24,7 @@ import org.springframework.security.web.server.csrf.CookieServerCsrfTokenReposit
|
|||||||
import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler;
|
import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestAttributeHandler;
|
||||||
import org.springframework.security.web.server.savedrequest.ServerRequestCache;
|
import org.springframework.security.web.server.savedrequest.ServerRequestCache;
|
||||||
import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;
|
import org.springframework.security.web.server.savedrequest.WebSessionServerRequestCache;
|
||||||
import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
|
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -40,31 +33,10 @@ import java.net.URI;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
@EnableReactiveMethodSecurity
|
@EnableReactiveMethodSecurity
|
||||||
public class SecurityConfig {
|
public class SecurityConfig {
|
||||||
|
|
||||||
private final AppUserRepository userRepository;
|
private final AppUserRepository userRepository;
|
||||||
private final JwtService jwtService;
|
private final JwtService jwtService;
|
||||||
|
|
||||||
// Цепочка для API (Basic Auth) - CSRF отключаем
|
|
||||||
@Bean
|
@Bean
|
||||||
@Order(1)
|
public SecurityWebFilterChain filterChain(ServerHttpSecurity http){
|
||||||
public SecurityWebFilterChain apiFilterChain(ServerHttpSecurity http) {
|
|
||||||
return http
|
|
||||||
.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**"))
|
|
||||||
.authorizeExchange(exchange -> exchange
|
|
||||||
.pathMatchers("/api/mobile/login").permitAll()
|
|
||||||
.anyExchange().authenticated()
|
|
||||||
)
|
|
||||||
.httpBasic(Customizer.withDefaults())
|
|
||||||
.formLogin(ServerHttpSecurity.FormLoginSpec::disable)
|
|
||||||
.csrf(ServerHttpSecurity.CsrfSpec::disable) // CSRF отключаем для API
|
|
||||||
.authenticationManager(reactiveAuthenticationManager())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Цепочка для веб-интерфейса (Form Login) - CSRF включаем
|
|
||||||
@Bean
|
|
||||||
@Order(2)
|
|
||||||
public SecurityWebFilterChain webFilterChain(ServerHttpSecurity http) {
|
|
||||||
ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
|
ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
|
||||||
requestHandler.setTokenFromMultipartDataEnabled(true);
|
requestHandler.setTokenFromMultipartDataEnabled(true);
|
||||||
|
|
||||||
@@ -74,13 +46,13 @@ public class SecurityConfig {
|
|||||||
ErrorHandlingFilter errorHandlingFilter = new ErrorHandlingFilter();
|
ErrorHandlingFilter errorHandlingFilter = new ErrorHandlingFilter();
|
||||||
|
|
||||||
return http
|
return http
|
||||||
.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/account/**"))
|
|
||||||
.csrf(csrf -> csrf.csrfTokenRequestHandler(requestHandler).csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
|
.csrf(csrf -> csrf.csrfTokenRequestHandler(requestHandler).csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
|
||||||
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
|
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
|
||||||
.addFilterAfter(errorHandlingFilter, SecurityWebFiltersOrder.AUTHENTICATION)
|
//.addFilterAfter(errorHandlingFilter, SecurityWebFiltersOrder.AUTHENTICATION)
|
||||||
.authorizeExchange(exchange -> exchange
|
.authorizeExchange(exchange -> exchange
|
||||||
.pathMatchers("/account/login").permitAll()
|
.pathMatchers("/account/login","/error","/error/**").permitAll()
|
||||||
.anyExchange().authenticated()
|
.pathMatchers("/account/**").authenticated()
|
||||||
|
.anyExchange().permitAll()
|
||||||
)
|
)
|
||||||
.formLogin(loginSpec -> loginSpec.loginPage("/account/login").authenticationSuccessHandler(authenticationSuccessHandler()))
|
.formLogin(loginSpec -> loginSpec.loginPage("/account/login").authenticationSuccessHandler(authenticationSuccessHandler()))
|
||||||
.logout(logoutSpec -> logoutSpec.logoutSuccessHandler(logoutSuccessHandler()))
|
.logout(logoutSpec -> logoutSpec.logoutSuccessHandler(logoutSuccessHandler()))
|
||||||
@@ -88,19 +60,6 @@ public class SecurityConfig {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Цепочка для всех остальных путей (публичные)
|
|
||||||
@Bean
|
|
||||||
@Order(3)
|
|
||||||
public SecurityWebFilterChain defaultFilterChain(ServerHttpSecurity http) {
|
|
||||||
return http
|
|
||||||
.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/**"))
|
|
||||||
.authorizeExchange(exchange -> exchange
|
|
||||||
.anyExchange().permitAll()
|
|
||||||
)
|
|
||||||
// .csrf(ServerHttpSecurity.CsrfSpec::disable)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ServerRequestCache serverRequestCache() {
|
public ServerRequestCache serverRequestCache() {
|
||||||
return new WebSessionServerRequestCache();
|
return new WebSessionServerRequestCache();
|
||||||
@@ -129,33 +88,12 @@ public class SecurityConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
|
public PasswordEncoder passwordEncoder(){
|
||||||
UserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(userService());
|
return new BCryptPasswordEncoder();
|
||||||
manager.setPasswordEncoder(passwordEncoder());
|
|
||||||
manager.setUserDetailsPasswordService(userDetailsPasswordService());
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public ReactiveUserDetailsPasswordService userDetailsPasswordService() {
|
|
||||||
return new ReactiveUserDetailsPasswordService() {
|
|
||||||
@Override
|
|
||||||
public Mono<UserDetails> updatePassword(UserDetails user, String newPassword) {
|
|
||||||
return userRepository.findByPhone(user.getUsername()).flatMap(appUser -> {
|
|
||||||
appUser.setPassword(passwordEncoder().encode(newPassword));
|
|
||||||
return userRepository.save(appUser);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public UserService userService(){
|
public UserService userService(){
|
||||||
return new UserService(userRepository,passwordEncoder());
|
return new UserService(userRepository,passwordEncoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public PasswordEncoder passwordEncoder(){
|
|
||||||
return new BCryptPasswordEncoder();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ public class JwtAuthenticationManager implements ReactiveAuthenticationManager {
|
|||||||
//log.info("start auth manager");
|
//log.info("start auth manager");
|
||||||
String accessToken = authentication.getPrincipal().toString();
|
String accessToken = authentication.getPrincipal().toString();
|
||||||
String refreshToken = authentication.getCredentials().toString();
|
String refreshToken = authentication.getCredentials().toString();
|
||||||
log.info("refresh is {},{}", accessToken, refreshToken);
|
|
||||||
|
|
||||||
if(jwt.validateToken(accessToken)){
|
if(jwt.validateToken(accessToken)){
|
||||||
//log.info("access token is valid");
|
//log.info("access token is valid");
|
||||||
@@ -105,7 +104,7 @@ public class JwtAuthenticationManager implements ReactiveAuthenticationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Mono<Authentication> baseAuth(Authentication authentication){
|
private Mono<Authentication> baseAuth(Authentication authentication){
|
||||||
log.info("access and refresh token is not valid - try authenticate by basic login");
|
//log.info("access and refresh token is not valid - try authenticate by basic login");
|
||||||
String username = authentication.getPrincipal().toString();
|
String username = authentication.getPrincipal().toString();
|
||||||
String password = authentication.getCredentials().toString();
|
String password = authentication.getCredentials().toString();
|
||||||
return userService.findByUsername(username).flatMap(user -> {
|
return userService.findByUsername(username).flatMap(user -> {
|
||||||
|
|||||||
@@ -53,4 +53,4 @@ public class JwtAuthenticationSuccessHandler implements ServerAuthenticationSucc
|
|||||||
return webFilterExchange.getExchange().getResponse().setComplete();
|
return webFilterExchange.getExchange().getResponse().setComplete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ public class AppUser implements UserDetails {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return this.phone;
|
return getPhone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ import java.util.UUID;
|
|||||||
public interface AppUserRepository extends R2dbcRepository<AppUser, UUID> {
|
public interface AppUserRepository extends R2dbcRepository<AppUser, UUID> {
|
||||||
|
|
||||||
Mono<AppUser> findByPhone(String phone);
|
Mono<AppUser> findByPhone(String phone);
|
||||||
|
Mono<AppUser> findByUsername(String username);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,20 +2,22 @@ package com.example.dateplanner.services;
|
|||||||
|
|
||||||
import com.example.dateplanner.repositories.AppUserRepository;
|
import com.example.dateplanner.repositories.AppUserRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
@Service
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserService implements ReactiveUserDetailsService {
|
public class UserService implements ReactiveUserDetailsService {
|
||||||
private final AppUserRepository appUserRepository;
|
private final AppUserRepository userRepository;
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<UserDetails> findByUsername(String username) {
|
public Mono<UserDetails> findByUsername(String username) {
|
||||||
return appUserRepository.findByPhone(username).flatMap(Mono::just).cast(UserDetails.class);
|
log.info("username {}", username);
|
||||||
|
return userRepository.findByPhone(username).flatMap(Mono::just).cast(UserDetails.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class CookieUtil {
|
|||||||
private final String SESSION;
|
private final String SESSION;
|
||||||
|
|
||||||
protected CookieUtil(){
|
protected CookieUtil(){
|
||||||
String appName = "pstweb";
|
String appName = "dp";
|
||||||
REFRESH = appName + "-refresh";
|
REFRESH = appName + "-refresh";
|
||||||
ACCESS = appName + "-access";
|
ACCESS = appName + "-access";
|
||||||
SESSION = appName + "-session";
|
SESSION = appName + "-session";
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ spring.flyway.locations=db/migration
|
|||||||
spring.flyway.validate-migration-naming=true
|
spring.flyway.validate-migration-naming=true
|
||||||
spring.flyway.baseline-on-migrate=true
|
spring.flyway.baseline-on-migrate=true
|
||||||
#====================== minio configuration =====================
|
#====================== minio configuration =====================
|
||||||
minio.bucket=
|
minio.bucket=${spring.application.name}
|
||||||
minio.url=
|
minio.url=
|
||||||
minio.cdn=
|
minio.cdn=
|
||||||
minio.username=
|
minio.username=
|
||||||
|
|||||||
@@ -103,14 +103,14 @@ function initHeader($header){
|
|||||||
<!-- Форма входа -->
|
<!-- Форма входа -->
|
||||||
<div class="tab-pane fade show active" id="login" role="tabpanel">
|
<div class="tab-pane fade show active" id="login" role="tabpanel">
|
||||||
<form id="loginForm" method="POST" action="/account/login">
|
<form id="loginForm" method="POST" action="/account/login">
|
||||||
<input type="hidden" name="${csrf.parameterName}" value="${csrf.token}" />
|
<input type="hidden" name="_csrf" value="${csrf.token}"/>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Email или телефон</label>
|
<label class="form-label">Email или телефон</label>
|
||||||
<input type="text" class="form-control" id="loginEmail" placeholder="example@mail.ru" required>
|
<input type="text" class="form-control" id="loginEmail" placeholder="example@mail.ru" name="username" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Пароль</label>
|
<label class="form-label">Пароль</label>
|
||||||
<input type="password" class="form-control" id="loginPassword" required>
|
<input type="password" class="form-control" id="loginPassword" name="password" required>
|
||||||
<div class="text-end mt-2">
|
<div class="text-end mt-2">
|
||||||
<a href="#" class="forgot-password" onclick="showForgotPassword()">Забыли пароль?</a>
|
<a href="#" class="forgot-password" onclick="showForgotPassword()">Забыли пароль?</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user