3 Commits
main ... lab8

Author SHA1 Message Date
beab30cb44 lab8 2024-12-10 04:49:44 +03:00
9339a1b7b1 lab6-7 2024-12-09 13:39:53 +03:00
baeeed40d1 lab5 2024-11-14 13:35:46 +03:00
23 changed files with 567 additions and 48 deletions

View File

@@ -28,12 +28,23 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-rest'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.security:spring-security-core'
implementation 'org.springframework.security:spring-security-web'
implementation 'org.springframework.security:spring-security-config'
implementation 'org.springframework.security:spring-security-crypto'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-authorization-server'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
developmentOnly 'org.springframework.boot:spring-boot-devtools' developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.hibernate.orm:hibernate-community-dialects' implementation 'org.hibernate.orm:hibernate-community-dialects'
implementation 'org.xerial:sqlite-jdbc:3.46.1.3' implementation 'org.xerial:sqlite-jdbc:3.46.1.3'
implementation 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok' compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok'

View File

@@ -0,0 +1,14 @@
package ru.lionarius.isdojplab.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class ApplicationConfig {
@Bean
public WebClient webClient() {
return WebClient.create();
}
}

View File

@@ -0,0 +1,119 @@
package ru.lionarius.isdojplab.config;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.web.SecurityFilterChain;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.UUID;
@Configuration
@EnableWebSecurity
public class AuthorizationServerConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.csrf(AbstractHttpConfigurer::disable)
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
var secret = passwordEncoder.encode("secret");
System.out.println("secret: " + secret);
RegisteredClient loginClient = RegisteredClient
.withId(UUID.randomUUID().toString())
.clientId("login-client")
.clientSecret(secret)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://localhost:8080/login/oauth2/code/login-client")
.redirectUri("http://localhost:8080/")
.scope("read")
.scope("write")
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(true)
.build()
)
.tokenSettings(TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofDays(1))
.refreshTokenTimeToLive(Duration.ofDays(1))
.build()
)
.build();
return new InMemoryRegisteredClientRepository(loginClient);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("http://localhost:8080")
.build();
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelect, securityContext) -> jwkSelect.select(jwkSet);
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
private static RSAKey generateRsa() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
}

View File

@@ -0,0 +1,78 @@
package ru.lionarius.isdojplab.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers("/").permitAll()
.requestMatchers("/login/**").permitAll()
.requestMatchers("/oauth2/**").permitAll()
.requestMatchers("/register").permitAll()
.requestMatchers("/logout").permitAll()
.requestMatchers("/css/**").permitAll()
.requestMatchers("/js/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/**").hasAuthority("scope_read")
.requestMatchers("/api/**").hasAuthority("scope_write")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> {
oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()));
});
http.exceptionHandling(exception -> exception.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")));
http.logout(logout -> logout.logoutSuccessUrl("/").permitAll());
http
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
.securityContext(securityContext -> securityContext
.securityContextRepository(new HttpSessionSecurityContextRepository())
);
return http.build();
}
private JwtAuthenticationConverter jwtAuthenticationConverter() {
var converter = new JwtAuthenticationConverter();
var authoritiesConverter = new JwtGrantedAuthoritiesConverter();
authoritiesConverter.setAuthoritiesClaimName("scope");
authoritiesConverter.setAuthorityPrefix("scope_");
converter.setJwtGrantedAuthoritiesConverter(authoritiesConverter);
return converter;
}
@Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
var provider = new DaoAuthenticationProvider(passwordEncoder);
provider.setUserDetailsService(userDetailsService);
return new ProviderManager(provider);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@@ -0,0 +1,13 @@
package ru.lionarius.isdojplab.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "store")
public class StoreProperties {
private String name = "Магазин электротоваров";
private String description = "Все для вашего дома и офиса";
}

View File

@@ -12,6 +12,9 @@ public class CartController {
@GetMapping @GetMapping
public String showCart(Model model) { public String showCart(Model model) {
var cart = new Cart();
model.addAttribute("cart", cart);
return "cart"; return "cart";
} }
} }

View File

