From c1a2e77e4d332068f2a9e21810b73c917d2ba712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=9F=D0=BE=D0=BB?= =?UTF-8?q?=D0=B5=D0=B2=D0=BE=D0=B9?= Date: Tue, 2 May 2023 01:24:20 +0400 Subject: [PATCH] Lab6: MVC side authentication is done --- build.gradle | 5 +- .../java/np/something/DTO/UserSignupDto.java | 29 ++++++++ .../java/np/something/WebConfiguration.java | 7 ++ .../controllers/CommentController.java | 2 +- .../controllers/CustomerController.java | 2 +- .../something/controllers/PostController.java | 2 +- src/main/java/np/something/model/Comment.java | 4 +- .../java/np/something/model/Customer.java | 21 +++++- src/main/java/np/something/model/Post.java | 4 +- .../java/np/something/model/UserRole.java | 20 ++++++ src/main/java/np/something/mvc/Admin.java | 26 ++++--- src/main/java/np/something/mvc/Comments.java | 14 ++-- src/main/java/np/something/mvc/Customers.java | 17 +++-- src/main/java/np/something/mvc/Feed.java | 12 ++-- src/main/java/np/something/mvc/Posts.java | 12 ++-- src/main/java/np/something/mvc/Session.java | 16 ----- .../java/np/something/mvc/UserSignUp.java | 53 +++++++++++++++ .../repositories/CustomerRepository.java | 1 + .../PasswordEncoderConfiguration.java | 14 ++++ .../security/SecurityConfiguration.java | 67 +++++++++++++++++++ .../np/something/services/CommentService.java | 2 +- .../something/services/CustomerService.java | 44 +++++++++--- .../np/something/services/PostService.java | 8 +-- .../util/validation/ValidatorUtil.java | 8 +-- src/main/resources/application.properties | 2 + src/main/resources/templates/admin.html | 2 +- src/main/resources/templates/customers.html | 14 +--- src/main/resources/templates/default.html | 21 ++---- src/main/resources/templates/feed.html | 12 ++-- src/main/resources/templates/login.html | 31 +++++++++ src/main/resources/templates/signup.html | 44 ++++++++++++ 31 files changed, 409 insertions(+), 107 deletions(-) create mode 100644 src/main/java/np/something/DTO/UserSignupDto.java create mode 100644 src/main/java/np/something/model/UserRole.java delete mode 100644 src/main/java/np/something/mvc/Session.java create mode 100644 src/main/java/np/something/mvc/UserSignUp.java create mode 100644 src/main/java/np/something/security/PasswordEncoderConfiguration.java create mode 100644 src/main/java/np/something/security/SecurityConfiguration.java create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/signup.html diff --git a/build.gradle b/build.gradle index 34eb38a..880d221 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.springframework.boot' version '3.0.2' + id 'org.springframework.boot' version '2.6.3' id 'io.spring.dependency-management' version '1.1.0' } @@ -22,6 +22,9 @@ dependencies { implementation 'org.webjars:bootstrap:5.1.3' implementation 'org.webjars:jquery:3.6.0' implementation 'org.webjars:font-awesome:6.1.0' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' + implementation 'com.auth0:java-jwt:4.4.0' implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'org.hibernate.validator:hibernate-validator' diff --git a/src/main/java/np/something/DTO/UserSignupDto.java b/src/main/java/np/something/DTO/UserSignupDto.java new file mode 100644 index 0000000..ee81ace --- /dev/null +++ b/src/main/java/np/something/DTO/UserSignupDto.java @@ -0,0 +1,29 @@ +package np.something.DTO; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +public class UserSignupDto { + @NotBlank + @Size(min = 3, max = 64) + private String username; + @NotBlank + @Size(min = 6, max = 64) + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} \ No newline at end of file diff --git a/src/main/java/np/something/WebConfiguration.java b/src/main/java/np/something/WebConfiguration.java index b5e8c55..3a1e562 100644 --- a/src/main/java/np/something/WebConfiguration.java +++ b/src/main/java/np/something/WebConfiguration.java @@ -2,12 +2,19 @@ package np.something; 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/np/something/controllers/CommentController.java b/src/main/java/np/something/controllers/CommentController.java index b88054d..4064fae 100644 --- a/src/main/java/np/something/controllers/CommentController.java +++ b/src/main/java/np/something/controllers/CommentController.java @@ -1,6 +1,6 @@ package np.something.controllers; -import jakarta.validation.Valid; +import javax.validation.Valid; import np.something.DTO.CommentDto; import np.something.WebConfiguration; import np.something.model.Comment; diff --git a/src/main/java/np/something/controllers/CustomerController.java b/src/main/java/np/something/controllers/CustomerController.java index b693df5..29e8bba 100644 --- a/src/main/java/np/something/controllers/CustomerController.java +++ b/src/main/java/np/something/controllers/CustomerController.java @@ -1,6 +1,6 @@ package np.something.controllers; -import jakarta.validation.Valid; +import javax.validation.Valid; import np.something.DTO.CustomerDto; import np.something.WebConfiguration; import np.something.model.Customer; diff --git a/src/main/java/np/something/controllers/PostController.java b/src/main/java/np/something/controllers/PostController.java index 6d3977e..f69aece 100644 --- a/src/main/java/np/something/controllers/PostController.java +++ b/src/main/java/np/something/controllers/PostController.java @@ -1,6 +1,6 @@ package np.something.controllers; -import jakarta.validation.Valid; +import javax.validation.Valid; import np.something.DTO.PostDto; import np.something.WebConfiguration; import np.something.services.CustomerService; diff --git a/src/main/java/np/something/model/Comment.java b/src/main/java/np/something/model/Comment.java index 650e37e..54059cf 100644 --- a/src/main/java/np/something/model/Comment.java +++ b/src/main/java/np/something/model/Comment.java @@ -1,7 +1,7 @@ package np.something.model; -import jakarta.persistence.*; -import jakarta.validation.constraints.NotBlank; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; import java.time.LocalDateTime; import java.util.Objects; diff --git a/src/main/java/np/something/model/Customer.java b/src/main/java/np/something/model/Customer.java index 8e998dc..cbfc795 100644 --- a/src/main/java/np/something/model/Customer.java +++ b/src/main/java/np/something/model/Customer.java @@ -1,7 +1,9 @@ package np.something.model; -import jakarta.persistence.*; -import jakarta.validation.constraints.NotBlank; +import org.h2.engine.User; + +import javax.persistence.*; +import javax.validation.constraints.NotBlank; import java.util.*; @@ -21,6 +23,8 @@ public class Customer { @OneToMany(fetch = FetchType.EAGER, mappedBy = "customer", cascade = CascadeType.ALL) private List posts; + private UserRole role; + public Customer() { } @@ -28,6 +32,15 @@ public class Customer { public Customer(String username, String password) { this.username = username; this.password = password; + this.role = UserRole.USER; + this.comments = new ArrayList<>(); + this.posts = new ArrayList<>(); + } + + public Customer(String username, String password, UserRole role) { + this.username = username; + this.password = password; + this.role = role; this.comments = new ArrayList<>(); this.posts = new ArrayList<>(); } @@ -60,6 +73,10 @@ public class Customer { this.password = password; } + public UserRole getRole() { + return role; + } + @Override public boolean equals(Object obj) { if (this == obj) return true; diff --git a/src/main/java/np/something/model/Post.java b/src/main/java/np/something/model/Post.java index ccf6d86..26ceafa 100644 --- a/src/main/java/np/something/model/Post.java +++ b/src/main/java/np/something/model/Post.java @@ -1,7 +1,7 @@ package np.something.model; -import jakarta.persistence.*; -import jakarta.validation.constraints.NotBlank; +import javax.persistence.*; +import javax.validation.constraints.NotBlank; import java.time.LocalDateTime; import java.util.ArrayList; diff --git a/src/main/java/np/something/model/UserRole.java b/src/main/java/np/something/model/UserRole.java new file mode 100644 index 0000000..22e92ad --- /dev/null +++ b/src/main/java/np/something/model/UserRole.java @@ -0,0 +1,20 @@ +package np.something.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"; + } +} diff --git a/src/main/java/np/something/mvc/Admin.java b/src/main/java/np/something/mvc/Admin.java index c51dcd8..5cb1e27 100644 --- a/src/main/java/np/something/mvc/Admin.java +++ b/src/main/java/np/something/mvc/Admin.java @@ -1,10 +1,14 @@ package np.something.mvc; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; -import jakarta.validation.Valid; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; import np.something.DTO.CustomerDto; +import np.something.model.UserRole; import np.something.services.CustomerService; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -20,9 +24,12 @@ public class Admin { } @GetMapping(value = { "/", "/{id}" }) - public String getCustomers(@PathVariable(required = false) Long id, HttpServletRequest request, HttpSession session, Model model) { + @Secured({UserRole.AsString.ADMIN}) + public String getCustomers(@PathVariable(required = false) Long id, HttpServletRequest request, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); if (id == null || id <= 0) { model.addAttribute("customers", customerService.findAllCustomers().stream().map(CustomerDto::new).toList()); return "admin"; @@ -32,19 +39,20 @@ public class Admin { } @PostMapping("/delete/{id}") - public String deleteCustomer(@PathVariable Long id, HttpSession session) { - session.setAttribute("currentCustomerId", -1); + public String deleteCustomer(@PathVariable Long id) { customerService.deleteCustomer(id); return "redirect:/admin/"; } @PostMapping(value = { "/", "/{id}"}) public String manipulateCustomer(@PathVariable(required = false) Long id, @ModelAttribute @Valid CustomerDto customerDto, - HttpServletRequest request, HttpSession session, + HttpServletRequest request, BindingResult bindingResult, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); if (bindingResult.hasErrors()) { model.addAttribute("errors", bindingResult.getAllErrors()); return "/admin"; diff --git a/src/main/java/np/something/mvc/Comments.java b/src/main/java/np/something/mvc/Comments.java index a53c4cf..a535be2 100644 --- a/src/main/java/np/something/mvc/Comments.java +++ b/src/main/java/np/something/mvc/Comments.java @@ -1,13 +1,15 @@ package np.something.mvc; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; -import jakarta.validation.Valid; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; import np.something.DTO.CommentDto; import np.something.DTO.PostDto; import np.something.services.CommentService; import np.something.services.CustomerService; import np.something.services.PostService; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -28,9 +30,11 @@ public class Comments { @PostMapping(value = { "/", "/{id}"}) public String manipulateComment(@PathVariable(required = false) Long id, @ModelAttribute @Valid CommentDto commentDto, - HttpServletRequest request, HttpSession session, BindingResult bindingResult, Model model) { + HttpServletRequest request, BindingResult bindingResult, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList()); if (bindingResult.hasErrors()) { diff --git a/src/main/java/np/something/mvc/Customers.java b/src/main/java/np/something/mvc/Customers.java index e06acf8..ef6af64 100644 --- a/src/main/java/np/something/mvc/Customers.java +++ b/src/main/java/np/something/mvc/Customers.java @@ -1,10 +1,12 @@ package np.something.mvc; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; -import jakarta.validation.Valid; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; import np.something.DTO.CustomerDto; import np.something.services.CustomerService; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -22,7 +24,9 @@ public class Customers { @GetMapping(value = { "/", "/{id}" }) public String getCustomers(@PathVariable(required = false) Long id, HttpServletRequest request, HttpSession session, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); if (id == null || id <= 0) { model.addAttribute("customers", customerService.findAllCustomers().stream().map(CustomerDto::new).toList()); } else { @@ -34,7 +38,6 @@ public class Customers { @PostMapping("/delete/{id}") public String deleteCustomer(@PathVariable Long id, HttpSession session) { - session.setAttribute("currentCustomerId", -1); customerService.deleteCustomer(id); return "redirect:/customers/"; } @@ -45,7 +48,9 @@ public class Customers { BindingResult bindingResult, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); if (bindingResult.hasErrors()) { model.addAttribute("errors", bindingResult.getAllErrors()); return "/customers"; diff --git a/src/main/java/np/something/mvc/Feed.java b/src/main/java/np/something/mvc/Feed.java index 48988f8..651b3b8 100644 --- a/src/main/java/np/something/mvc/Feed.java +++ b/src/main/java/np/something/mvc/Feed.java @@ -1,7 +1,7 @@ package np.something.mvc; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import np.something.DTO.CommentDto; import np.something.DTO.CustomerDto; import np.something.DTO.PostDto; @@ -9,6 +9,8 @@ import np.something.model.Post; import np.something.services.CommentService; import np.something.services.CustomerService; import np.something.services.PostService; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.validation.BindingResult; @@ -21,7 +23,7 @@ import java.util.List; import java.util.Objects; @Controller -@RequestMapping("/feed") +@RequestMapping(value = { "", "/feed" }) public class Feed { private final PostService postService; private final CustomerService customerService; @@ -37,7 +39,9 @@ public class Feed { @GetMapping public String getPosts(@RequestParam(required = false) String search, HttpServletRequest request, HttpSession session, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); if (search == null) { model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList()); } else { diff --git a/src/main/java/np/something/mvc/Posts.java b/src/main/java/np/something/mvc/Posts.java index 2d9892c..0cab3ea 100644 --- a/src/main/java/np/something/mvc/Posts.java +++ b/src/main/java/np/something/mvc/Posts.java @@ -1,15 +1,17 @@ package np.something.mvc; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import np.something.services.CommentService; import np.something.services.CustomerService; import np.something.services.PostService; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import org.springframework.validation.BindingResult; -import jakarta.validation.Valid; +import javax.validation.Valid; import np.something.DTO.PostDto; @Controller @@ -37,7 +39,9 @@ public class Posts { BindingResult bindingResult, Model model) { model.addAttribute("request", request); - model.addAttribute("session", session); + model.addAttribute("currentCustomerId", customerService.findByUsername( + ((UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUsername() + ).getId()); model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList()); if (bindingResult.hasErrors()) { diff --git a/src/main/java/np/something/mvc/Session.java b/src/main/java/np/something/mvc/Session.java deleted file mode 100644 index bd7fd14..0000000 --- a/src/main/java/np/something/mvc/Session.java +++ /dev/null @@ -1,16 +0,0 @@ -package np.something.mvc; - -import jakarta.servlet.http.HttpSession; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; - -@Controller -public class Session { - @PostMapping("/update-session") - public ResponseEntity updateSession(@RequestParam("currentCustomerId") int currentCustomerId, HttpSession session) { - session.setAttribute("currentCustomerId", currentCustomerId); - return ResponseEntity.ok().build(); - } -} \ No newline at end of file diff --git a/src/main/java/np/something/mvc/UserSignUp.java b/src/main/java/np/something/mvc/UserSignUp.java new file mode 100644 index 0000000..b573a60 --- /dev/null +++ b/src/main/java/np/something/mvc/UserSignUp.java @@ -0,0 +1,53 @@ +package np.something.mvc; + +import np.something.DTO.UserSignupDto; +import np.something.model.Customer; +import np.something.services.CustomerService; +import np.something.util.validation.ValidationException; +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 javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; + +@Controller +@RequestMapping(UserSignUp.SIGNUP_URL) +public class UserSignUp { + public static final String SIGNUP_URL = "/signup"; + + private final CustomerService customerService; + + public UserSignUp(CustomerService customerService) { + this.customerService = customerService; + } + + @GetMapping + public String showSignupForm(Model model) { + model.addAttribute("userDto", new UserSignupDto()); + return "signup"; + } + + @PostMapping + public String signup(@ModelAttribute("userDto") @Valid UserSignupDto userSignupDto, + BindingResult bindingResult, HttpServletRequest request, + Model model) { + model.addAttribute("request", request); + if (bindingResult.hasErrors()) { + model.addAttribute("errors", bindingResult.getAllErrors()); + return "signup"; + } + try { + final Customer customer = customerService.addCustomer( + userSignupDto.getUsername(), userSignupDto.getPassword()); + return "redirect:/login?created=" + customer.getUsername(); + } catch (ValidationException e) { + model.addAttribute("errors", e.getMessage()); + return "signup"; + } + } +} diff --git a/src/main/java/np/something/repositories/CustomerRepository.java b/src/main/java/np/something/repositories/CustomerRepository.java index 2cfe495..c339549 100644 --- a/src/main/java/np/something/repositories/CustomerRepository.java +++ b/src/main/java/np/something/repositories/CustomerRepository.java @@ -4,4 +4,5 @@ import np.something.model.Customer; import org.springframework.data.jpa.repository.JpaRepository; public interface CustomerRepository extends JpaRepository { + Customer findByUsername(String username); } diff --git a/src/main/java/np/something/security/PasswordEncoderConfiguration.java b/src/main/java/np/something/security/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..323dc0a --- /dev/null +++ b/src/main/java/np/something/security/PasswordEncoderConfiguration.java @@ -0,0 +1,14 @@ +package np.something.security; + +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(); + } +} diff --git a/src/main/java/np/something/security/SecurityConfiguration.java b/src/main/java/np/something/security/SecurityConfiguration.java new file mode 100644 index 0000000..ca63111 --- /dev/null +++ b/src/main/java/np/something/security/SecurityConfiguration.java @@ -0,0 +1,67 @@ +package np.something.security; + +import np.something.model.UserRole; +import np.something.mvc.UserSignUp; +import np.something.services.CustomerService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(securedEnabled = true) +public class SecurityConfiguration extends WebSecurityConfigurerAdapter { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + private static final String LOGIN_URL = "/login"; + private final CustomerService customerService; + + public SecurityConfiguration(CustomerService customerService) { + this.customerService = customerService; + createAdminOnStartup(); + } + + private void createAdminOnStartup() { + final String admin = "admin"; + if (customerService.findByUsername(admin) == null) { + log.info("Admin user successfully created"); + customerService.addCustomer(admin, admin, UserRole.ADMIN); + } + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.headers().frameOptions().sameOrigin().and() + .cors().and() + .csrf().disable() + .authorizeRequests() + .antMatchers(UserSignUp.SIGNUP_URL).permitAll() + .antMatchers(HttpMethod.GET, LOGIN_URL).permitAll() + .anyRequest().authenticated() + .and() + .formLogin() + .loginPage(LOGIN_URL).permitAll() + .and() + .logout().permitAll(); + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.userDetailsService(customerService); + } + + @Override + public void configure(WebSecurity web) { + web.ignoring() + .antMatchers("/css/**") + .antMatchers("/js/**") + .antMatchers("/templates/**") + .antMatchers("/webjars/**"); + } +} \ No newline at end of file diff --git a/src/main/java/np/something/services/CommentService.java b/src/main/java/np/something/services/CommentService.java index bff7e44..d363090 100644 --- a/src/main/java/np/something/services/CommentService.java +++ b/src/main/java/np/something/services/CommentService.java @@ -67,7 +67,7 @@ public class CommentService { commentRepository.deleteAll(); } - @jakarta.transaction.Transactional + @Transactional public List searchComments(String tag) { return commentRepository.searchComments(tag); } diff --git a/src/main/java/np/something/services/CustomerService.java b/src/main/java/np/something/services/CustomerService.java index d3fa8c1..3027252 100644 --- a/src/main/java/np/something/services/CustomerService.java +++ b/src/main/java/np/something/services/CustomerService.java @@ -1,28 +1,31 @@ package np.something.services; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityNotFoundException; -import jakarta.persistence.PersistenceContext; -import jakarta.transaction.Transactional; +import javax.transaction.Transactional; import np.something.Exceptions.CustomerNotFoundException; import np.something.model.Customer; -import np.something.repositories.CommentRepository; +import np.something.model.UserRole; import np.something.repositories.CustomerRepository; import np.something.util.validation.ValidatorUtil; +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.util.StringUtils; +import java.util.Collections; import java.util.List; @Service -public class CustomerService { +public class CustomerService implements UserDetailsService { private final CustomerRepository customerRepository; private final ValidatorUtil validatorUtil; + private final PasswordEncoder passwordEncoder; public CustomerService(CustomerRepository customerRepository, - ValidatorUtil validatorUtil) { + ValidatorUtil validatorUtil, PasswordEncoder passwordEncoder) { this.customerRepository = customerRepository; this.validatorUtil = validatorUtil; + this.passwordEncoder = passwordEncoder; } @Transactional @@ -37,7 +40,14 @@ public class CustomerService { @Transactional public Customer addCustomer(String username, String password) { - Customer customer = new Customer(username, password); + Customer customer = new Customer(username, passwordEncoder.encode(password)); + validatorUtil.validate(customer); + return customerRepository.save(customer); + } + + @Transactional + public Customer addCustomer(String username, String password, UserRole role) { + Customer customer = new Customer(username, passwordEncoder.encode(password), role); validatorUtil.validate(customer); return customerRepository.save(customer); } @@ -46,7 +56,7 @@ public class CustomerService { public Customer updateCustomer(Long id, String username, String password) { Customer customer = findCustomer(id); customer.setUsername(username); - customer.setPassword(password); + customer.setPassword(passwordEncoder.encode(password)); validatorUtil.validate(customer); return customerRepository.save(customer); } @@ -62,4 +72,18 @@ public class CustomerService { public void deleteAllCustomers() { customerRepository.deleteAll(); } + + public Customer findByUsername(String username) { + return customerRepository.findByUsername(username); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + final Customer customerEntity = findByUsername(username); + if (customerEntity == null) { + throw new UsernameNotFoundException(username); + } + return new org.springframework.security.core.userdetails.User( + customerEntity.getUsername(), customerEntity.getPassword(), Collections.singleton(customerEntity.getRole())); + } } diff --git a/src/main/java/np/something/services/PostService.java b/src/main/java/np/something/services/PostService.java index d9e9d04..135d200 100644 --- a/src/main/java/np/something/services/PostService.java +++ b/src/main/java/np/something/services/PostService.java @@ -1,9 +1,9 @@ package np.something.services; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityNotFoundException; -import jakarta.persistence.PersistenceContext; -import jakarta.transaction.Transactional; +import javax.persistence.EntityManager; +import javax.persistence.EntityNotFoundException; +import javax.persistence.PersistenceContext; +import javax.transaction.Transactional; import np.something.Exceptions.PostNotFoundException; import np.something.model.Comment; import np.something.model.Customer; diff --git a/src/main/java/np/something/util/validation/ValidatorUtil.java b/src/main/java/np/something/util/validation/ValidatorUtil.java index 1a880a0..323cd98 100644 --- a/src/main/java/np/something/util/validation/ValidatorUtil.java +++ b/src/main/java/np/something/util/validation/ValidatorUtil.java @@ -2,10 +2,10 @@ package np.something.util.validation; import org.springframework.stereotype.Component; -import jakarta.validation.ConstraintViolation; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; import java.util.Set; import java.util.stream.Collectors; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index da7b0b1..055f74b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -9,3 +9,5 @@ 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 diff --git a/src/main/resources/templates/admin.html b/src/main/resources/templates/admin.html index 5bc6896..433b574 100644 --- a/src/main/resources/templates/admin.html +++ b/src/main/resources/templates/admin.html @@ -35,7 +35,7 @@
-
+
diff --git a/src/main/resources/templates/customers.html b/src/main/resources/templates/customers.html index 5c06ce9..cac7b24 100644 --- a/src/main/resources/templates/customers.html +++ b/src/main/resources/templates/customers.html @@ -9,15 +9,6 @@

Профили

-
-
-
- -
-
-

Список профилей

@@ -25,8 +16,7 @@
-
-
+

Комментарии:

@@ -47,7 +37,7 @@

Нет постов

-
+
diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 663977e..cab96f9 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -1,5 +1,5 @@ - + @@ -12,16 +12,15 @@
-