diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorController.java index 2238588..64b483d 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorController.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorController.java @@ -1,7 +1,6 @@ -/*package ru.ip.labworks.labworks.bookshop.controller; +package ru.ip.labworks.labworks.bookshop.controller; import org.springframework.web.bind.annotation.*; import ru.ip.labworks.labworks.bookshop.service.AuthorService; -import ru.ip.labworks.labworks.configuration.WebConfiguration; import jakarta.validation.Valid; import java.io.IOException; @@ -9,7 +8,7 @@ import java.util.List; import java.util.Map; @RestController -@RequestMapping(WebConfiguration.REST_API + "/author") +@RequestMapping("/author") public class AuthorController { private final AuthorService authorService; @@ -62,4 +61,4 @@ public class AuthorController { public Map> getAuthorsBooks(){ return authorService.AllAuthorsAndBooks(); } -}*/ +} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorDto.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorDto.java index 7bd814b..355af27 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorDto.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorDto.java @@ -9,6 +9,7 @@ public class AuthorDto { private String firstname; private String lastname; private String photo; + private String login; public AuthorDto(){} public AuthorDto(Author author){ @@ -16,15 +17,18 @@ public class AuthorDto { firstname = author.getFirstnameName(); lastname = author.getLastName(); photo = new String(author.getPhoto(), StandardCharsets.UTF_8); + login = author.getUser().getLogin(); } public Long getId(){return id;} public String getFirstname(){return firstname;} public String getLastname(){return lastname;} public String getPhoto(){return photo;} + public String getLogin(){return login;} public void setId(Long id){this.id = id;} public void setFirstname(String firstname){this.firstname = firstname;} public void setLastname(String lastname){this.lastname = lastname;} public void setPhoto(String photo){this.photo = photo;} + public void setLogin(String login){this.login = login;} } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorMvcController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorMvcController.java deleted file mode 100644 index 741f5cb..0000000 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/AuthorMvcController.java +++ /dev/null @@ -1,114 +0,0 @@ -package ru.ip.labworks.labworks.bookshop.controller; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import ru.ip.labworks.labworks.bookshop.service.AuthorService; -import ru.ip.labworks.labworks.bookshop.service.BookService; -import ru.ip.labworks.labworks.bookshop.service.UserService; - -import java.io.IOException; -import java.security.Principal; -import java.util.Base64; - -@Controller -@RequestMapping("/author") -public class AuthorMvcController { - private final AuthorService authorService; - private final BookService bookService; - private final UserService userService; - public AuthorMvcController(UserService userService, AuthorService authorService, BookService bookService) - { - this.authorService = authorService; - this.bookService = bookService; - this.userService = userService; - } - - @GetMapping - public String getAuthors(Model model, Authentication authentication) { - model.addAttribute("user", userService.findByLogin(authentication.getName())); - model.addAttribute("authors", - authorService.findAllAuthors().stream() - .map(AuthorDto::new) - .toList()); - return "authors"; - } - - @GetMapping(value = {"/update", "/update/{id}"}) - public String updateAuthor(@PathVariable(required = false) Long id, - Model model, Principal principal) { - if (id == null || id <= 0) { - Long userId = userService.findByLogin(principal.getName()).getId(); - model.addAttribute("userId",userId); - model.addAttribute("authorDto", new AuthorDto()); - } else { - model.addAttribute("authorDto", id); - model.addAttribute("authorDto", new AuthorDto(authorService.findAuthor(id))); - } - return "author-update"; - } - - @PostMapping(value = {"/", "/{id}"}) - public String saveAuthor(@PathVariable(required = false) Long id, - @RequestParam(value = "multipartFile") MultipartFile multipartFile, - @ModelAttribute("authorDto") AuthorDto authorDto, - BindingResult bindingResult, - Model model, Principal principal) throws IOException { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", - bindingResult.getAllErrors()); - 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())); - if (id == null || id <= 0) { - return "redirect:/author/" + authorService.addAuthor(authorDto, userId).getId().toString() + "/books"; - } else { - authorService.updateAuthor(id, authorDto, userId); - } - return "redirect:/author"; - } - - @PostMapping("/delete/{id}") - public String deleteAuthor(@PathVariable Long id, Principal principal) { - authorService.deleteAuthor(id, userService.findByLogin(principal.getName()).getId()); - return "redirect:/author"; - } - - @GetMapping("/{id}/books") - public String getAuthorBooks(@PathVariable Long id, Model model){ - model.addAttribute("author", - new AuthorDto(authorService.findAuthor(id))); - model.addAttribute("authorbooks", - authorService.authorBooks(id).stream() - .map(BookDto::new) - .toList()); - model.addAttribute("books", - bookService.findAllBooks().stream() - .map(BookDto::new) - .toList()); - return "author-mtm"; - } - - @PostMapping("/{id}/books") - public String addBookToAuthor(@PathVariable Long id, @RequestParam(value = "bookid") Long bookid, Principal principal){ - authorService.addBookToAuthor(id, bookid, userService.findByLogin(principal.getName()).getId()); - return "redirect:/author/" + id.toString() + "/books"; - } - - @PostMapping("/{id}/books/{bookid}") - public String removeBookFromAuthor(@PathVariable Long id, @PathVariable Long bookid, Principal principal){ - authorService.removeBookFromAuthor(id, bookid, userService.findByLogin(principal.getName()).getId()); - return "redirect:/author/" + id.toString() + "/books"; - } - - @GetMapping("/books") - private String getAllAuthorsBooks(Model model){ - model.addAttribute("authorsbooks", - authorService.AllAuthorsAndBooks()); - return "all-authors-books"; - } -} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookController.java index a7dbc85..e0c658a 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookController.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookController.java @@ -1,15 +1,13 @@ -/* package ru.ip.labworks.labworks.bookshop.controller; import org.springframework.web.bind.annotation.*; import ru.ip.labworks.labworks.bookshop.service.BookService; -import ru.ip.labworks.labworks.configuration.WebConfiguration; import jakarta.validation.Valid; import java.io.IOException; import java.util.List; @RestController -@RequestMapping(WebConfiguration.REST_API + "/book") +@RequestMapping("/book") public class BookController { private final BookService bookService; @@ -58,4 +56,3 @@ public class BookController { bookService.deleteBook(id); } } -*/ diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookDto.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookDto.java index 6754a9b..9a0d78b 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookDto.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookDto.java @@ -12,6 +12,7 @@ public class BookDto { @DateTimeFormat(pattern = "yyyy-MM-dd") private Date release; private String cover; + private String login; public BookDto(){} public BookDto(Book book){ @@ -19,15 +20,18 @@ public class BookDto { name = book.getName(); release = book.getRelease(); cover = new String(book.getCover(), StandardCharsets.UTF_8); + login = book.getUser().getLogin(); } public Long getId(){return id;} public String getName(){return name;} public Date getRelease(){return release;} public String getCover(){return cover;} + public String getLogin(){return login;} public void setId(Long id){this.id = id;} public void setName(String name){this.name = name;} public void setRelease(Date release){this.release = release;} public void setCover(String cover){this.cover = cover;} + public void setLogin(String login){this.login = login;} } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookMvcController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookMvcController.java deleted file mode 100644 index fc8936c..0000000 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/BookMvcController.java +++ /dev/null @@ -1,106 +0,0 @@ -package ru.ip.labworks.labworks.bookshop.controller; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; -import ru.ip.labworks.labworks.bookshop.service.BookService; -import ru.ip.labworks.labworks.bookshop.service.GenreService; -import ru.ip.labworks.labworks.bookshop.service.UserService; - -import java.io.IOException; -import java.security.Principal; -import java.util.Base64; - -@Controller -@RequestMapping("/book") -public class BookMvcController { - private final BookService bookService; - private final GenreService genreService; - private final UserService userService; - public BookMvcController(BookService bookService, GenreService genreService, UserService userService){ - this.bookService = bookService; - this.genreService = genreService; - this.userService = userService; - } - - @GetMapping - public String getBooks(Model model, Authentication authentication) { - model.addAttribute("user", userService.findByLogin(authentication.getName())); - model.addAttribute("books", - bookService.findAllBooks().stream() - .map(BookDto::new) - .toList()); - return "books"; - } - - @GetMapping(value = {"/update", "/update/{id}"}) - public String updateBook(@PathVariable(required = false) Long id, - Model model, Principal principal) { - if (id == null || id <= 0) { - Long userId = userService.findByLogin(principal.getName()).getId(); - model.addAttribute("userId",userId); - model.addAttribute("bookDto", new BookDto()); - } else { - model.addAttribute("bookDto", id); - model.addAttribute("bookDto", new BookDto(bookService.findBook(id))); - } - return "book-update"; - } - - @PostMapping(value = {"/", "/{id}"}) - public String saveBook(@PathVariable(required = false) Long id, - @RequestParam(value = "multipartFile") MultipartFile multipartFile, - @ModelAttribute("bookDto") BookDto bookDto, - BindingResult bindingResult, - Model model, Principal principal) throws IOException { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", - bindingResult.getAllErrors()); - return "book-update"; - } - Long userId = userService.findByLogin(principal.getName()).getId(); - model.addAttribute("userId", userId); - bookDto.setCover("data:" + multipartFile.getContentType() + ";base64," + Base64.getEncoder().encodeToString(multipartFile.getBytes())); - if (id == null || id <= 0) { - return "redirect:/book/" + bookService.addBook(bookDto, userId).getId().toString() + "/genres"; - } else { - bookService.updateBook(id, bookDto, userId); - } - return "redirect:/book"; - } - - @PostMapping("/delete/{id}") - public String deleteBook(@PathVariable Long id, Principal principal) { - bookService.deleteBook(id, userService.findByLogin(principal.getName()).getId()); - return "redirect:/book"; - } - - @GetMapping("/{id}/genres") - public String getBookGenres(@PathVariable Long id, Model model){ - model.addAttribute("book", - new BookDto(bookService.findBook(id))); - model.addAttribute("bookgenres", - bookService.bookGenres(id).stream() - .map(GenreDto::new) - .toList()); - model.addAttribute("genres", - genreService.findAllGenres().stream() - .map(GenreDto::new) - .toList()); - return "book-mtm"; - } - - @PostMapping("/{id}/genres") - public String addGenreToBook(@PathVariable Long id, @RequestParam(value = "genreid") Long genreid, Principal principal){ - bookService.addGenreToBook(id, genreid, userService.findByLogin(principal.getName()).getId()); - return "redirect:/book/" + id.toString() + "/genres"; - } - - @PostMapping("/{id}/genres/{genreid}") - public String removeGenreFromBook(@PathVariable Long id, @PathVariable Long genreid, Principal principal){ - bookService.removeGenreFromBook(id, genreid, userService.findByLogin(principal.getName()).getId()); - return "redirect:/book/" + id.toString() + "/genres"; - } -} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreController.java index bbcceee..a02983d 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreController.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreController.java @@ -1,14 +1,13 @@ -/*package ru.ip.labworks.labworks.bookshop.controller; +package ru.ip.labworks.labworks.bookshop.controller; import org.springframework.web.bind.annotation.*; import ru.ip.labworks.labworks.bookshop.service.GenreService; -import ru.ip.labworks.labworks.configuration.WebConfiguration; import jakarta.validation.Valid; import java.io.IOException; import java.util.List; @RestController -@RequestMapping(WebConfiguration.REST_API + "/genre") +@RequestMapping("/genre") public class GenreController { private final GenreService genreService; @@ -40,4 +39,4 @@ public class GenreController { public void deleteGenre(@PathVariable Long id){ genreService.deleteGenre(id); } -}*/ +} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreDto.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreDto.java index 30f081e..43e2aab 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreDto.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreDto.java @@ -5,16 +5,20 @@ import ru.ip.labworks.labworks.bookshop.model.Genre; public class GenreDto { private Long id; private String name; + private String login; public GenreDto(){} public GenreDto(Genre genre){ id = genre.getId(); name = genre.getName(); + login = genre.getUser().getLogin(); } public Long getId(){return id;} public String getName(){return name;} + public String getLogin(){return login;} public void setId(Long id){this.id = id;} public void setName(String name){this.name = name;} + public void setLogin(String login){this.login = login;} } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreMvcController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreMvcController.java deleted file mode 100644 index 9c12284..0000000 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/GenreMvcController.java +++ /dev/null @@ -1,72 +0,0 @@ -package ru.ip.labworks.labworks.bookshop.controller; -import org.springframework.security.core.Authentication; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; -import ru.ip.labworks.labworks.bookshop.service.GenreService; -import ru.ip.labworks.labworks.bookshop.service.UserService; - -import java.io.IOException; -import java.security.Principal; - -@Controller -@RequestMapping("/genre") -public class GenreMvcController { - private final GenreService genreService; - private final UserService userService; - public GenreMvcController(GenreService genreService, UserService userService){ - this.genreService = genreService; - this.userService = userService; - } - - @GetMapping - public String getBooks(Model model, Authentication authentication) { - model.addAttribute("user", userService.findByLogin(authentication.getName())); - model.addAttribute("genres", - genreService.findAllGenres().stream() - .map(GenreDto::new) - .toList()); - return "genres"; - } - - @GetMapping(value = {"/update", "/update/{id}"}) - public String editBook(@PathVariable(required = false) Long id, - Model model, Principal principal) { - if (id == null || id <= 0) { - Long userId = userService.findByLogin(principal.getName()).getId(); - model.addAttribute("userId",userId); - model.addAttribute("genreDto", new GenreDto()); - } else { - model.addAttribute("genreDto", id); - model.addAttribute("genreDto", new GenreDto(genreService.findGenre(id))); - } - return "genre-update"; - } - - @PostMapping(value = {"/", "/{id}"}) - public String saveBook(@PathVariable(required = false) Long id, - @ModelAttribute("genreDto") GenreDto genreDto, - BindingResult bindingResult, - Model model, Principal principal) throws IOException { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", - bindingResult.getAllErrors()); - return "genre-update"; - } - Long userId = userService.findByLogin(principal.getName()).getId(); - model.addAttribute("userId", userId); - if (id == null || id <= 0) { - genreService.addGenre(genreDto, userId); - } else { - genreService.updateGenre(id, genreDto, userId); - } - return "redirect:/genre"; - } - - @PostMapping("/delete/{id}") - public String deleteGenre(@PathVariable Long id, Principal principal) { - genreService.deleteGenre(id, userService.findByLogin(principal.getName()).getId()); - return "redirect:/genre"; - } -} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserController.java new file mode 100644 index 0000000..5110b26 --- /dev/null +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserController.java @@ -0,0 +1,64 @@ +package ru.ip.labworks.labworks.bookshop.controller; + +import jakarta.validation.Valid; +import jakarta.validation.ValidationException; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; +import ru.ip.labworks.labworks.bookshop.model.User; +import ru.ip.labworks.labworks.bookshop.model.UserRole; +import ru.ip.labworks.labworks.bookshop.service.UserService; + +import java.util.List; + +@RestController +public class UserController { + public static final String URL_LOGIN = "/jwt/login"; + public static final String URL_SIGN_UP = "/sign_up"; + public static final String URL_WHO_AM_I = "/who_am_i"; + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + @PostMapping(URL_LOGIN) + public String login(@RequestBody @Valid UserDto userDto) { + return userService.loginAndGetToken(userDto); + } + @GetMapping(URL_WHO_AM_I) + public String role(@RequestParam("userLogin") String userLogin) { + return userService.findByLogin(userLogin).getRole().name(); + } + @PostMapping(URL_SIGN_UP) + public String signUp(@RequestBody @Valid UserSignUpDTO userSignupDto) { + try { + final User user = userService.addUser(userSignupDto.getLogin(), + userSignupDto.getPassword(), userSignupDto.getPasswordConfirm(), UserRole.USER); + return "created " + user.getLogin(); + } catch (ValidationException e) { + return e.getMessage(); + } + } + @GetMapping("/{id}") + @Secured({UserRole.AsString.ADMIN}) + public UserDto getUser(@PathVariable Long id) { + return new UserDto(userService.findUser(id)); + } + @GetMapping("/") + @Secured({UserRole.AsString.ADMIN}) + public List getUsers() { + return userService.findAllUsers().stream() + .map(UserDto::new) + .toList(); + } + + @PutMapping("/{id}") + public UserDto updateUser(@RequestBody @Valid UserDto userDto){ + return new UserDto(userService.updateUser(userDto)); + } + + + @DeleteMapping("/{id}") + public void deleteUser(@PathVariable Long id) { + userService.deleteUser(id); + } +} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserDto.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserDto.java index 6e899bc..8bd57fc 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserDto.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserDto.java @@ -4,14 +4,17 @@ 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; + private long id; + private String login; + private UserRole role; + private String password; + public UserDto(){} public UserDto(User user) { this.id = user.getId(); this.login = user.getLogin(); this.role = user.getRole(); + this.password = user.getPassword(); } public long getId() { @@ -25,4 +28,23 @@ public class UserDto { public UserRole getRole() { return role; } + + public String getPassword() + { + return password; + } + + public void setId(Long id) { + this.id = id; + } + + public void setLogin(String login) + { + this.login = login; + } + + public void setPassword(String password) + { + this.password = password; + } } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserMvcController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserMvcController.java deleted file mode 100644 index 7a26f30..0000000 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserMvcController.java +++ /dev/null @@ -1,41 +0,0 @@ -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 users = userService.findAllPages(page, size) - .map(UserDto::new); - model.addAttribute("users", users); - final int totalPages = users.getTotalPages(); - final List pageNumbers = IntStream.rangeClosed(1, totalPages) - .boxed() - .toList(); - model.addAttribute("pages", pageNumbers); - model.addAttribute("totalPages", totalPages); - return "users"; - } -} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserSignUpMvcController.java b/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserSignUpMvcController.java deleted file mode 100644 index feb94aa..0000000 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/controller/UserSignUpMvcController.java +++ /dev/null @@ -1,48 +0,0 @@ -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"; - } - } -} diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/model/User.java b/src/main/java/ru/ip/labworks/labworks/bookshop/model/User.java index e1d4f0f..fb58836 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/model/User.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/model/User.java @@ -4,6 +4,8 @@ import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; +import ru.ip.labworks.labworks.bookshop.controller.UserSignUpDTO; + import java.util.Objects; @Entity @@ -35,6 +37,12 @@ public class User { this.role = role; } + public User(UserSignUpDTO userDto){ + this.login = userDto.getLogin(); + this.password = userDto.getPassword(); + this.role = UserRole.USER; + } + public Long getId() { return id; } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/service/AuthorService.java b/src/main/java/ru/ip/labworks/labworks/bookshop/service/AuthorService.java index 60dc6cc..c2ca12f 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/service/AuthorService.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/service/AuthorService.java @@ -45,8 +45,8 @@ public class AuthorService { } @Transactional - public Author addAuthor(AuthorDto authorDto, Long userId) throws IOException { - User currentUser = userService.findUser(userId); + public Author addAuthor(AuthorDto authorDto) throws IOException { + User currentUser = userService.findByLogin(authorDto.getLogin()); final Author author = new Author(authorDto); author.setUser(currentUser); validatorUtil.validate(author); @@ -88,8 +88,8 @@ public class AuthorService { } @Transactional - public Author updateAuthor(Long id, AuthorDto authorDto, Long userId){ - User currentUser = userService.findUser(userId); + public Author updateAuthor(Long id, AuthorDto authorDto){ + User currentUser = userService.findByLogin(authorDto.getLogin()); final Author currentAuthor = findAuthor(id); if(currentUser.getId() != currentAuthor.getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ return null; @@ -102,12 +102,7 @@ public class AuthorService { } @Transactional - public void deleteAuthor(Long id, Long userId) { - User currentUser = userService.findUser(userId); - final Author currentAuthor = findAuthor(id); - if(currentUser.getId() != currentAuthor.getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } + public void deleteAuthor(Long id) { authorRepository.deleteById(id); } @@ -117,12 +112,8 @@ public class AuthorService { } @Transactional - public void addBookToAuthor(Long id, Long bookId, Long userId){ - User currentUser = userService.findUser(userId); + public void addBookToAuthor(Long id, Long bookId){ Optional author = authorRepository.findById(id); - if(currentUser.getId() != author.get().getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } if (author.isPresent() && !author.get().getBooks().contains(bookService.findBook(bookId))){ author.get().addBook(bookService.findBook(bookId)); } @@ -130,12 +121,8 @@ public class AuthorService { } @Transactional - public void removeBookFromAuthor(Long id, Long bookId, Long userId){ - User currentUser = userService.findUser(userId); + public void removeBookFromAuthor(Long id, Long bookId){ Optional author = authorRepository.findById(id); - if(currentUser.getId() != author.get().getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } if(author.isPresent() && author.get().getBooks().contains(bookService.findBook(bookId))){ author.get().removeBook(bookService.findBook(bookId)); } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/service/BookService.java b/src/main/java/ru/ip/labworks/labworks/bookshop/service/BookService.java index b7fc682..ef63a1f 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/service/BookService.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/service/BookService.java @@ -58,8 +58,8 @@ public class BookService { } @Transactional - public Book addBook(BookDto bookDto, Long userId) throws IOException { - User currentUser = userService.findUser(userId); + public Book addBook(BookDto bookDto) throws IOException { + User currentUser = userService.findByLogin(bookDto.getLogin()); final Book book = new Book(bookDto); book.setUser(currentUser); validatorUtil.validate(book); @@ -96,8 +96,8 @@ public class BookService { } @Transactional - public Book updateBook(Long id, BookDto bookDto, Long userId){ - User currentUser = userService.findUser(userId); + public Book updateBook(Long id, BookDto bookDto){ + User currentUser = userService.findByLogin(bookDto.getLogin()); final Book currentBook = findBook(id); if(currentUser.getId() != currentBook.getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ return null; @@ -110,12 +110,7 @@ public class BookService { } @Transactional - public void deleteBook(Long id, Long userId) { - User currentUser = userService.findUser(userId); - final Book currentBook = findBook(id); - if(currentUser.getId() != currentBook.getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } + public void deleteBook(Long id) { bookRepository.deleteById(id); } @@ -125,12 +120,8 @@ public class BookService { } @Transactional - public void addGenreToBook(Long id, Long genreId, Long userId){ - User currentUser = userService.findUser(userId); + public void addGenreToBook(Long id, Long genreId){ Optional book = bookRepository.findById(id); - if(currentUser.getId() != book.get().getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } if (book.isPresent() && !book.get().getGenres().contains(genreService.findGenre(genreId))){ book.get().addGenre(genreService.findGenre(genreId)); } @@ -138,12 +129,8 @@ public class BookService { } @Transactional - public void removeGenreFromBook(Long id, Long genreId, Long userId){ - User currentUser = userService.findUser(userId); + public void removeGenreFromBook(Long id, Long genreId){ Optional book = bookRepository.findById(id); - if(currentUser.getId() != book.get().getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } if(book.isPresent() && book.get().getGenres().contains(genreService.findGenre(genreId))){ book.get().removeGenre(genreService.findGenre(genreId)); } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/service/GenreService.java b/src/main/java/ru/ip/labworks/labworks/bookshop/service/GenreService.java index 626b2af..e16eebb 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/service/GenreService.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/service/GenreService.java @@ -39,8 +39,8 @@ public class GenreService { return genreRepository.save(genre); } @Transactional - public Genre addGenre(GenreDto genreDto, Long userId) throws IOException { - User currentUser = userService.findUser(userId); + public Genre addGenre(GenreDto genreDto) throws IOException { + User currentUser = userService.findByLogin(genreDto.getLogin()); final Genre genre = new Genre(genreDto); genre.setUser(currentUser); validatorUtil.validate(genre); @@ -69,8 +69,8 @@ public class GenreService { } @Transactional - public Genre updateGenre(Long id, GenreDto genreDto, Long userId){ - User currentUser = userService.findUser(userId); + public Genre updateGenre(Long id, GenreDto genreDto){ + User currentUser = userService.findByLogin(genreDto.getLogin()); final Genre currentGenre = findGenre(id); if(currentUser.getId() != currentGenre.getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ return null; @@ -80,12 +80,7 @@ public class GenreService { } @Transactional - public void deleteGenre(Long id, Long userId) { - User currentUser = userService.findUser(userId); - final Genre currentGenre = findGenre(id); - if(currentUser.getId() != currentGenre.getUser().getId() && currentUser.getRole() != UserRole.ADMIN){ - return; - } + public void deleteGenre(Long id) { genreRepository.deleteById(id); } diff --git a/src/main/java/ru/ip/labworks/labworks/bookshop/service/UserService.java b/src/main/java/ru/ip/labworks/labworks/bookshop/service/UserService.java index 9324570..faaf773 100644 --- a/src/main/java/ru/ip/labworks/labworks/bookshop/service/UserService.java +++ b/src/main/java/ru/ip/labworks/labworks/bookshop/service/UserService.java @@ -9,12 +9,19 @@ 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 org.springframework.util.StringUtils; +import ru.ip.labworks.labworks.bookshop.controller.UserDto; +import ru.ip.labworks.labworks.bookshop.controller.UserSignUpDTO; 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 ru.ip.labworks.labworks.configuration.jwt.JwtException; +import ru.ip.labworks.labworks.configuration.jwt.JwtProvider; + import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -22,31 +29,77 @@ import java.util.Optional; public class UserService implements UserDetailsService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; + private final JwtProvider jwtProvider; public UserService(UserRepository userRepository, - PasswordEncoder passwordEncoder) { + PasswordEncoder passwordEncoder, + JwtProvider jwtProvider){ this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; - } - public Page findAllPages(int page, int size) { - return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending())); + this.jwtProvider = jwtProvider; } 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 Page findAllPages(int page, int size) { + return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending())); } - public User createUser(String login, String password, String passwordConfirm, UserRole role) { + @Transactional + public User addUser(UserSignUpDTO userDto){ + final User user = new User(userDto); + userRepository.save(user); + return user; + } + @Transactional + public User addUser(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"); } + final User user = new User(login,passwordEncoder.encode(password),role); return userRepository.save(user); } + @Transactional(readOnly = true) + public User findUser(Long id) { + final Optional user = userRepository.findById(id); + return user.orElseThrow(() -> new UserNotFoundException(id)); + } + + @Transactional(readOnly = true) + public List findAllUsers() { + return userRepository.findAll(); + } + + @Transactional + public User updateUser(UserDto userDto) { + final User currentUser = findUser(userDto.getId()); + currentUser.setLogin(userDto.getLogin()); + currentUser.setPassword(userDto.getPassword()); + return userRepository.save(currentUser); + } + @Transactional + public User updateUser(Long id,String login, String password) { + if (!StringUtils.hasText(login) || !StringUtils.hasText(password)) { + throw new IllegalArgumentException("User name, login or password is null or empty"); + } + final User currentUser = findUser(id); + currentUser.setLogin(login); + currentUser.setPassword(password); + return userRepository.save(currentUser); + } + @Transactional + public void deleteUser(Long id) { + userRepository.deleteById(id); + } + + @Transactional + public void deleteAllUsers() { + userRepository.deleteAll(); + } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { @@ -57,10 +110,20 @@ public class UserService implements UserDetailsService { 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 = userRepository.findById(id); - return user.orElseThrow(() -> new UserNotFoundException(id)); + public String loginAndGetToken(UserDto userDto) { + final User user = findByLogin(userDto.getLogin()); + if (user == null) { + } + if (!passwordEncoder.matches(userDto.getPassword(), user.getPassword())) { + } + return jwtProvider.generateToken(user.getLogin()); + } + public UserDetails loadUserByToken(String token) throws UsernameNotFoundException { + if (!jwtProvider.isTokenValid(token)) { + throw new JwtException("Bad token"); + } + final String userLogin = jwtProvider.getLoginFromToken(token) + .orElseThrow(() -> new JwtException("Token is not contain Login")); + return loadUserByUsername(userLogin); } } diff --git a/src/main/java/ru/ip/labworks/labworks/configuration/SecurityConfiguration.java b/src/main/java/ru/ip/labworks/labworks/configuration/SecurityConfiguration.java index 1e0c6e6..afc4b19 100644 --- a/src/main/java/ru/ip/labworks/labworks/configuration/SecurityConfiguration.java +++ b/src/main/java/ru/ip/labworks/labworks/configuration/SecurityConfiguration.java @@ -10,10 +10,13 @@ import org.springframework.security.config.annotation.method.configuration.Enabl 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.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; -import ru.ip.labworks.labworks.bookshop.controller.UserSignUpMvcController; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import ru.ip.labworks.labworks.bookshop.controller.UserController; import ru.ip.labworks.labworks.bookshop.model.UserRole; import ru.ip.labworks.labworks.bookshop.service.UserService; +import ru.ip.labworks.labworks.configuration.jwt.JwtFilter; @Configuration @EnableWebSecurity @@ -23,10 +26,15 @@ import ru.ip.labworks.labworks.bookshop.service.UserService; public class SecurityConfiguration { private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); private static final String LOGIN_URL = "/login"; - private final UserService userService; + public static final String SPA_URL_MASK = "/{path:[^\\.]*}"; - public SecurityConfiguration(UserService userService) { + private final UserService userService; + private final JwtFilter jwtFilter; + + public SecurityConfiguration(UserService userService) + { this.userService = userService; + this.jwtFilter = new JwtFilter(userService); createAdminOnStartup(); } @@ -34,35 +42,44 @@ public class SecurityConfiguration { final String admin = "admin"; if (userService.findByLogin(admin) == null) { log.info("Admin user successfully created"); - userService.createUser(admin, admin, admin, UserRole.ADMIN); + userService.addUser(admin, admin, admin, UserRole.ADMIN); } } - @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http.headers().frameOptions().sameOrigin().and() - .cors().and() + http.cors() + .and() .csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() .authorizeHttpRequests() - .requestMatchers(UserSignUpMvcController.SIGNUP_URL).permitAll() - .requestMatchers(HttpMethod.GET, LOGIN_URL).permitAll() - .anyRequest().authenticated() + .requestMatchers("", SPA_URL_MASK).permitAll() + .requestMatchers("/", SPA_URL_MASK).permitAll() + .requestMatchers(HttpMethod.POST, UserController.URL_LOGIN).permitAll() + .requestMatchers(HttpMethod.POST, UserController.URL_SIGN_UP).permitAll() + .requestMatchers(HttpMethod.POST, UserController.URL_WHO_AM_I).permitAll() + .anyRequest() + .authenticated() .and() - .formLogin() - .loginPage(LOGIN_URL).permitAll() - .defaultSuccessUrl("/author", true) - .and() - .logout().permitAll(); + .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) + .anonymous(); return http.userDetailsService(userService).build(); } - @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring() - .requestMatchers("/css/**") - .requestMatchers("/js/**") - .requestMatchers("/templates/**") - .requestMatchers("/webjars/**"); + .requestMatchers(HttpMethod.OPTIONS, "/**") + .requestMatchers("/*.js") + .requestMatchers("/*.html") + .requestMatchers("/*.css") + .requestMatchers("/assets/**") + .requestMatchers("/favicon.ico") + .requestMatchers("/.js", "/.css") + .requestMatchers("/swagger-ui/index.html") + .requestMatchers("/webjars/**") + .requestMatchers("/swagger-resources/**") + .requestMatchers("/v3/api-docs/**") + .requestMatchers("/h2-console/**"); } } \ No newline at end of file diff --git a/src/main/java/ru/ip/labworks/labworks/configuration/WebConfiguration.java b/src/main/java/ru/ip/labworks/labworks/configuration/WebConfiguration.java index 54ba1c6..29a172c 100644 --- a/src/main/java/ru/ip/labworks/labworks/configuration/WebConfiguration.java +++ b/src/main/java/ru/ip/labworks/labworks/configuration/WebConfiguration.java @@ -2,18 +2,10 @@ package ru.ip.labworks.labworks.configuration; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfiguration implements WebMvcConfigurer { - public static final String REST_API = "/api"; - @Override - public void addViewControllers(ViewControllerRegistry registry) { - WebMvcConfigurer.super.addViewControllers(registry); - registry.addViewController("login"); - } - @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedMethods("*"); diff --git a/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtException.java b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtException.java new file mode 100644 index 0000000..0c0a127 --- /dev/null +++ b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtException.java @@ -0,0 +1,11 @@ +package ru.ip.labworks.labworks.configuration.jwt; + +public class JwtException extends RuntimeException { + public JwtException(Throwable throwable) { + super(throwable); + } + + public JwtException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtFilter.java b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtFilter.java new file mode 100644 index 0000000..0aefdf6 --- /dev/null +++ b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtFilter.java @@ -0,0 +1,72 @@ +package ru.ip.labworks.labworks.configuration.jwt; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.http.MediaType; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.GenericFilterBean; +import ru.ip.labworks.labworks.bookshop.service.UserService; + +import java.io.IOException; + +public class JwtFilter extends GenericFilterBean { + private static final String AUTHORIZATION = "Authorization"; + public static final String TOKEN_BEGIN_STR = "Bearer "; + + private final UserService userService; + + public JwtFilter(UserService userService) { + this.userService = userService; + } + + private String getTokenFromRequest(HttpServletRequest request) { + String bearer = request.getHeader(AUTHORIZATION); + if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) { + return bearer.substring(TOKEN_BEGIN_STR.length()); + } + return null; + } + + private void raiseException(ServletResponse response, int status, String message) throws IOException { + if (response instanceof final HttpServletResponse httpResponse) { + httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE); + httpResponse.setStatus(status); + final byte[] body = new ObjectMapper().writeValueAsBytes(message); + response.getOutputStream().write(body); + } + } + + @Override + public void doFilter(ServletRequest request, + ServletResponse response, + FilterChain chain) throws IOException, ServletException { + if (request instanceof final HttpServletRequest httpRequest) { + final String token = getTokenFromRequest(httpRequest); + if (StringUtils.hasText(token)) { + try { + final UserDetails user = userService.loadUserByToken(token); + final UsernamePasswordAuthenticationToken auth = + new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(auth); + } catch (JwtException e) { + raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); + return; + } catch (Exception e) { + e.printStackTrace(); + raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, + String.format("Internal error: %s", e.getMessage())); + return; + } + } + } + chain.doFilter(request, response); + } +} diff --git a/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtProperties.java b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtProperties.java new file mode 100644 index 0000000..dbb4674 --- /dev/null +++ b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtProperties.java @@ -0,0 +1,27 @@ +package ru.ip.labworks.labworks.configuration.jwt; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true) +public class JwtProperties { + private String devToken = ""; + private Boolean isDev = true; + + public String getDevToken() { + return devToken; + } + + public void setDevToken(String devToken) { + this.devToken = devToken; + } + + public Boolean isDev() { + return isDev; + } + + public void setDev(Boolean dev) { + isDev = dev; + } +} diff --git a/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtProvider.java b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtProvider.java new file mode 100644 index 0000000..8b89b5c --- /dev/null +++ b/src/main/java/ru/ip/labworks/labworks/configuration/jwt/JwtProvider.java @@ -0,0 +1,108 @@ +package ru.ip.labworks.labworks.configuration.jwt; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.JWTVerifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; +import java.util.Optional; +import java.util.UUID; + +@Component +public class JwtProvider { + private final static Logger LOG = LoggerFactory.getLogger(JwtProvider.class); + + private final static byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); + private final static String ISSUER = "auth0"; + + private final Algorithm algorithm; + private final JWTVerifier verifier; + + public JwtProvider(JwtProperties jwtProperties) { + if (!jwtProperties.isDev()) { + LOG.info("Generate new JWT key for prod"); + try { + final MessageDigest salt = MessageDigest.getInstance("SHA-256"); + salt.update(UUID.randomUUID().toString().getBytes(StandardCharsets.UTF_8)); + LOG.info("Use generated JWT key for prod \n{}", bytesToHex(salt.digest())); + algorithm = Algorithm.HMAC256(bytesToHex(salt.digest())); + } catch (NoSuchAlgorithmException e) { + throw new JwtException(e); + } + } else { + LOG.info("Use default JWT key for dev \n{}", jwtProperties.getDevToken()); + algorithm = Algorithm.HMAC256(jwtProperties.getDevToken()); + } + verifier = JWT.require(algorithm) + .withIssuer(ISSUER) + .build(); + } + + private static String bytesToHex(byte[] bytes) { + byte[] hexChars = new byte[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars, StandardCharsets.UTF_8); + } + + public String generateToken(String login) { + final Date issueDate = Date.from(LocalDate.now() + .atStartOfDay(ZoneId.systemDefault()) + .toInstant()); + final Date expireDate = Date.from(LocalDate.now() + .plusDays(15) + .atStartOfDay(ZoneId.systemDefault()) + .toInstant()); + var temp = JWT.create(); + var temp2 = temp.withIssuer(ISSUER); + var temp3 = temp2.withIssuedAt(issueDate); + var temp4 = temp3.withExpiresAt(expireDate); + var temp5 = temp4.withSubject(login); + var temp6 = temp5.sign(algorithm); + return temp6; + } + + private DecodedJWT validateToken(String token) { + try { + return verifier.verify(token); + } catch (JWTVerificationException e) { + throw new JwtException(String.format("Token verification error: %s", e.getMessage())); + } + } + + public boolean isTokenValid(String token) { + if (!StringUtils.hasText(token)) { + return false; + } + try { + validateToken(token); + return true; + } catch (JwtException e) { + LOG.error(e.getMessage()); + return false; + } + } + + public Optional getLoginFromToken(String token) { + try { + return Optional.ofNullable(validateToken(token).getSubject()); + } catch (JwtException e) { + LOG.error(e.getMessage()); + return Optional.empty(); + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index da7b0b1..32dc422 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,3 +9,6 @@ spring.jpa.hibernate.ddl-auto=update spring.h2.console.enabled=true spring.h2.console.settings.trace=false spring.h2.console.settings.web-allow-others=false +jwt.dev-token=my-secret-jwt +jwt.dev=true +jwt.secret = my-secret-jwt \ No newline at end of file diff --git a/src/main/resources/frontend/spa-vue/src/components/Header.vue b/src/main/resources/frontend/spa-vue/src/components/Header.vue index 841fd56..f00cb94 100644 --- a/src/main/resources/frontend/spa-vue/src/components/Header.vue +++ b/src/main/resources/frontend/spa-vue/src/components/Header.vue @@ -13,6 +13,7 @@ Authors Books Genres + Users diff --git a/src/main/resources/frontend/spa-vue/src/pages/Authors.vue b/src/main/resources/frontend/spa-vue/src/pages/Authors.vue index 0612495..789b601 100644 --- a/src/main/resources/frontend/spa-vue/src/pages/Authors.vue +++ b/src/main/resources/frontend/spa-vue/src/pages/Authors.vue @@ -26,11 +26,38 @@ export default{ bookid: 0, URL: "http://localhost:8080/", author: new Author(), + postParams: { + method:"POST", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, + putParams: { + method:"PUT", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + }, + }, + delParams: { + method:"DELETE", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, + getParams: { + method:"GET", + headers:{ + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, } }, methods:{ getAuthors(){ - axios.get(this.URL + "author") + axios.get(this.URL + "author", this.getParams) .then(response => { this.authors = response.data; console.log(response.data); @@ -41,8 +68,9 @@ export default{ }, async addAuthor(){ await this.toBase64(); + this.author.login = localStorage.getItem("user"); console.log(this.author); - axios.post(this.URL + "author", this.author) + axios.post(this.URL + "author", this.author, this.postParams) .then((response) => { this.getAuthors(); this.closeModal(); @@ -54,14 +82,14 @@ export default{ }); }, deleteAuthor(id){ - axios.delete(this.URL + `author/${id}`) + axios.delete(this.URL + `author/${id}`, this.delParams) .then(() =>{ this.getAuthors(); }) }, async updateAuthor(author){ if(author.photo === undefined) await this.toBase64(); - axios.put(this.URL + `author/${author.id}`, this.author) + axios.put(this.URL + `author/${author.id}`, this.author, this.putParams) .then(() =>{ this.getAuthors(); }) @@ -106,7 +134,7 @@ export default{ }); }, getAllBooks(){ - axios.get(this.URL + "book") + axios.get(this.URL + "book", this.getParams) .then(response => { this.allBooks = response.data; console.log(response.data); @@ -116,7 +144,7 @@ export default{ }); }, getAuthorBooks(){ - axios.get(this.URL + `author/${this.author.id}/books`) + axios.get(this.URL + `author/${this.author.id}/books`, this.getParams) .then(response => { this.authorBooks = response.data; console.log(response.data); @@ -127,7 +155,7 @@ export default{ }, addBook(){ console.log(this.bookid + " " + this.author.id); - axios.post(this.URL + `author/${this.author.id}/Book/${this.bookid}`) + axios.post(this.URL + `author/${this.author.id}/Book/${this.bookid}`, null, this.postParams) .then(() => { this.getAuthorBooks(); }) @@ -136,13 +164,13 @@ export default{ }); }, removeBook(id){ - axios.delete(this.URL + `author/${this.author.id}/Book/${id}`) + axios.delete(this.URL + `author/${this.author.id}/Book/${id}`, this.delParams) .then(() =>{ this.getAuthorBooks(); }) }, getAuthorsBooks(){ - axios.get(this.URL + "author/books") + axios.get(this.URL + "author/books", this.getParams) .then(response => { console.log(response.data); this.mapAuthorsBooks = response.data; diff --git a/src/main/resources/frontend/spa-vue/src/pages/Books.vue b/src/main/resources/frontend/spa-vue/src/pages/Books.vue index 8ab1fcb..d625c4c 100644 --- a/src/main/resources/frontend/spa-vue/src/pages/Books.vue +++ b/src/main/resources/frontend/spa-vue/src/pages/Books.vue @@ -25,11 +25,38 @@ export default{ genreid: 0, URL: "http://localhost:8080/", book: new Book(), + postParams: { + method:"POST", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, + putParams: { + method:"PUT", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + }, + }, + delParams: { + method:"DELETE", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, + getParams: { + method:"GET", + headers:{ + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, } }, methods:{ getBooks(){ - axios.get(this.URL + "book") + axios.get(this.URL + "book", this.getParams) .then(response => { this.books = response.data; console.log(response.data); @@ -41,7 +68,7 @@ export default{ async addBook(){ await this.toBase64(); console.log(this.book); - axios.post(this.URL + "book", this.book) + axios.post(this.URL + "book", this.book, this.postParams) .then((response) => { this.getBooks(); this.closeModal(); @@ -53,14 +80,14 @@ export default{ }); }, deleteBook(id){ - axios.delete(this.URL + `book/${id}`) + axios.delete(this.URL + `book/${id}`, this.delParams) .then(() =>{ this.getBooks(); }) }, async updateBook(book){ if(book.cover === undefined) await this.toBase64(); - axios.put(this.URL + `book/${book.id}`, this.book) + axios.put(this.URL + `book/${book.id}`, this.book, this.putParams) .then(() =>{ this.getBooks(); }) @@ -98,7 +125,7 @@ export default{ }); }, getAllGenres(){ - axios.get(this.URL + "genre") + axios.get(this.URL + "genre", this.getParams) .then(response => { this.allGenres = response.data; console.log(response.data); @@ -108,7 +135,7 @@ export default{ }); }, getBookGenres(){ - axios.get(this.URL + `book/${this.book.id}/genres`) + axios.get(this.URL + `book/${this.book.id}/genres`, this.getParams) .then(response => { this.bookGenres = response.data; console.log(response.data); @@ -118,7 +145,7 @@ export default{ }); }, addGenre(){ - axios.post(this.URL + `book/${this.book.id}/Genre/${this.genreid}`) + axios.post(this.URL + `book/${this.book.id}/Genre/${this.genreid}`, null, this.postParams) .then(() => { this.getBookGenres(); }) @@ -127,7 +154,7 @@ export default{ }); }, removeGenre(id){ - axios.delete(this.URL + `book/${this.book.id}/Genre/${id}`) + axios.delete(this.URL + `book/${this.book.id}/Genre/${id}`, this.delParams) .then(() =>{ this.getBookGenres(); }) diff --git a/src/main/resources/frontend/spa-vue/src/pages/Error.vue b/src/main/resources/frontend/spa-vue/src/pages/Error.vue new file mode 100644 index 0000000..f6d5da1 --- /dev/null +++ b/src/main/resources/frontend/spa-vue/src/pages/Error.vue @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/src/main/resources/frontend/spa-vue/src/pages/Genres.vue b/src/main/resources/frontend/spa-vue/src/pages/Genres.vue index fa36366..1e39440 100644 --- a/src/main/resources/frontend/spa-vue/src/pages/Genres.vue +++ b/src/main/resources/frontend/spa-vue/src/pages/Genres.vue @@ -22,11 +22,38 @@ export default{ genres: [], URL: "http://localhost:8080/", genre: new Genre(), + postParams: { + method:"POST", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, + putParams: { + method:"PUT", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + }, + }, + delParams: { + method:"DELETE", + headers:{ + "Content-Type":"application/json", + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, + getParams: { + method:"GET", + headers:{ + "Authorization": "Bearer " + localStorage.getItem("token"), + } + }, } }, methods:{ getGenres(){ - axios.get(this.URL + "genre") + axios.get(this.URL + "genre", this.getParams) .then(response => { this.genres = response.data; console.log(response.data); @@ -37,7 +64,7 @@ export default{ }, addGenre(){ console.log(this.genre); - axios.post(this.URL + "genre", this.genre) + axios.post(this.URL + "genre", this.genre, this.postParams) .then(() => { this.getGenres(); this.closeModal(); @@ -47,14 +74,14 @@ export default{ }); }, deleteGenre(id){ - axios.delete(this.URL + `genre/${id}`) + axios.delete(this.URL + `genre/${id}`, this.delParams) .then(() =>{ this.getGenres(); }) }, updateGenre(genre){ this.genre = genre; - axios.put(this.URL + `genre/${genre.id}`, this.genre) + axios.put(this.URL + `genre/${genre.id}`, this.genre, this.putParams) .then(() =>{ this.getGenres(); }) diff --git a/src/main/resources/frontend/spa-vue/src/pages/Index.vue b/src/main/resources/frontend/spa-vue/src/pages/Index.vue deleted file mode 100644 index 7eae3d5..0000000 --- a/src/main/resources/frontend/spa-vue/src/pages/Index.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/main/resources/frontend/spa-vue/src/pages/login.vue b/src/main/resources/frontend/spa-vue/src/pages/login.vue new file mode 100644 index 0000000..c8c850f --- /dev/null +++ b/src/main/resources/frontend/spa-vue/src/pages/login.vue @@ -0,0 +1,83 @@ + + + \ No newline at end of file diff --git a/src/main/resources/frontend/spa-vue/src/pages/registration.vue b/src/main/resources/frontend/spa-vue/src/pages/registration.vue new file mode 100644 index 0000000..ed28f1e --- /dev/null +++ b/src/main/resources/frontend/spa-vue/src/pages/registration.vue @@ -0,0 +1,46 @@ + + \ No newline at end of file diff --git a/src/main/resources/frontend/spa-vue/src/pages/users.vue b/src/main/resources/frontend/spa-vue/src/pages/users.vue new file mode 100644 index 0000000..3543375 --- /dev/null +++ b/src/main/resources/frontend/spa-vue/src/pages/users.vue @@ -0,0 +1,52 @@ + + + \ No newline at end of file diff --git a/src/main/resources/frontend/spa-vue/src/router/router.js b/src/main/resources/frontend/spa-vue/src/router/router.js index 09500d4..88c3ac5 100644 --- a/src/main/resources/frontend/spa-vue/src/router/router.js +++ b/src/main/resources/frontend/spa-vue/src/router/router.js @@ -1,14 +1,20 @@ import {createRouter, createWebHistory} from "vue-router" -import Index from '../pages/Index.vue' import Authors from '../pages/Authors.vue' import Books from '../pages/Books.vue' import Genres from '../pages/Genres.vue' +import users from "../pages/users.vue"; +import login from "../pages/login.vue"; +import registration from "../pages/registration.vue"; +import Error from "../pages/Error.vue"; const routes = [ - {path: '/', component: Index}, - {path: '/authors', component: Authors}, - {path: '/books', component: Books}, - {path: '/genres', component: Genres}, + {path: '/authors', component: Authors, meta: { requiresAuth: true }}, + {path: '/books', component: Books, meta: { requiresAuth: true }}, + {path: '/genres', component: Genres, meta: { requiresAuth: true }}, + {path: "/users", component: users, meta: { requiresAuth: true, requiresAdmin: true }}, + {path: "/login", component: login}, + {path: "/registration", component: registration}, + {path: "/error", component: Error, meta: { requiresAuth: true }}, ] const router = createRouter({ @@ -17,4 +23,22 @@ const router = createRouter({ routes }) +router.beforeEach((to, from, next) => { + const isAuthenticated = localStorage.getItem("token"); + if (to.matched.some((route) => route.meta.requiresAuth)) { + if (!isAuthenticated) { + next("/login"); + return; + } + } + const isAdmin = localStorage.getItem("role") === "ADMIN"; + if (to.matched.some((route) => route.meta.requiresAdmin)) { + if (!isAdmin) { + next("/error"); + return; + } + } + next(); +}); + export default router; \ No newline at end of file diff --git a/src/main/resources/templates/all-authors-books.html b/src/main/resources/templates/all-authors-books.html deleted file mode 100644 index 95e7d24..0000000 --- a/src/main/resources/templates/all-authors-books.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - -
АвторНазвание книг
- -
    -
  • - -
  • -
-
-
- -
- - \ No newline at end of file diff --git a/src/main/resources/templates/author-mtm.html b/src/main/resources/templates/author-mtm.html deleted file mode 100644 index 402a0d2..0000000 --- a/src/main/resources/templates/author-mtm.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - -
НазваниеДата релизаФотоРедактировать запись
- - -
- -
-
- -
-
-
-
-
- - -
-
- -
- - \ No newline at end of file diff --git a/src/main/resources/templates/author-update.html b/src/main/resources/templates/author-update.html deleted file mode 100644 index b073034..0000000 --- a/src/main/resources/templates/author-update.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - -
-
-
-
- - -
-
- - -
-
- - - -
-
- - - Назад - -
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/authors.html b/src/main/resources/templates/authors.html deleted file mode 100644 index dff0e15..0000000 --- a/src/main/resources/templates/authors.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - -
- -
- - - - - - - - - - - - - - - -
ИмяФамилияФотоРедактировать запись
- - -
- - Изменить - - - - Книга - -
-
- -
-
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/book-mtm.html b/src/main/resources/templates/book-mtm.html deleted file mode 100644 index cf2632a..0000000 --- a/src/main/resources/templates/book-mtm.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - -
НазваниеРедактировать запись
- -
- -
-
- -
-
-
-
-
- - -
-
- -
- - \ No newline at end of file diff --git a/src/main/resources/templates/book-update.html b/src/main/resources/templates/book-update.html deleted file mode 100644 index 262b49e..0000000 --- a/src/main/resources/templates/book-update.html +++ /dev/null @@ -1,40 +0,0 @@ - - - - - -
-
-
-
- - -
-
- - -
-
- - - -
-
- - - Назад - -
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/books.html b/src/main/resources/templates/books.html deleted file mode 100644 index d8035ab..0000000 --- a/src/main/resources/templates/books.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - -
- -
- - - - - - - - - - - - - - - -
НазваниеДата релизаФотоРедактировать запись
- - -
- - Изменить - - - - Жанры - -
-
- -
-
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html deleted file mode 100644 index 6e15801..0000000 --- a/src/main/resources/templates/default.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - Онлайн библиотека - - - - - - - -
- -
-
-
-
-
-
-
-
-
End
-
-
-
- - - - \ No newline at end of file diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html deleted file mode 100644 index a2f317f..0000000 --- a/src/main/resources/templates/error.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/main/resources/templates/genre-update.html b/src/main/resources/templates/genre-update.html deleted file mode 100644 index b4a683a..0000000 --- a/src/main/resources/templates/genre-update.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - -
-
-
-
- - -
-
- - - Назад - -
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/genres.html b/src/main/resources/templates/genres.html deleted file mode 100644 index 0dd2593..0000000 --- a/src/main/resources/templates/genres.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - -
- -
- - - - - - - - - - - - -
НазваниеРедактировать запись
- -
- - Изменить - - -
-
- -
-
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html deleted file mode 100644 index e8b17ca..0000000 --- a/src/main/resources/templates/login.html +++ /dev/null @@ -1,42 +0,0 @@ - - - -
-
- User not found -
-
- Logout success -
-
- User '' was successfully created -
-
-
-

Login

- -
-
-

Password

- -
-
- -
-
-

- Not a member yet? - Sign Up here -

-
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/signup.html b/src/main/resources/templates/signup.html deleted file mode 100644 index 9f4e0e6..0000000 --- a/src/main/resources/templates/signup.html +++ /dev/null @@ -1,29 +0,0 @@ - - - -
-
-
-
- -
-
- -
-
- -
-
- - Назад -
-
-
- - \ No newline at end of file diff --git a/src/main/resources/templates/users.html b/src/main/resources/templates/users.html deleted file mode 100644 index 34c6cfa..0000000 --- a/src/main/resources/templates/users.html +++ /dev/null @@ -1,38 +0,0 @@ - - - -
-
- - - - - - - - - - - - - - - - - -
#IDЛогинРоль
-
- -
- - \ No newline at end of file