diff --git a/frontend/public/index.html b/frontend/public/index.html index 94366f6..00cc197 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -6,6 +6,7 @@ + Социальная сеть diff --git a/frontend/src/App.vue b/frontend/src/App.vue index dc124e5..0632484 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -6,8 +6,8 @@ Администрирование @@ -24,7 +24,23 @@ export default { methods: { logout() { this.token = null + this.role = '' + localStorage.clear() this.$router.push('login') + }, + async actualRole() { + let response = await fetch( + "http://localhost:8080/api/1.0/customer/role/" + localStorage.getItem("token"), + { + method: "GET", + headers: { + "Authorization": "Bearer " + localStorage.getItem("token") + } + } + ) + + this.role = await response.text() + localStorage.setItem("role", this.role) } }, computed: { @@ -35,6 +51,7 @@ export default { set: function(value) { this.token_value = value localStorage.setItem("token", value) + localStorage.setItem("role", this.role) } } }, @@ -43,22 +60,13 @@ export default { return null; } - let response = await fetch( - "http://localhost:8080/api/1.0/customer/role/" + localStorage.getItem("token"), - { - method: "POST", - headers: { - "Authorization": "Bearer " + localStorage.getItem("token") - } - } - ) - - this.role = await response.json() + await this.actualRole() const component = this - document.addEventListener('token_changed', function() { + document.addEventListener('token_changed', async function() { component.token = localStorage.getItem("token") + await component.actualRole() }) } } diff --git a/frontend/src/components/Admin.vue b/frontend/src/components/Admin.vue new file mode 100644 index 0000000..4cbdc23 --- /dev/null +++ b/frontend/src/components/Admin.vue @@ -0,0 +1,223 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Customers.vue b/frontend/src/components/Customers.vue index b10ce11..92dc0a3 100644 --- a/frontend/src/components/Customers.vue +++ b/frontend/src/components/Customers.vue @@ -33,7 +33,7 @@

Нет постов

- +
@@ -57,7 +57,7 @@ @@ -74,17 +74,30 @@ export default { }, methods: { async updateCustomers() { - const response = await fetch( - "http://localhost:8080/api/1.0/customer", - { - method: "GET", - headers: { - "Authorization": "Bearer " + localStorage.getItem("token") + if (!this.$route.params.id) { + const response = await fetch( + "http://localhost:8080/api/1.0/customer", + { + method: "GET", + headers: { + "Authorization": "Bearer " + localStorage.getItem("token") + } } - } - ) - - this.customers = await response.json() + ) + this.customers = await response.json() + } + else { + const response = await fetch( + "http://localhost:8080/api/1.0/customer/" + this.$route.params.id, + { + method: "GET", + headers: { + "Authorization": "Bearer " + localStorage.getItem("token") + } + } + ) + this.customers = [await response.json()] + } }, async getCurrentCustomer() { const response = await fetch( @@ -120,23 +133,26 @@ export default { await fetch( "http://localhost:8080/api/1.0/customer/" + this.currentCustomerId, { - method: "GET", + method: "PUT", headers: { + 'Content-Type': 'application/json', "Authorization": "Bearer " + localStorage.getItem("token") }, body: JSON.stringify({ - username: $("usernameTextE").val(), - password: $("passwordTextE").val() + "username": $("#usernameTextE").val(), + "password": $("#passwordTextE").val() }) } ) - await this.updateCustomers() + localStorage.clear() + + this.$router.replace("login") } }, async beforeMount() { - if (localStorage.getItem("token") === null) { - this.$router.replace("login") + if (localStorage.getItem("token") == null) { + this.$router.push("login") } await Promise.all([this.updateCustomers(), this.getCurrentCustomer()]) diff --git a/frontend/src/components/Feed.vue b/frontend/src/components/Feed.vue new file mode 100644 index 0000000..78a5360 --- /dev/null +++ b/frontend/src/components/Feed.vue @@ -0,0 +1,338 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/Login.vue b/frontend/src/components/Login.vue index bd68adc..0f3fd14 100644 --- a/frontend/src/components/Login.vue +++ b/frontend/src/components/Login.vue @@ -17,7 +17,7 @@
- Регистрация + Регистрация
@@ -38,13 +38,16 @@ { method: "POST", body: JSON.stringify({ - username: $("#username").val(), - password: $("#password").val() - }) + "username": $("#username").val(), + "password": $("#password").val() + }), + headers: { + 'Content-Type': 'application/json' + } } ) - if (response.statusCode !== 200) { + if (response.status !== 200) { this.error = await response.text() } else { localStorage.setItem("token", await response.text()) diff --git a/frontend/src/components/Posts.vue b/frontend/src/components/Posts.vue deleted file mode 100644 index 5e10131..0000000 --- a/frontend/src/components/Posts.vue +++ /dev/null @@ -1,247 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/src/components/Signup.vue b/frontend/src/components/Signup.vue index 25b1a74..6c4ec6a 100644 --- a/frontend/src/components/Signup.vue +++ b/frontend/src/components/Signup.vue @@ -33,7 +33,7 @@ export default { }, methods: { check() { - if ($("#password").val() !== $("#confirm-password").val()) { + if ($("#password").val() === $("#confirm-password").val()) { $("#enter").removeAttr("disabled") } else { $("#enter").attr("disabled", "disabled") @@ -45,13 +45,16 @@ export default { { method: "POST", body: JSON.stringify({ - username: $("#username").val(), - password: $("#password").val() - }) + "username": $("#username").val(), + "password": $("#password").val() + }), + headers: { + 'Content-Type': 'application/json' + } } ) - if (response.statusCode !== 200) { + if (response.status !== 200) { this.error = await response.text() } else { this.$router.push("login") diff --git a/frontend/src/main.js b/frontend/src/main.js index 1dad12d..49a1f54 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -5,6 +5,8 @@ import { createRouter, createWebHistory } from "vue-router" import Customers from './components/Customers' import Feed from './components/Feed' import Login from "@/components/Login.vue"; +import Signup from "@/components/Signup.vue"; +import Admin from "@/components/Admin.vue"; const routes = [ { @@ -21,6 +23,16 @@ const routes = [ path: '/login', name: "Login", component: Login + }, + { + path: '/signup', + name: "Signup", + component: Signup + }, + { + path: '/admin', + name: "Admin", + component: Admin } ] diff --git a/src/main/java/np/something/controllers/CustomerController.java b/src/main/java/np/something/controllers/CustomerController.java index 7d75805..fc34436 100644 --- a/src/main/java/np/something/controllers/CustomerController.java +++ b/src/main/java/np/something/controllers/CustomerController.java @@ -65,7 +65,7 @@ public class CustomerController { @GetMapping ("/me") CustomerDto getCurrentCustomer(@RequestHeader(HttpHeaders.AUTHORIZATION) String token) { - return new CustomerDto(customerService.findByUsername(customerService.loadUserByToken(token).getUsername())); + return new CustomerDto(customerService.findByUsername(customerService.loadUserByToken(token.substring(7)).getUsername())); } @GetMapping("role/{token}") diff --git a/src/main/java/np/something/controllers/PostController.java b/src/main/java/np/something/controllers/PostController.java index f69aece..3158edc 100644 --- a/src/main/java/np/something/controllers/PostController.java +++ b/src/main/java/np/something/controllers/PostController.java @@ -52,4 +52,15 @@ public class PostController { public void deleteAllPosts() { postService.deleteAllPosts(); } + + @GetMapping("/search") + public List searchPosts(@RequestParam(required = false) String query) { + if (query == null || query.isBlank()) { + return postService.findAllPosts().stream() + .map(PostDto::new) + .toList(); + } else { + return postService.searchPosts(query); + } + } } \ No newline at end of file diff --git a/src/main/java/np/something/mvc/Admin.java b/src/main/java/np/something/mvc/Admin.java index 5cb1e27..e28ecf7 100644 --- a/src/main/java/np/something/mvc/Admin.java +++ b/src/main/java/np/something/mvc/Admin.java @@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.*; @Controller @RequestMapping("/admin") +@Secured({UserRole.AsString.ADMIN}) public class Admin { private final CustomerService customerService; diff --git a/src/main/java/np/something/mvc/Feed.java b/src/main/java/np/something/mvc/Feed.java index 651b3b8..ee70548 100644 --- a/src/main/java/np/something/mvc/Feed.java +++ b/src/main/java/np/something/mvc/Feed.java @@ -45,28 +45,7 @@ public class Feed { if (search == null) { model.addAttribute("posts", postService.findAllPosts().stream().map(PostDto::new).toList()); } else { - var posts = new ArrayList<>(postService.searchPosts(search)); - var comments = commentService.searchComments(search); - for (var post: posts) { - post.getComments().clear(); - } - for (var comment: comments) { - boolean found = false; - for (var post: posts) { - if (Objects.equals(comment.getPost().getId(), post.getId())) { - post.getComments().add(comment); - found = true; - break; - } - } - if (!found) { - var newPost = comment.getPost(); - newPost.getComments().clear(); - newPost.getComments().add(comment); - posts.add(newPost); - } - } - model.addAttribute("posts", posts.stream().map(PostDto::new).toList()); + model.addAttribute("posts", postService.searchPosts(search)); } model.addAttribute("customers", customerService.findAllCustomers().stream().map(CustomerDto::new).toList()); diff --git a/src/main/java/np/something/security/SecurityConfiguration.java b/src/main/java/np/something/security/SecurityConfiguration.java index 978bef6..8ecd2dc 100644 --- a/src/main/java/np/something/security/SecurityConfiguration.java +++ b/src/main/java/np/something/security/SecurityConfiguration.java @@ -7,6 +7,7 @@ import np.something.mvc.UserSignUp; import np.something.services.CustomerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -16,7 +17,15 @@ 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; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; +import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import java.util.LinkedHashMap; @Configuration @EnableWebSecurity @@ -42,40 +51,42 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { } } -// @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(HttpSecurity http) throws Exception { - log.info("Creating security configuration"); - http.cors() - .and() + http.exceptionHandling().authenticationEntryPoint(delegatingEntryPoint()); + http.headers().frameOptions().sameOrigin().and() + .cors().and() .csrf().disable() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() .authorizeRequests() - .antMatchers("/", SPA_URL_MASK).permitAll() - .antMatchers(HttpMethod.POST, WebConfiguration.REST_API + "/customer" + CustomerController.URL_LOGIN).permitAll() - .anyRequest() - .authenticated() + .antMatchers(UserSignUp.SIGNUP_URL).permitAll() + .antMatchers(HttpMethod.GET, LOGIN_URL).permitAll() + .anyRequest().authenticated() .and() - .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) - .anonymous(); + .formLogin() + .loginPage(LOGIN_URL).permitAll() + .and() + .logout().permitAll(); } +// @Override +// protected void configure(HttpSecurity http) throws Exception { +// log.info("Creating security configuration"); +// http.cors() +// .and() +// .csrf().disable() +// .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) +// .and() +// .authorizeRequests() +// .antMatchers("/", SPA_URL_MASK).permitAll() +// .antMatchers(HttpMethod.POST, WebConfiguration.REST_API + "/customer" + CustomerController.URL_LOGIN).permitAll() +// .antMatchers(HttpMethod.POST, WebConfiguration.REST_API + "/customer").permitAll() +// .anyRequest() +// .authenticated() +// .and() +// .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) +// .anonymous(); +// } + @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customerService); @@ -91,4 +102,16 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { .antMatchers("/swagger-resources/**") .antMatchers("/v3/api-docs/**"); } + + @Bean + public AuthenticationEntryPoint delegatingEntryPoint() { + final LinkedHashMap map = new LinkedHashMap(); + map.put(new AntPathRequestMatcher("/"), new LoginUrlAuthenticationEntryPoint("/login")); + map.put(new AntPathRequestMatcher("/api/1.0/**"), new Http403ForbiddenEntryPoint()); + + final DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(map); + entryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")); + + return entryPoint; + } } \ No newline at end of file diff --git a/src/main/java/np/something/services/PostService.java b/src/main/java/np/something/services/PostService.java index 135d200..ae824c7 100644 --- a/src/main/java/np/something/services/PostService.java +++ b/src/main/java/np/something/services/PostService.java @@ -1,30 +1,31 @@ package np.something.services; -import javax.persistence.EntityManager; -import javax.persistence.EntityNotFoundException; -import javax.persistence.PersistenceContext; import javax.transaction.Transactional; + +import np.something.DTO.PostDto; import np.something.Exceptions.PostNotFoundException; -import np.something.model.Comment; import np.something.model.Customer; import np.something.model.Post; -import np.something.repositories.CustomerRepository; +import np.something.repositories.CommentRepository; import np.something.repositories.PostRepository; import np.something.util.validation.ValidatorUtil; import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; @Service public class PostService { private final PostRepository postRepository; + private final CommentRepository commentRepository; private final ValidatorUtil validatorUtil; public PostService(PostRepository postRepository, - ValidatorUtil validatorUtil) { + CommentRepository commentRepository, ValidatorUtil validatorUtil) { this.postRepository = postRepository; + this.commentRepository = commentRepository; this.validatorUtil = validatorUtil; } @@ -70,7 +71,28 @@ public class PostService { } @Transactional - public List searchPosts(String tag) { - return postRepository.searchPosts(tag); + public List searchPosts(String search) { + var posts = new ArrayList<>(postRepository.searchPosts(search)); + var comments = commentRepository.searchComments(search); + for (var post: posts) { + post.getComments().clear(); + } + for (var comment: comments) { + boolean found = false; + for (var post: posts) { + if (Objects.equals(comment.getPost().getId(), post.getId())) { + post.getComments().add(comment); + found = true; + break; + } + } + if (!found) { + var newPost = comment.getPost(); + newPost.getComments().clear(); + newPost.getComments().add(comment); + posts.add(newPost); + } + } + return posts.stream().map(PostDto::new).toList(); } } diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html new file mode 100644 index 0000000..e961ce1 --- /dev/null +++ b/src/main/resources/templates/error.html @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/feed.html b/src/main/resources/templates/feed.html index 596f4c4..826f1b1 100644 --- a/src/main/resources/templates/feed.html +++ b/src/main/resources/templates/feed.html @@ -76,7 +76,7 @@

Пусто

- +