This commit is contained in:
maxnes3 2023-05-25 01:50:41 +04:00
parent 9df9926503
commit 508954b902
43 changed files with 647 additions and 59 deletions

View File

@ -1,6 +1,6 @@
plugins { plugins {
id 'java' id 'java'
id 'org.springframework.boot' version '2.7.8' id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.0.15.RELEASE' id 'io.spring.dependency-management' version '1.0.15.RELEASE'
} }
@ -13,17 +13,24 @@ repositories {
} }
dependencies { dependencies {
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation 'org.webjars:bootstrap:5.1.3'
implementation 'org.webjars:jquery:3.6.0'
implementation 'org.webjars:font-awesome:6.1.0'
implementation 'com.h2database:h2:2.1.210'
implementation 'org.hibernate.validator:hibernate-validator:6.0.17.Final'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'com.auth0:java-jwt:4.4.0'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect'
implementation 'org.webjars:bootstrap:5.1.3'
implementation 'org.webjars:font-awesome:6.1.0'
implementation 'org.webjars:jquery:3.6.0'
implementation 'com.h2database:h2:2.1.210'
implementation 'jakarta.validation:jakarta.validation-api:3.0.0'
implementation 'org.hibernate.validator:hibernate-validator:7.0.1.Final'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
} }

Binary file not shown.

View File

@ -1,9 +1,9 @@
package ru.ip.labworks.labworks.bookshop.controller; /*package ru.ip.labworks.labworks.bookshop.controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import ru.ip.labworks.labworks.bookshop.service.AuthorService; import ru.ip.labworks.labworks.bookshop.service.AuthorService;
import ru.ip.labworks.labworks.configuration.WebConfiguration; import ru.ip.labworks.labworks.configuration.WebConfiguration;
import javax.validation.Valid; import jakarta.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -62,4 +62,4 @@ public class AuthorController {
public Map<String, List<String>> getAuthorsBooks(){ public Map<String, List<String>> getAuthorsBooks(){
return authorService.AllAuthorsAndBooks(); return authorService.AllAuthorsAndBooks();
} }
} }*/

View File