@@ -4,22 +4,22 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import ru.lionarius.isdojplab.model.Product;
import ru.lionarius.isdojplab.repository.ProductRepository; import ru.lionarius.isdojplab.repository.ProductRepository;
import java.util.List;
@Controller @Controller
public class HomeController { public class HomeController {
@Autowired private final ProductRepository productRepository;
private ProductRepository productRepository;
public HomeController(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@GetMapping("/") @GetMapping("/")
public String home(Model model) { public String home(Model model) {
model.addAttribute("message", "Добро пожаловать в магазин электротоваров!"); model.addAttribute("message", "Добро пожаловать в магазин электротоваров!");
List<Product> products = productRepository.findAll(); var products = productRepository.findAll();
model.addAttribute("products", products); model.addAttribute("products", products);

View File

@@ -0,0 +1,74 @@
package ru.lionarius.isdojplab.controller;
import jakarta.servlet.http.HttpSession;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import ru.lionarius.isdojplab.model.User;
import ru.lionarius.isdojplab.repository.UserRepository;
@Controller
public class LoginFormController {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final AuthenticationManager authenticationManager;
public LoginFormController(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthenticationManager authenticationManager) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
this.authenticationManager = authenticationManager;
}
@GetMapping("/login")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "login_form";
}
@PostMapping("/login")
public String submitForm(@ModelAttribute("user") User user, BindingResult bindingResult, Model model, HttpSession httpSession) {
if (bindingResult.hasErrors()) {
return "login_form";
}
var userFromDb = userRepository.findByEmail(user.getEmail());
if (userFromDb.isEmpty()) {
bindingResult.rejectValue("email", "email.not.found", "Пользователь не найден");
return "login_form";
}
if (!passwordEncoder.matches(user.getPassword(), userFromDb.get().getPassword())) {
bindingResult.rejectValue("password", "password.incorrect", "Неверный пароль");
return "login_form";
}
model.addAttribute("user", userFromDb.get());
var authCredentials = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword());
var auth = authenticationManager.authenticate(authCredentials);
var context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
httpSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
return "redirect:/";
}
@GetMapping("/logout")
public String logout(HttpSession httpSession) {
SecurityContextHolder.clearContext();
httpSession.invalidate();
return "redirect:/";
}
}

View File

@@ -1,7 +1,7 @@
package ru.lionarius.isdojplab.controller; package ru.lionarius.isdojplab.controller;
import jakarta.validation.Valid; import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
@@ -14,8 +14,13 @@ import ru.lionarius.isdojplab.repository.UserRepository;
@Controller @Controller
public class RegisterFormController { public class RegisterFormController {
@Autowired private final UserRepository userRepository;
private UserRepository userRepository; private final PasswordEncoder passwordEncoder;
public RegisterFormController(UserRepository userRepository, PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@GetMapping("/register") @GetMapping("/register")
public String showForm(Model model) { public String showForm(Model model) {
@@ -40,10 +45,13 @@ public class RegisterFormController {
return "register_form"; return "register_form";
} }
var hashedPassword = passwordEncoder.encode(user.getPassword());
user.setPassword(hashedPassword);
userRepository.save(user); userRepository.save(user);
model.addAttribute("message", "Пользователь успешно зарегистрирован"); model.addAttribute("message", "Пользователь успешно зарегистрирован");
return "register_form"; return "redirect:/login";
} }
} }

View File

@@ -1,6 +1,9 @@
package ru.lionarius.isdojplab.model; package ru.lionarius.isdojplab.model;
import jakarta.persistence.*; import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;

View File

@@ -8,10 +8,16 @@ import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import lombok.Data; import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
@Data @Data
@Entity @Entity
public class User { public class User implements UserDetails {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; private Long id;
@@ -26,4 +32,14 @@ public class User {
@NotBlank(message = "Поле не может быть пустым") @NotBlank(message = "Поле не может быть пустым")
@Size(min = 6, message = "Пароль должен состоять не менее 6 символов") @Size(min = 6, message = "Пароль должен состоять не менее 6 символов")
private String password; private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of(new SimpleGrantedAuthority("SIMPLE_USER"));
}
@Override
public String getUsername() {
return email;
}
} }

View File

@@ -1,9 +1,9 @@
package ru.lionarius.isdojplab.repository; package ru.lionarius.isdojplab.repository;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import ru.lionarius.isdojplab.model.Product; import ru.lionarius.isdojplab.model.Product;
@Repository @Repository
public interface ProductRepository extends JpaRepository<Product, Long> { public interface ProductRepository extends CrudRepository<Product, Long> {
} }

View File

@@ -1,14 +1,15 @@
package ru.lionarius.isdojplab.repository; package ru.lionarius.isdojplab.repository;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import ru.lionarius.isdojplab.model.User; import ru.lionarius.isdojplab.model.User;
import java.util.Optional; import java.util.Optional;
@Repository @Repository
public interface UserRepository extends JpaRepository<User, Long> { public interface UserRepository extends CrudRepository<User, Long> {
Optional<User> findByEmail(String email); Optional<User> findByEmail(String email);
Optional<User> findByName(String name); Optional<User> findByName(String name);
} }

View File

@@ -0,0 +1,34 @@
package ru.lionarius.isdojplab.rest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import java.util.Base64;
@RestController
public class OAuth2Token {
private final WebClient webClient;
public OAuth2Token(WebClient webClient) {
this.webClient = webClient;
}
@GetMapping(value = "/login/oauth2/code/login-client", produces = "application/json")
public String handle(String code) {
var form = new LinkedMultiValueMap<String, String>();
form.add("grant_type", "authorization_code");
form.add("code", code);
form.add("redirect_uri", "http://localhost:8080/login/oauth2/code/login-client");
return webClient.post()
.uri("http://localhost:8080/oauth2/token")
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString("login-client:secret".getBytes()))
.bodyValue(form)
.retrieve()
.bodyToMono(String.class)
.block();
}
}

View File

@@ -0,0 +1,35 @@
package ru.lionarius.isdojplab.rest;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import ru.lionarius.isdojplab.model.User;
@Component
@RepositoryEventHandler
public class UserEventHandler {
private final PasswordEncoder passwordEncoder;
public UserEventHandler(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@HandleBeforeCreate
public void handleBeforeCreate(User user) {
encodePassword(user);
}
@HandleBeforeSave
public void handleBeforeSave(User user) {
encodePassword(user);
}
private void encodePassword(User user) {
if (user.getPassword() != null) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
}
}
}

View File

@@ -0,0 +1,25 @@
package ru.lionarius.isdojplab.runner;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import ru.lionarius.isdojplab.model.Product;
import ru.lionarius.isdojplab.repository.ProductRepository;
@Component
@Profile("dev")
public class DevDataInitializer implements CommandLineRunner {
private final ProductRepository productRepository;
public DevDataInitializer(ProductRepository productRepository) {
this.productRepository = productRepository;
}
@Override
public void run(String... args) throws Exception {
productRepository.save(new Product(null, "Тестовый товар 1", 1000.0, "Описание товара", null));
productRepository.save(new Product(null, "Тестовый товар 2", 2000.0, "Описание товара", null));
productRepository.save(new Product(null, "Тестовый товар 3", 3000.0, "Описание товара", null));
}
}

View File

@@ -0,0 +1,22 @@
package ru.lionarius.isdojplab.service;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import ru.lionarius.isdojplab.repository.UserRepository;
@Service
public class MyUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public MyUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(email));
}
}

