Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
baeeed40d1
|
@@ -29,6 +29,11 @@ dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
|
||||
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'
|
||||
|
||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package ru.lionarius.isdojplab.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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.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.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.authorizeHttpRequests(auth -> auth
|
||||
.requestMatchers("/").permitAll()
|
||||
.requestMatchers("/login").permitAll()
|
||||
.requestMatchers("/register").permitAll()
|
||||
.requestMatchers("/logout").permitAll()
|
||||
.requestMatchers("/css/**").permitAll()
|
||||
.requestMatchers("/js/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
// http.formLogin(form -> form
|
||||
// .loginPage("/login")
|
||||
// .loginProcessingUrl("/login")
|
||||
// .usernameParameter("email")
|
||||
// .failureHandler(authenticationFailureHandler())
|
||||
// .permitAll());
|
||||
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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@ public class CartController {
|
||||
|
||||
@GetMapping
|
||||
public String showCart(Model model) {
|
||||
var cart = new Cart();
|
||||
model.addAttribute("cart", cart);
|
||||
|
||||
return "cart";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import ru.lionarius.isdojplab.model.Product;
|
||||
import ru.lionarius.isdojplab.repository.ProductRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class HomeController {
|
||||
|
||||
@@ -19,7 +16,7 @@ public class HomeController {
|
||||
public String home(Model model) {
|
||||
model.addAttribute("message", "Добро пожаловать в магазин электротоваров!");
|
||||
|
||||
List<Product> products = productRepository.findAll();
|
||||
var products = productRepository.findAll();
|
||||
|
||||
model.addAttribute("products", products);
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
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(@RequestParam String email, @RequestParam String password) {
|
||||
// System.out.println(email);
|
||||
// System.out.println(password);
|
||||
//
|
||||
// return "redirect:/";
|
||||
// }
|
||||
|
||||
@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:/";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package ru.lionarius.isdojplab.controller;
|
||||
|
||||
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.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
@@ -14,8 +14,13 @@ import ru.lionarius.isdojplab.repository.UserRepository;
|
||||
@Controller
|
||||
public class RegisterFormController {
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public RegisterFormController(UserRepository userRepository, PasswordEncoder passwordEncoder) {
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@GetMapping("/register")
|
||||
public String showForm(Model model) {
|
||||
@@ -40,10 +45,13 @@ public class RegisterFormController {
|
||||
return "register_form";
|
||||
}
|
||||
|
||||
var hashedPassword = passwordEncoder.encode(user.getPassword());
|
||||
user.setPassword(hashedPassword);
|
||||
|
||||
userRepository.save(user);
|
||||
|
||||
model.addAttribute("message", "Пользователь успешно зарегистрирован");
|
||||
|
||||
return "register_form";
|
||||
return "redirect:/login";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
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.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -8,10 +8,16 @@ import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
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
|
||||
@Entity
|
||||
public class User {
|
||||
public class User implements UserDetails {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@@ -26,4 +32,14 @@ public class User {
|
||||
@NotBlank(message = "Поле не может быть пустым")
|
||||
@Size(min = 6, message = "Пароль должен состоять не менее 6 символов")
|
||||
private String password;
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return List.of(new SimpleGrantedAuthority("SIMPLE_USER"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return email;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package ru.lionarius.isdojplab.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.lionarius.isdojplab.model.Product;
|
||||
|
||||
@Repository
|
||||
public interface ProductRepository extends JpaRepository<Product, Long> {
|
||||
public interface ProductRepository extends CrudRepository<Product, Long> {
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package ru.lionarius.isdojplab.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.lionarius.isdojplab.model.User;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends JpaRepository<User, Long> {
|
||||
public interface UserRepository extends CrudRepository<User, Long> {
|
||||
|
||||
Optional<User> findByEmail(String email);
|
||||
|
||||
Optional<User> findByName(String name);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<!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>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@@ -14,9 +14,17 @@
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<div sec:authorize="!isAuthenticated()">
|
||||
<div class="register-container">
|
||||
<a href="/register" class="register-button">Регистрация</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div sec:authorize="isAuthenticated()">
|
||||
<div class="register-container">
|
||||
<a href="/logout" class="register-button">Выход</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Каталог товаров</h2>
|
||||
|
||||
|
||||
42
src/main/resources/templates/login_form.html
Normal file
42
src/main/resources/templates/login_form.html
Normal 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>
|
||||
@@ -39,8 +39,10 @@
|
||||
</form>
|
||||
|
||||
<p th:if="${message}" th:text="${message}"></p>
|
||||
<a href="/login">Уже зарегистрированы?</a>
|
||||
</div>
|
||||
|
||||
|
||||
<footer>
|
||||
<p>© 2024 Магазин электротоваров. Все права защищены.</p>
|
||||
</footer>
|
||||
|
||||
Reference in New Issue
Block a user