@ -1,4 +1,5 @@
package ru.ip.labworks.labworks.bookshop.controller; package ru.ip.labworks.labworks.bookshop.controller;
import org.springframework.security.core.Authentication;
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;
@ -6,8 +7,10 @@ import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import ru.ip.labworks.labworks.bookshop.service.AuthorService; import ru.ip.labworks.labworks.bookshop.service.AuthorService;
import ru.ip.labworks.labworks.bookshop.service.BookService; import ru.ip.labworks.labworks.bookshop.service.BookService;
import ru.ip.labworks.labworks.bookshop.service.UserService;
import java.io.IOException; import java.io.IOException;
import java.security.Principal;
import java.util.Base64; import java.util.Base64;
@Controller @Controller
@ -15,14 +18,17 @@ import java.util.Base64;
public class AuthorMvcController { public class AuthorMvcController {
private final AuthorService authorService; private final AuthorService authorService;
private final BookService bookService; private final BookService bookService;
public AuthorMvcController(AuthorService authorService, BookService bookService) private final UserService userService;
public AuthorMvcController(UserService userService, AuthorService authorService, BookService bookService)
{ {
this.authorService = authorService; this.authorService = authorService;
this.bookService = bookService; this.bookService = bookService;
this.userService = userService;
} }
@GetMapping @GetMapping
public String getAuthors(Model model) { public String getAuthors(Model model, Authentication authentication) {
model.addAttribute("user", userService.findByLogin(authentication.getName()));
model.addAttribute("authors", model.addAttribute("authors",
authorService.findAllAuthors().stream() authorService.findAllAuthors().stream()
.map(AuthorDto::new) .map(AuthorDto::new)
@ -32,8 +38,10 @@ public class AuthorMvcController {
@GetMapping(value = {"/update", "/update/{id}"}) @GetMapping(value = {"/update", "/update/{id}"})
public String updateAuthor(@PathVariable(required = false) Long id, public String updateAuthor(@PathVariable(required = false) Long id,
Model model) { Model model, Principal principal) {
if (id == null || id <= 0) { if (id == null || id <= 0) {
Long userId = userService.findByLogin(principal.getName()).getId();
model.addAttribute("userId",userId);
model.addAttribute("authorDto", new AuthorDto()); model.addAttribute("authorDto", new AuthorDto());
} else { } else {
model.addAttribute("authorDto", id); model.addAttribute("authorDto", id);
@ -47,15 +55,17 @@ public class AuthorMvcController {
@RequestParam(value = "multipartFile") MultipartFile multipartFile, @RequestParam(value = "multipartFile") MultipartFile multipartFile,
@ModelAttribute("authorDto") AuthorDto authorDto, @ModelAttribute("authorDto") AuthorDto authorDto,
BindingResult bindingResult, BindingResult bindingResult,
Model model) throws IOException { Model model, Principal principal) throws IOException {
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
model.addAttribute("errors", model.addAttribute("errors",
bindingResult.getAllErrors()); bindingResult.getAllErrors());
return "author-update"; return "author-update";
} }
Long userId = userService.findByLogin(principal.getName()).getId();
model.addAttribute("userId", userId);
authorDto.setPhoto("data:" + multipartFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(multipartFile.getBytes())); authorDto.setPhoto("data:" + multipartFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(multipartFile.getBytes()));
if (id == null || id <= 0) { if (id == null || id <= 0) {
return "redirect:/author/" + authorService.addAuthor(authorDto).getId().toString() + "/books"; return "redirect:/author/" + authorService.addAuthor(authorDto, userId).getId().toString() + "/books";
} else { } else {
authorService.updateAuthor(id, authorDto); authorService.updateAuthor(id, authorDto);
} }

View File

@ -3,7 +3,7 @@ import org.springframework.web.bind.annotation.*;
import ru.ip.labworks.labworks.bookshop.service.BookService; import ru.ip.labworks.labworks.bookshop.service.BookService;
import ru.ip.labworks.labworks.configuration.WebConfiguration; import ru.ip.labworks.labworks.configuration.WebConfiguration;
import javax.validation.Valid; import jakarta.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;

View File

@ -3,7 +3,7 @@ import org.springframework.web.bind.annotation.*;
import ru.ip.labworks.labworks.bookshop.service.GenreService; import ru.ip.labworks.labworks.bookshop.service.GenreService;
import ru.ip.labworks.labworks.configuration.WebConfiguration; import ru.ip.labworks.labworks.configuration.WebConfiguration;
import javax.validation.Valid; import jakarta.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;

View File

@ -3,7 +3,6 @@ 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;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import ru.ip.labworks.labworks.bookshop.service.BookService;
import ru.ip.labworks.labworks.bookshop.service.GenreService; import ru.ip.labworks.labworks.bookshop.service.GenreService;
import java.io.IOException; import java.io.IOException;

View File

@ -0,0 +1,28 @@
package ru.ip.labworks.labworks.bookshop.controller;
import ru.ip.labworks.labworks.bookshop.model.User;
import ru.ip.labworks.labworks.bookshop.model.UserRole;
public class UserDto {
private final long id;
private final String login;
private final UserRole role;
public UserDto(User user) {
this.id = user.getId();
this.login = user.getLogin();
this.role = user.getRole();
}
public long getId() {
return id;
}
public String getLogin() {
return login;
}
public UserRole getRole() {
return role;
}
}

View File

@ -0,0 +1,41 @@
package ru.ip.labworks.labworks.bookshop.controller;
import org.springframework.data.domain.Page;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import ru.ip.labworks.labworks.bookshop.model.UserRole;
import ru.ip.labworks.labworks.bookshop.service.UserService;
import java.util.List;
import java.util.stream.IntStream;
@Controller
@RequestMapping("/users")
public class UserMvcController {
private final UserService userService;
public UserMvcController(UserService userService) {
this.userService = userService;
}
@GetMapping
@Secured({UserRole.AsString.ADMIN})
public String getUsers(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "5") int size,
Model model) {
final Page<UserDto> users = userService.findAllPages(page, size)
.map(UserDto::new);
model.addAttribute("users", users);
final int totalPages = users.getTotalPages();
final List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
.boxed()
.toList();
model.addAttribute("pages", pageNumbers);
model.addAttribute("totalPages", totalPages);
return "users";
}
}

View File

@ -0,0 +1,40 @@
package ru.ip.labworks.labworks.bookshop.controller;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class UserSignUpDTO {
@NotBlank
@Size(min = 3, max = 64)
private String login;
@NotBlank
@Size(min = 6, max = 64)
private String password;
@NotBlank
@Size(min = 6, max = 64)
private String passwordConfirm;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPasswordConfirm() {
return passwordConfirm;
}
public void setPasswordConfirm(String passwordConfirm) {
this.passwordConfirm = passwordConfirm;
}
}

View File

@ -0,0 +1,48 @@
package ru.ip.labworks.labworks.bookshop.controller;
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 org.springframework.web.bind.annotation.RequestMapping;
import ru.ip.labworks.labworks.bookshop.model.User;
import ru.ip.labworks.labworks.bookshop.service.UserService;
import jakarta.validation.Valid;
import jakarta.validation.ValidationException;
@Controller
@RequestMapping(UserSignUpMvcController.SIGNUP_URL)
public class UserSignUpMvcController {
public static final String SIGNUP_URL = "/signup";
private final UserService userService;
public UserSignUpMvcController(UserService userService) {
this.userService = userService;
}
@GetMapping
public String showSignupForm(Model model) {
model.addAttribute("UserDTO", new UserSignUpDTO());
return "signup";
}
@PostMapping
public String signup(@ModelAttribute("UserDTO") @Valid UserSignUpDTO userSignupDto,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
model.addAttribute("errors", bindingResult.getAllErrors());
return "signup";
}
try {
final User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm());
return "redirect:/login?created=" + user.getLogin();
} catch (ValidationException e) {
model.addAttribute("errors", e.getMessage());
return "signup";
}
}
}

View File

@ -1,8 +1,8 @@
package ru.ip.labworks.labworks.bookshop.model; package ru.ip.labworks.labworks.bookshop.model;
import jakarta.persistence.*;
import ru.ip.labworks.labworks.bookshop.controller.AuthorDto; import ru.ip.labworks.labworks.bookshop.controller.AuthorDto;
import javax.persistence.*;
import java.util.*; import java.util.*;
@Entity @Entity
@ -20,6 +20,10 @@ public class Author {
inverseJoinColumns = @JoinColumn(name = "book_fk")) inverseJoinColumns = @JoinColumn(name = "book_fk"))
private List<Book> books; private List<Book> books;
@ManyToOne
@JoinColumn(name= "user_id", nullable = false)
private User user;
public Author(){} public Author(){}
public Author(String firstname, String lastname, byte[] photo){ public Author(String firstname, String lastname, byte[] photo){
this.firstname = firstname; this.firstname = firstname;
@ -74,4 +78,11 @@ public class Author {
public void removeBook(Book book) { public void removeBook(Book book) {
books.remove(book); books.remove(book);
} }
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
} }

View File

@ -1,10 +1,10 @@
package ru.ip.labworks.labworks.bookshop.model; package ru.ip.labworks.labworks.bookshop.model;
import jakarta.persistence.*;
import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollection;
import org.hibernate.annotations.LazyCollectionOption; import org.hibernate.annotations.LazyCollectionOption;
import ru.ip.labworks.labworks.bookshop.controller.BookDto; import ru.ip.labworks.labworks.bookshop.controller.BookDto;
import javax.persistence.*;
import java.util.*; import java.util.*;
@Entity @Entity
@ -26,6 +26,10 @@ public class Book {
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "books") @ManyToMany(fetch = FetchType.EAGER, mappedBy = "books")
private List<Author> authors; private List<Author> authors;
@ManyToOne
@JoinColumn(name= "user_id", nullable = false)
private User user;
public Book(){} public Book(){}
public Book(String name,Date release, byte[] cover){ public Book(String name,Date release, byte[] cover){
this.name = name; this.name = name;
@ -80,4 +84,11 @@ public class Book {
public void removeGenre(Genre genre) { public void removeGenre(Genre genre) {
genres.remove(genre); genres.remove(genre);
} }
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
} }

View File

@ -1,8 +1,8 @@
package ru.ip.labworks.labworks.bookshop.model; package ru.ip.labworks.labworks.bookshop.model;
import jakarta.persistence.*;
import ru.ip.labworks.labworks.bookshop.controller.GenreDto; import ru.ip.labworks.labworks.bookshop.controller.GenreDto;
import javax.persistence.*;
import java.util.*; import java.util.*;
@Entity @Entity
@ -12,6 +12,10 @@ public class Genre {
private Long id; private Long id;
private String name; private String name;
@ManyToOne
@JoinColumn(name= "user_id", nullable = false)
private User user;
public Genre(){} public Genre(){}
public Genre(String name){ public Genre(String name){
this.name = name; this.name = name;
@ -45,4 +49,11 @@ public class Genre {
", name='" + name + '\'' + ", name='" + name + '\'' +
'}'; '}';
} }
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
} }

View File

@ -0,0 +1,83 @@
package ru.ip.labworks.labworks.bookshop.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.util.Objects;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(nullable = false, unique = true, length = 64)
@NotBlank
@Size(min = 3, max = 64)
private String login;
@Column(nullable = false, length = 64)
@NotBlank
@Size(min = 6, max = 64)
private String password;
private UserRole role;
public User() {
}
public User(String login, String password) {
this(login, password, UserRole.USER);
}
public User(String login, String password, UserRole role) {
this.login = login;
this.password = password;
this.role = role;
}
public Long getId() {
return id;
}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public UserRole getRole() {
return role;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(login, user.login);
}
@Override
public int hashCode() {
return Objects.hash(id, login);
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", login='" + login + '\'' +
", password='" + password + '\'' +
", role='" + role + '\'' +
'}';
}
}

View File

@ -0,0 +1,20 @@
package ru.ip.labworks.labworks.bookshop.model;
import org.springframework.security.core.GrantedAuthority;
public enum UserRole implements GrantedAuthority {
ADMIN,
USER;
private static final String PREFIX = "ROLE_";
@Override
public String getAuthority() {
return PREFIX + this.name();
}
public static final class AsString {
public static final String ADMIN = PREFIX + "ADMIN";
public static final String USER = PREFIX + "USER";
}
}

View File

@ -0,0 +1,8 @@
package ru.ip.labworks.labworks.bookshop.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.ip.labworks.labworks.bookshop.model.User;
public interface UserRepository extends JpaRepository<User, Long> {
User findOneByLoginIgnoreCase(String login);
}

View File

@ -24,11 +24,14 @@ public class AuthorService {
private final BookService bookService; private final BookService bookService;
@Autowired @Autowired
private final ValidatorUtil validatorUtil; private final ValidatorUtil validatorUtil;
@Autowired
private final UserService userService;
public AuthorService(AuthorRepository authorRepository, BookService bookService, ValidatorUtil validatorUtil){ public AuthorService(AuthorRepository authorRepository, BookService bookService, ValidatorUtil validatorUtil, UserService userService){
this.authorRepository = authorRepository; this.authorRepository = authorRepository;
this.bookService = bookService; this.bookService = bookService;
this.validatorUtil = validatorUtil; this.validatorUtil = validatorUtil;
this.userService = userService;
} }
@Transactional @Transactional
@ -42,8 +45,10 @@ public class AuthorService {
} }
@Transactional @Transactional
public Author addAuthor(AuthorDto authorDto) throws IOException { public Author addAuthor(AuthorDto authorDto, Long userId) throws IOException {
User currentUser = userService.findUser(userId);
final Author author = new Author(authorDto); final Author author = new Author(authorDto);
author.setUser(currentUser);
validatorUtil.validate(author); validatorUtil.validate(author);
return authorRepository.save(author); return authorRepository.save(author);
} }

View File

@ -1,6 +1,6 @@
package ru.ip.labworks.labworks.bookshop.service; package ru.ip.labworks.labworks.bookshop.service;
import javax.imageio.ImageIO; //import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
@ -8,13 +8,14 @@ import java.io.File;
public class ImageHelper { public class ImageHelper {
public static byte[] ImageToByte(File image){ public static byte[] ImageToByte(File image){
try { try {
BufferedImage bufferedImage = ImageIO.read(image); /*BufferedImage bufferedImage = ImageIO.read(image);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream); ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);
return byteArrayOutputStream.toByteArray(); return byteArrayOutputStream.toByteArray();*/
} }
catch (Exception ex) { catch (Exception ex) {
return null; return null;
} }
return null;
} }
} }

View File

@ -0,0 +1,7 @@
package ru.ip.labworks.labworks.bookshop.service;
public class UserNotFoundException extends RuntimeException{
public UserNotFoundException(Long id) {
super(String.format("User with id [%s] is not found", id));
}
}

View File

@ -0,0 +1,66 @@
package ru.ip.labworks.labworks.bookshop.service;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.ip.labworks.labworks.bookshop.model.User;
import ru.ip.labworks.labworks.bookshop.model.UserRole;
import ru.ip.labworks.labworks.bookshop.repository.UserRepository;
import jakarta.validation.ValidationException;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public UserService(UserRepository userRepository,
PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
public Page<User> findAllPages(int page, int size) {
return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
}
public User findByLogin(String login) {
return userRepository.findOneByLoginIgnoreCase(login);
}
public User createUser(String login, String password, String passwordConfirm) {
return createUser(login, password, passwordConfirm, UserRole.USER);
}
public User createUser(String login, String password, String passwordConfirm, UserRole role) {
if (findByLogin(login) != null) {
throw new ValidationException(String.format("User '%s' already exists", login));
}
final User user = new User(login, passwordEncoder.encode(password), role);
if (!Objects.equals(password, passwordConfirm)) {
throw new ValidationException("Passwords not equals");
}
return userRepository.save(user);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final User userEntity = findByLogin(username);
if (userEntity == null) {
throw new UsernameNotFoundException(username);
}
return new org.springframework.security.core.userdetails.User(
userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
}
@Transactional(readOnly = true)
public User findUser(Long id) {
final Optional<User> user = userRepository.findById(id);
return user.orElseThrow(() -> new UserNotFoundException(id));
}
}

View File

@ -0,0 +1,14 @@
package ru.ip.labworks.labworks.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfiguration {
@Bean
public PasswordEncoder createPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,68 @@
package ru.ip.labworks.labworks.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.web.SecurityFilterChain;
import ru.ip.labworks.labworks.bookshop.controller.UserSignUpMvcController;
import ru.ip.labworks.labworks.bookshop.model.UserRole;
import ru.ip.labworks.labworks.bookshop.service.UserService;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(
securedEnabled = true
)
public class SecurityConfiguration {
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
private static final String LOGIN_URL = "/login";
private final UserService userService;
public SecurityConfiguration(UserService userService) {
this.userService = userService;
createAdminOnStartup();
}
private void createAdminOnStartup() {
final String admin = "admin";
if (userService.findByLogin(admin) == null) {
log.info("Admin user successfully created");
userService.createUser(admin, admin, admin, UserRole.ADMIN);
}
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.headers().frameOptions().sameOrigin().and()
.cors().and()
.csrf().disable()
.authorizeHttpRequests()
.requestMatchers(UserSignUpMvcController.SIGNUP_URL).permitAll()
.requestMatchers(HttpMethod.GET, LOGIN_URL).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage(LOGIN_URL).permitAll()
.defaultSuccessUrl("/author", true)
.and()
.logout().permitAll();
return http.userDetailsService(userService).build();
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring()
.requestMatchers("/css/**")
.requestMatchers("/js/**")
.requestMatchers("/templates/**")
.requestMatchers("/webjars/**");
}
}

View File

@ -11,7 +11,7 @@ public class WebConfiguration implements WebMvcConfigurer {
@Override @Override
public void addViewControllers(ViewControllerRegistry registry) { public void addViewControllers(ViewControllerRegistry registry) {
WebMvcConfigurer.super.addViewControllers(registry); WebMvcConfigurer.super.addViewControllers(registry);
registry.addViewController("author"); registry.addViewController("login");
} }
@Override @Override

View File

@ -2,10 +2,10 @@ package ru.ip.labworks.labworks.util.validation;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation; import jakarta.validation.ConstraintViolation;
import javax.validation.Validation; import jakarta.validation.Validation;
import javax.validation.Validator; import jakarta.validation.Validator;
import javax.validation.ValidatorFactory; import jakarta.validation.ValidatorFactory;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>
<div layout:fragment="content"> <div layout:fragment="content">
<div> <div>
<a class="btn btn-success button-fixed" <a class="btn btn-success button-fixed"
th:href="@{/author/update/}"> th:href="@{/author/update}">
<i class="fa-solid fa-plus"></i> Добавить <i class="fa-solid fa-plus"></i> Добавить
</a> </a>
<a class="btn btn-primary button-fixed" <a class="btn btn-primary button-fixed"

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>
<div layout:fragment="content"> <div layout:fragment="content">
<div> <div>
<a class="btn btn-success button-fixed" <a class="btn btn-success button-fixed"
th:href="@{/book/update/}"> th:href="@{/book/update}">
<i class="fa-solid fa-plus"></i> Добавить <i class="fa-solid fa-plus"></i> Добавить
</a> </a>
</div> </div>

View File

@ -22,10 +22,12 @@
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup"> <div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav" th:with="activeLink=${#request.requestURI}"> <div class="navbar-nav">
<a class="nav-link active text-white" aria-current="page" href="/author" th:classappend="${#strings.equals(activeLink, '/author')} ? 'active' : ''">Authors</a> <a class="nav-link active text-white" aria-current="page" href="/author" th:classappend="${#strings.equals(activeLink, '/author')} ? 'active' : ''">Authors</a>
<a class="nav-link active text-white" href="/book" th:classappend="${#strings.equals(activeLink, '/book')} ? 'active' : ''">Books</a> <a class="nav-link active text-white" href="/book" th:classappend="${#strings.equals(activeLink, '/book')} ? 'active' : ''">Books</a>
<a class="nav-link active text-white" href="/genre" th:classappend="${#strings.equals(activeLink, '/genre')} ? 'active' : ''">Genres</a> <a class="nav-link active text-white" href="/genre" th:classappend="${#strings.equals(activeLink, '/genre')} ? 'active' : ''">Genres</a>
<a class="nav-link active text-white" href="/users" th:classappend="${#strings.equals(activeLink, '/users')} ? 'active' : ''">Users</a>
<a class="nav-link active text-white" href="/logout" th:classappend="${#strings.equals(activeLink, '/login')} ? 'active' : ''">Logout</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en"
<head> xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
<meta charset="UTF-8"> layout:decorate="~{default}">
<title>Title</title>
</head>
<body> <body>
<div class="container" layout:fragment="content">
<div class="alert alert-danger">
<span th:text="${error}"></span>
</div>
<a href="/author">На главную</a>
</div>
</body> </body>
</html> </html>

View File

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>

View File

@ -1,14 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" <html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{default}"> layout:decorate="~{default}" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
</head> </head>
<body> <body>
<div layout:fragment="content"> <div layout:fragment="content">
<div> <div>
<a class="btn btn-success button-fixed" <a class="btn btn-success button-fixed"
th:href="@{/genre/update/}"> th:href="@{/genre/update}">
<i class="fa-solid fa-plus"></i> Добавить <i class="fa-solid fa-plus"></i> Добавить
</a> </a>
</div> </div>

View File

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.w3.org/1999/xhtml"
layout:decorate="~{default}">
<body>
<div layout:fragment="content">
<div th:if="${param.error}" class="alert alert-danger margin-bottom">
User not found
</div>
<div th:if="${param.logout}" class="alert alert-success margin-bottom">
Logout success
</div>
<div th:if="${param.created}" class="alert alert-success margin-bottom">
User '<span th:text="${param.created}"></span>' was successfully created
</div>
<form th:action="@{/login}" method="post">
<div class="mb-3">
<p class="mb-1">Login</p>
<input name="username" id="username" class="form-control"
type="text" required autofocus />
</div>
<div class="mb-3">
<p class="mb-1">Password</p>
<input name="password" id="password" class="form-control"
type="password" required />
</div>
<div class="mb-3">
<button type="submit" class="btn btn-success">
Sign in
</button>
</div>
<div>
<p>
<span>Not a member yet?</span>
<a href="/signup">Sign Up here</a>
</p>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.thymeleaf.org"
layout:decorate="~{default}">
<body>
<div class="container container-padding mt-3" layout:fragment="content">
<div th:if="${errors}" th:text="${errors}" class="margin-bottom alert alert-danger"></div>
<form action="#" th:action="@{/signup}" th:object="${UserDTO}" method="post">
<div class="mb-3">
<input type="text" class="form-control" th:field="${UserDTO.login}"
placeholder="Логин" required="true" autofocus="true" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${UserDTO.password}"
placeholder="Пароль" required="true" minlength="6" maxlength="64"/>
</div>
<div class="mb-3">
<input type="password" class="form-control" th:field="${UserDTO.passwordConfirm}"
placeholder="Пароль (подтверждение)" required="true" minlength="6" maxlength="64"/>
</div>
<div class="mx-4">
<button type="submit" class="btn btn-success button-fixed">Создать</button>
<a class="btn btn-primary button-fixed" href="/login">Назад</a>
</div>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.thymeleaf.org"
layout:decorate="~{default}">
<body>
<div class="container" layout:fragment="content">
<div class="table-responsive">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">ID</th>
<th scope="col">Логин</th>
<th scope="col">Роль</th>
</tr>
</thead>
<tbody>
<tr th:each="user, iterator: ${users}">
<th scope="row" th:text="${iterator.index} + 1"></th>
<td th:text="${user.id}"></td>
<td th:text="${user.login}" style="width: 60%"></td>
<td th:text="${user.role}" style="width: 20%"></td>
</tr>
</tbody>
</table>
</div>
<div th:if="${totalPages > 0}" class="pagination">
<span style="float: left; padding: 5px 5px;">Страницы:</span>
<a th:each="page : ${pages}"
th:href="@{/users(page=${page}, size=${users.size})}"
th:text="${page}"
th:class="${page == users.number + 1} ? active">
</a>
</div>
</div>
</body>
</html>

View File

@ -8,7 +8,6 @@ import ru.ip.labworks.labworks.bookshop.model.Author;
import ru.ip.labworks.labworks.bookshop.model.Book; import ru.ip.labworks.labworks.bookshop.model.Book;
import ru.ip.labworks.labworks.bookshop.service.AuthorService; import ru.ip.labworks.labworks.bookshop.service.AuthorService;
import ru.ip.labworks.labworks.bookshop.service.BookService; import ru.ip.labworks.labworks.bookshop.service.BookService;
import javax.persistence.EntityNotFoundException;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;

View File

@ -10,7 +10,6 @@ import ru.ip.labworks.labworks.bookshop.service.BookService;
import ru.ip.labworks.labworks.bookshop.service.GenreService; import ru.ip.labworks.labworks.bookshop.service.GenreService;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.persistence.EntityNotFoundException;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;

View File

@ -7,7 +7,6 @@ import org.springframework.boot.test.context.SpringBootTest;
import ru.ip.labworks.labworks.bookshop.model.Genre; import ru.ip.labworks.labworks.bookshop.model.Genre;
import ru.ip.labworks.labworks.bookshop.service.GenreService; import ru.ip.labworks.labworks.bookshop.service.GenreService;
import javax.persistence.EntityNotFoundException;
import java.util.List; import java.util.List;
@SpringBootTest @SpringBootTest

View File

@ -5,11 +5,10 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import ru.ip.labworks.labworks.calculator.service.CalculatorService;
@SpringBootTest @SpringBootTest
class LabworksApplicationTests { class LabworksApplicationTests {
@Autowired /*@Autowired
CalculatorService calculatorService; CalculatorService calculatorService;
@Test @Test
@ -87,5 +86,5 @@ class LabworksApplicationTests {
@Test @Test
void testErrorErrorWired() { void testErrorErrorWired() {
Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> calculatorService.Plus("date", 1, 2)); Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> calculatorService.Plus("date", 1, 2));
} }*/
} }