View File

@@ -0,0 +1,6 @@
spring.datasource.driver-class-name=org.sqlite.JDBC
spring.datasource.url=jdbc:sqlite::memory:
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

View File

@@ -0,0 +1,6 @@
spring.datasource.driver-class-name=org.sqlite.JDBC
spring.datasource.url=jdbc:sqlite:./database/data.db
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect
spring.jpa.generate-ddl=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false

View File

@@ -1,7 +1,6 @@
spring.profiles.active=dev
spring.application.name=isdojp-lab spring.application.name=isdojp-lab
spring.datasource.driver-class-name=org.sqlite.JDBC spring.data.rest.base-path=/api
spring.datasource.url=jdbc:sqlite:./database/data.db
spring.jpa.database-platform=org.hibernate.community.dialect.SQLiteDialect store.name=asdfasfsdfs
spring.jpa.generate-ddl=true store.description=\u0412\u0441\u0435 \u0434\u043b\u044f \u0432\u0430\u0448\u0435\u0433\u043e \u0434\u043e\u043c\u0430 \u0438 \u043e\u0444\u0438\u0441\u0430
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.w3.org/1999/xhtml">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
@@ -9,14 +9,22 @@
<body> <body>
<header> <header>
<h1>Магазин электротоваров</h1> <h1 th:text="${@storeProperties.name}"></h1>
<p>Все для вашего дома и офиса</p> <p th:text="${@storeProperties.description}"></p>
</header> </header>
<div class="container"> <div class="container">
<div sec:authorize="!isAuthenticated()">
<div class="register-container"> <div class="register-container">
<a href="/register" class="register-button">Регистрация</a> <a href="/register" class="register-button">Регистрация</a>
</div> </div>
</div>
<div sec:authorize="isAuthenticated()">
<div class="register-container">
<a href="/logout" class="register-button">Выход</a>
</div>
</div>
<h2>Каталог товаров</h2> <h2>Каталог товаров</h2>

View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Авторизация</title>
<link rel="stylesheet" th:href="@{/css/styles.css}">
</head>
<body>
<header>
<h1>Авторизация пользователя</h1>
</header>
<div class="container">
<form th:action="@{/login}" th:object="${user}" method="post">
<div>
<label for="email">Email:</label>
<input type="email" id="email" th:field="*{email}" />
<br />
<span style="color: red" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></span>
</div>
<div>
<label for="password">Пароль:</label>
<input type="password" id="password" th:field="*{password}" />
<br />
<span style="color: red" th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></span>
</div>
<button type="submit">Авторизироваться</button>
</form>
<p th:if="${message}" th:text="${message}"></p>
</div>
<footer>
<p>© 2024 Магазин электротоваров. Все права защищены.</p>
</footer>
</body>
</html>

View File

@@ -39,8 +39,10 @@
</form> </form>
<p th:if="${message}" th:text="${message}"></p> <p th:if="${message}" th:text="${message}"></p>
<a href="/login">Уже зарегистрированы?</a>
</div> </div>
<footer> <footer>
<p>© 2024 Магазин электротоваров. Все права защищены.</p> <p>© 2024 Магазин электротоваров. Все права защищены.</p>
</footer> </footer>