From 8e42ac159d519b42d36e06d28a90bcef88df3b24 Mon Sep 17 00:00:00 2001 From: Inohara Date: Mon, 15 May 2023 02:06:02 +0400 Subject: [PATCH] =?UTF-8?q?=D0=B2=D1=80=D0=BE=D0=B4=D0=B5=20=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82=D0=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/build.gradle | 23 ++-- .../configuration/SecurityConfiguration.java | 67 ----------- .../demo/configuration/WebConfiguration.java | 24 ---- .../Configuration/OpenAPI30Configuration.java | 28 +++++ .../PasswordEncoderConfiguration.java | 2 +- .../Configuration/SecurityConfiguration.java | 96 ++++++++++++++++ .../Configuration/WebConfiguration.java | 49 ++++++++ .../Configuration/jwt/JwtException.java | 11 ++ .../supply/Configuration/jwt/JwtFilter.java | 72 ++++++++++++ .../Configuration/jwt/JwtProperties.java | 27 +++++ .../supply/Configuration/jwt/JwtProvider.java | 107 ++++++++++++++++++ .../com/example/demo/supply/Order/Order.java | 6 +- .../demo/supply/Order/OrderController.java | 2 +- .../demo/supply/Order/OrderMvcController.java | 79 ------------- .../example/demo/supply/Product/Product.java | 2 +- .../supply/Product/ProductController.java | 5 +- .../supply/Product/ProductMvcController.java | 60 ---------- .../demo/supply/Product/ProductService.java | 6 +- .../demo/supply/Supplier/Supplier.java | 2 +- .../supply/Supplier/SupplierController.java | 2 +- .../Supplier/SupplierMvcController.java | 60 ---------- .../demo/supply/User/UserMvcController.java | 40 ------- .../supply/User/UserSignupMvcController.java | 48 -------- .../User/controller/UserController.java | 64 +++++++++++ .../supply/User/{ => controller}/UserDto.java | 6 +- .../supply/User/controller/UserInfoDto.java | 27 +++++ .../supply/User/controller/UserLoginDto.java | 25 ++++ .../User/{ => controller}/UserSignupDto.java | 6 +- .../demo/supply/User/{ => model}/User.java | 24 ++-- .../supply/User/{ => model}/UserRole.java | 2 +- .../User/{ => repository}/UserRepository.java | 3 +- .../User/service/UserExistsException.java | 7 ++ .../User/service/UserNotFoundException.java | 7 ++ .../User/{ => service}/UserService.java | 38 ++++++- .../supply/util/validation/ValidatorUtil.java | 8 +- .../src/main/resources/application.properties | 5 +- .../resources/templates/common/header.html | 10 -- .../src/main/resources/templates/default.html | 56 --------- demo/src/main/resources/templates/index.html | 12 -- demo/src/main/resources/templates/login.html | 34 ------ .../main/resources/templates/order-add.html | 37 ------ .../main/resources/templates/order-dop.html | 61 ---------- demo/src/main/resources/templates/order.html | 41 ------- .../resources/templates/product-edit.html | 30 ----- .../src/main/resources/templates/product.html | 55 --------- demo/src/main/resources/templates/signup.html | 29 ----- .../resources/templates/supplier-edit.html | 30 ----- .../main/resources/templates/supplier.html | 55 --------- demo/src/main/resources/templates/users.html | 38 ------- front/package.json | 6 +- front/src/App.js | 28 +++-- front/src/AuthService.js | 36 ++++++ front/src/DataService.js | 36 +++--- front/src/Pages/LoginPage.jsx | 72 ++++++++++++ front/src/Pages/SignUpPage.jsx | 82 ++++++++++++++ front/src/general/Header.jsx | 28 ++++- front/src/models/UserLogin.js | 6 + front/src/models/UserSignUp.js | 7 ++ front/src/utils/PrivateRoutes.jsx | 36 ++++++ 59 files changed, 914 insertions(+), 951 deletions(-) delete mode 100644 demo/src/main/java/com/example/demo/configuration/SecurityConfiguration.java delete mode 100644 demo/src/main/java/com/example/demo/configuration/WebConfiguration.java create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/OpenAPI30Configuration.java rename demo/src/main/java/com/example/demo/{configuration => supply/Configuration}/PasswordEncoderConfiguration.java (90%) create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/SecurityConfiguration.java create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/WebConfiguration.java create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtException.java create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtFilter.java create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProperties.java create mode 100644 demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProvider.java delete mode 100644 demo/src/main/java/com/example/demo/supply/Order/OrderMvcController.java delete mode 100644 demo/src/main/java/com/example/demo/supply/Product/ProductMvcController.java delete mode 100644 demo/src/main/java/com/example/demo/supply/Supplier/SupplierMvcController.java delete mode 100644 demo/src/main/java/com/example/demo/supply/User/UserMvcController.java delete mode 100644 demo/src/main/java/com/example/demo/supply/User/UserSignupMvcController.java create mode 100644 demo/src/main/java/com/example/demo/supply/User/controller/UserController.java rename demo/src/main/java/com/example/demo/supply/User/{ => controller}/UserDto.java (74%) create mode 100644 demo/src/main/java/com/example/demo/supply/User/controller/UserInfoDto.java create mode 100644 demo/src/main/java/com/example/demo/supply/User/controller/UserLoginDto.java rename demo/src/main/java/com/example/demo/supply/User/{ => controller}/UserSignupDto.java (83%) rename demo/src/main/java/com/example/demo/supply/User/{ => model}/User.java (72%) rename demo/src/main/java/com/example/demo/supply/User/{ => model}/UserRole.java (91%) rename demo/src/main/java/com/example/demo/supply/User/{ => repository}/UserRepository.java (65%) create mode 100644 demo/src/main/java/com/example/demo/supply/User/service/UserExistsException.java create mode 100644 demo/src/main/java/com/example/demo/supply/User/service/UserNotFoundException.java rename demo/src/main/java/com/example/demo/supply/User/{ => service}/UserService.java (61%) delete mode 100644 demo/src/main/resources/templates/common/header.html delete mode 100644 demo/src/main/resources/templates/default.html delete mode 100644 demo/src/main/resources/templates/index.html delete mode 100644 demo/src/main/resources/templates/login.html delete mode 100644 demo/src/main/resources/templates/order-add.html delete mode 100644 demo/src/main/resources/templates/order-dop.html delete mode 100644 demo/src/main/resources/templates/order.html delete mode 100644 demo/src/main/resources/templates/product-edit.html delete mode 100644 demo/src/main/resources/templates/product.html delete mode 100644 demo/src/main/resources/templates/signup.html delete mode 100644 demo/src/main/resources/templates/supplier-edit.html delete mode 100644 demo/src/main/resources/templates/supplier.html delete mode 100644 demo/src/main/resources/templates/users.html create mode 100644 front/src/AuthService.js create mode 100644 front/src/Pages/LoginPage.jsx create mode 100644 front/src/Pages/SignUpPage.jsx create mode 100644 front/src/models/UserLogin.js create mode 100644 front/src/models/UserSignUp.js create mode 100644 front/src/utils/PrivateRoutes.jsx diff --git a/demo/build.gradle b/demo/build.gradle index c57e731..c795a67 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' - id 'org.springframework.boot' version '2.6.3' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'org.springframework.boot' version '3.0.2' + id 'io.spring.dependency-management' version '1.1.0' } group = 'com.example' @@ -17,24 +17,15 @@ jar{ } dependencies { + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-devtools' - implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect' - implementation 'org.springframework.boot:spring-boot-starter-security' - implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5' - - 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-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'com.h2database:h2:2.1.210' - implementation 'org.hibernate.validator:hibernate-validator' - - implementation 'org.springdoc:springdoc-openapi-ui:1.6.5' - + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4' + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'com.auth0:java-jwt:4.4.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' } diff --git a/demo/src/main/java/com/example/demo/configuration/SecurityConfiguration.java b/demo/src/main/java/com/example/demo/configuration/SecurityConfiguration.java deleted file mode 100644 index 547e73e..0000000 --- a/demo/src/main/java/com/example/demo/configuration/SecurityConfiguration.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.example.demo.configuration; - -import com.example.demo.supply.User.UserRole; -import com.example.demo.supply.User.UserService; -import com.example.demo.supply.User.UserSignupMvcController; -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 UserService userService; - - public SecurityConfiguration(UserService userService) { - this.userService = userService; - createAdminOnStartup(); - } - - private void createAdminOnStartup() { - final String admin = "admin"; - if (userService.findByLogin(admin) == null) { - log.info("Admin user successfully created"); - userService.createUser(admin, admin, admin, UserRole.ADMIN); - } - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.headers().frameOptions().sameOrigin().and() - .cors().and() - .csrf().disable() - .authorizeRequests() - .antMatchers(UserSignupMvcController.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(userService); - } - - @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/demo/src/main/java/com/example/demo/configuration/WebConfiguration.java b/demo/src/main/java/com/example/demo/configuration/WebConfiguration.java deleted file mode 100644 index a28a3a4..0000000 --- a/demo/src/main/java/com/example/demo/configuration/WebConfiguration.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.example.demo.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("rest-test"); - registry.addViewController("login"); - } - - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**").allowedMethods("*"); - } -} - diff --git a/demo/src/main/java/com/example/demo/supply/Configuration/OpenAPI30Configuration.java b/demo/src/main/java/com/example/demo/supply/Configuration/OpenAPI30Configuration.java new file mode 100644 index 0000000..a30b725 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/OpenAPI30Configuration.java @@ -0,0 +1,28 @@ +package com.example.demo.supply.Configuration; + +import com.example.demo.supply.Configuration.jwt.JwtFilter; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenAPI30Configuration { + public static final String API_PREFIX = "/api/1.0"; + + @Bean + public OpenAPI customizeOpenAPI() { + final String securitySchemeName = JwtFilter.TOKEN_BEGIN_STR; + return new OpenAPI() + .addSecurityItem(new SecurityRequirement() + .addList(securitySchemeName)) + .components(new Components() + .addSecuritySchemes(securitySchemeName, new SecurityScheme() + .name(securitySchemeName) + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT"))); + } +} diff --git a/demo/src/main/java/com/example/demo/configuration/PasswordEncoderConfiguration.java b/demo/src/main/java/com/example/demo/supply/Configuration/PasswordEncoderConfiguration.java similarity index 90% rename from demo/src/main/java/com/example/demo/configuration/PasswordEncoderConfiguration.java rename to demo/src/main/java/com/example/demo/supply/Configuration/PasswordEncoderConfiguration.java index 6937121..5aad6e3 100644 --- a/demo/src/main/java/com/example/demo/configuration/PasswordEncoderConfiguration.java +++ b/demo/src/main/java/com/example/demo/supply/Configuration/PasswordEncoderConfiguration.java @@ -1,4 +1,4 @@ -package com.example.demo.configuration; +package com.example.demo.supply.Configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/demo/src/main/java/com/example/demo/supply/Configuration/SecurityConfiguration.java b/demo/src/main/java/com/example/demo/supply/Configuration/SecurityConfiguration.java new file mode 100644 index 0000000..da58fe9 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/SecurityConfiguration.java @@ -0,0 +1,96 @@ +package com.example.demo.supply.Configuration; + +import com.example.demo.supply.Configuration.jwt.JwtFilter; +import com.example.demo.supply.User.controller.UserController; +import com.example.demo.supply.User.model.UserRole; +import com.example.demo.supply.User.service.UserService; +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.authentication.AuthenticationManager; +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.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 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(securedEnabled = true) +public class SecurityConfiguration { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + + public static final String SPA_URL_MASK = "/{path:[^\\.]*}/"; + + private final UserService userService; + private final JwtFilter jwtFilter; + + public SecurityConfiguration(UserService userService) { + this.userService = userService; + this.jwtFilter = new JwtFilter(userService); + createAdminOnStartup(); + } + + private void createAdminOnStartup() { + final String admin = "admin"; + if (userService.findByLogin(admin) == null) { + log.info("Admin user successfully created"); + userService.createUser(admin, admin, admin, UserRole.ADMIN); + } + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + log.info("Creating security configuration"); + http.cors() + .and() + .csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeHttpRequests() + .requestMatchers("/", SPA_URL_MASK).permitAll() + .requestMatchers(HttpMethod.POST, UserController.URL_SIGNUP).permitAll() + .requestMatchers(HttpMethod.POST, UserController.URL_LOGIN).permitAll() + .requestMatchers(HttpMethod.GET, "/users/*").permitAll() + .requestMatchers(HttpMethod.GET, "/h2-console").permitAll() + .anyRequest() + .authenticated() + .and() + .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) + .anonymous(); + return http.build(); + } + + @Bean + public AuthenticationManager authenticationManager(HttpSecurity http, PasswordEncoderConfiguration bCryptPasswordEncoder) + throws Exception { + return http.getSharedObject(AuthenticationManagerBuilder.class) + .userDetailsService(userService) + .passwordEncoder(bCryptPasswordEncoder.createPasswordEncoder()) + .and() + .build(); + } + + @Bean + public WebSecurityCustomizer webSecurityCustomizer() { + return (web) -> web.ignoring() + .requestMatchers(HttpMethod.OPTIONS, "/**") + .requestMatchers("/*.js") + .requestMatchers("/*.png") + .requestMatchers("/*.jpg") + .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/**"); + } +} diff --git a/demo/src/main/java/com/example/demo/supply/Configuration/WebConfiguration.java b/demo/src/main/java/com/example/demo/supply/Configuration/WebConfiguration.java new file mode 100644 index 0000000..9edeba4 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/WebConfiguration.java @@ -0,0 +1,49 @@ +package com.example.demo.supply.Configuration; + +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.web.servlet.config.annotation.*; + +@Configuration +public class WebConfiguration implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry){ + registry.addMapping("/**").allowedMethods("*"); + } + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController(SecurityConfiguration.SPA_URL_MASK).setViewName("forward:/"); + registry.addViewController("/notFound").setViewName("forward:/"); + ViewControllerRegistration registration = registry.addViewController("/notFound"); + registration.setViewName("forward:/index.html"); + registration.setStatusCode(HttpStatus.OK); + } + //@Override +//public void addViewControllers(ViewControllerRegistry registry) { +// WebMvcConfigurer.super.addViewControllers(registry); +// registry.addViewController("rest-test"); +//} + @Bean + public WebServerFactoryCustomizer containerCustomizer() { + return container -> { + container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound")); + }; + } + @Override + public void addResourceHandlers( + ResourceHandlerRegistry registry) { + registry.addResourceHandler("/public/**") + .addResourceLocations("/WEB-INF/view/react/build/public/"); + registry.addResourceHandler("/*.js") + .addResourceLocations("/WEB-INF/view/react/build/"); + registry.addResourceHandler("/*.json") + .addResourceLocations("/WEB-INF/view/react/build/"); + registry.addResourceHandler("/*.ico") + .addResourceLocations("/WEB-INF/view/react/build/"); + } +} diff --git a/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtException.java b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtException.java new file mode 100644 index 0000000..213648f --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtException.java @@ -0,0 +1,11 @@ +package com.example.demo.supply.Configuration.jwt; + +public class JwtException extends RuntimeException { + public JwtException(Throwable throwable) { + super(throwable); + } + + public JwtException(String message) { + super(message); + } +} diff --git a/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtFilter.java b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtFilter.java new file mode 100644 index 0000000..004b892 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtFilter.java @@ -0,0 +1,72 @@ +package com.example.demo.supply.Configuration.jwt; + +import com.example.demo.supply.User.service.UserService; +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 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/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProperties.java b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProperties.java new file mode 100644 index 0000000..7fd4b39 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProperties.java @@ -0,0 +1,27 @@ +package com.example.demo.supply.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/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProvider.java b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProvider.java new file mode 100644 index 0000000..5f0c1e6 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/Configuration/jwt/JwtProvider.java @@ -0,0 +1,107 @@ +package com.example.demo.supply.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()); + return JWT.create() + .withIssuer(ISSUER) + .withIssuedAt(issueDate) + .withExpiresAt(expireDate) + .withSubject(login) + .sign(algorithm); + } + + 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/demo/src/main/java/com/example/demo/supply/Order/Order.java b/demo/src/main/java/com/example/demo/supply/Order/Order.java index 762d2e6..b2f101d 100644 --- a/demo/src/main/java/com/example/demo/supply/Order/Order.java +++ b/demo/src/main/java/com/example/demo/supply/Order/Order.java @@ -3,15 +3,11 @@ package com.example.demo.supply.Order; import com.example.demo.supply.Product.Product; import com.example.demo.supply.Supplier.Supplier; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; import java.util.Objects; -import javax.persistence.*; import java.util.Date; import java.util.ArrayList; import java.util.List; diff --git a/demo/src/main/java/com/example/demo/supply/Order/OrderController.java b/demo/src/main/java/com/example/demo/supply/Order/OrderController.java index baafe05..68f0356 100644 --- a/demo/src/main/java/com/example/demo/supply/Order/OrderController.java +++ b/demo/src/main/java/com/example/demo/supply/Order/OrderController.java @@ -11,7 +11,7 @@ import java.util.List; @RestController @CrossOrigin -@RequestMapping("/api/order") +@RequestMapping("/order") public class OrderController { private OrderService orderService; diff --git a/demo/src/main/java/com/example/demo/supply/Order/OrderMvcController.java b/demo/src/main/java/com/example/demo/supply/Order/OrderMvcController.java deleted file mode 100644 index 0e75409..0000000 --- a/demo/src/main/java/com/example/demo/supply/Order/OrderMvcController.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.example.demo.supply.Order; - -import com.example.demo.supply.Product.ProductDto; -import com.example.demo.supply.Product.ProductService; -import com.example.demo.supply.Supplier.SupplierDto; -import com.example.demo.supply.Supplier.SupplierService; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -@Controller -@RequestMapping("/order") -public class OrderMvcController { - private OrderService orderService; - private ProductService productService; - private SupplierService supplierService; - - public OrderMvcController(OrderService orderService, - ProductService productService, - SupplierService supplierService){ - this.orderService = orderService; - this.productService = productService; - this.supplierService = supplierService; - } - - @GetMapping - public String getOrders(Model model) { - model.addAttribute("orders", - orderService.findAllOrders().stream() - .map(OrderDto::new) - .toList()); - return "order"; - } - - @GetMapping("/dop") - public String getSuppliers(Model model, - @RequestParam(required = false) Long id) { - if(id == null || id <= 0) { - model.addAttribute("productDto", new ProductDto()); - model.addAttribute("suppliers", null); - model.addAttribute("selectedProduct", null); - } - else{ - ProductDto product = new ProductDto(productService.findProduct(id)); - model.addAttribute("productDto", product); - model.addAttribute("selectedProduct", null); - model.addAttribute("suppliers", orderService.suppliers(id).stream().map(SupplierDto::new).toList()); - } - model.addAttribute("products", productService.findAllProducts().stream().map(ProductDto::new).toList()); -// model.addAttribute("orderDto", new OrderDto(orderService.findOrder(id))); - return "order-dop"; - } - - @GetMapping("/add") - public String addOrder(Model model) { - model.addAttribute("orderDto", new OrderDtoForCreate()); - model.addAttribute("selectedSupplier", null); - model.addAttribute("suppliers", supplierService.findAllSuppliers().stream().map(SupplierDto::new).toList()); - model.addAttribute("products", productService.findAllProducts().stream().map(ProductDto::new).toList()); - return "order-add"; - } - - @PostMapping("/create") - public String saveOrder(Model model, - @ModelAttribute("orderDto") @Valid OrderDtoForCreate order, - BindingResult bindingResult) { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", bindingResult.getAllErrors()); - return "order-add"; - } - Order newOrder = orderService.addOrder(supplierService.findSupplier(order.getSupplierId()).getId()); - for(Long obj: order.getProductsId()) - orderService.addProduct(newOrder.getId(), obj); - return "redirect:/order"; - } -} diff --git a/demo/src/main/java/com/example/demo/supply/Product/Product.java b/demo/src/main/java/com/example/demo/supply/Product/Product.java index 3fae949..7048ebf 100644 --- a/demo/src/main/java/com/example/demo/supply/Product/Product.java +++ b/demo/src/main/java/com/example/demo/supply/Product/Product.java @@ -2,8 +2,8 @@ package com.example.demo.supply.Product; import com.example.demo.supply.Order.Order; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; -import javax.persistence.*; import java.util.Objects; import java.util.ArrayList; diff --git a/demo/src/main/java/com/example/demo/supply/Product/ProductController.java b/demo/src/main/java/com/example/demo/supply/Product/ProductController.java index 68ccee4..66f154f 100644 --- a/demo/src/main/java/com/example/demo/supply/Product/ProductController.java +++ b/demo/src/main/java/com/example/demo/supply/Product/ProductController.java @@ -1,12 +1,13 @@ package com.example.demo.supply.Product; +import com.example.demo.supply.User.model.UserRole; +import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController -@CrossOrigin -@RequestMapping("/api/product") +@RequestMapping("/product") public class ProductController { private final ProductService productService; diff --git a/demo/src/main/java/com/example/demo/supply/Product/ProductMvcController.java b/demo/src/main/java/com/example/demo/supply/Product/ProductMvcController.java deleted file mode 100644 index fabb9af..0000000 --- a/demo/src/main/java/com/example/demo/supply/Product/ProductMvcController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.example.demo.supply.Product; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -@Controller -@RequestMapping("/product") -public class ProductMvcController { - private final ProductService productService; - - public ProductMvcController(ProductService productService){ this.productService = productService;} - - @GetMapping - public String getProducts(Model model) { - model.addAttribute("products", - productService.findAllProducts().stream() - .map(ProductDto::new) - .toList()); - return "product"; - } - - @GetMapping(value = {"/edit", "/edit/{id}"}) - public String editProduct(@PathVariable(required = false) Long id, - Model model) { - if (id == null || id <= 0) { - model.addAttribute("productDto", new ProductDto()); - } else { - model.addAttribute("productId", id); - model.addAttribute("productDto", new ProductDto(productService.findProduct(id))); - } - return "product-edit"; - } - - @PostMapping(value = {"", "/{id}"}) - public String saveProduct(@PathVariable(required = false) Long id, - @ModelAttribute @Valid ProductDto productDto, - BindingResult bindingResult, - Model model) { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", bindingResult.getAllErrors()); - return "product-edit"; - } - if (id == null || id <= 0) { - productService.addProduct(productDto.getName(), productDto.getCost()); - } else { - productService.updateProduct(id, productDto.getName(), productDto.getCost()); - } - return "redirect:/product"; - } - - @PostMapping("/delete/{id}") - public String deleteProduct(@PathVariable Long id) { - productService.deleteProduct(id); - return "redirect:/product"; - } -} diff --git a/demo/src/main/java/com/example/demo/supply/Product/ProductService.java b/demo/src/main/java/com/example/demo/supply/Product/ProductService.java index 6a7e5d9..05a789c 100644 --- a/demo/src/main/java/com/example/demo/supply/Product/ProductService.java +++ b/demo/src/main/java/com/example/demo/supply/Product/ProductService.java @@ -1,5 +1,6 @@ package com.example.demo.supply.Product; +import com.example.demo.supply.User.service.UserService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -9,8 +10,11 @@ import java.util.Optional; @Service public class ProductService { private final ProductRepository productRepository; - public ProductService(ProductRepository productRepository){ + private final UserService userService; + + public ProductService(ProductRepository productRepository, UserService userService){ this.productRepository = productRepository; + this.userService = userService; } @Transactional diff --git a/demo/src/main/java/com/example/demo/supply/Supplier/Supplier.java b/demo/src/main/java/com/example/demo/supply/Supplier/Supplier.java index dc984f9..72e83bb 100644 --- a/demo/src/main/java/com/example/demo/supply/Supplier/Supplier.java +++ b/demo/src/main/java/com/example/demo/supply/Supplier/Supplier.java @@ -2,8 +2,8 @@ package com.example.demo.supply.Supplier; import com.example.demo.supply.Order.Order; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; -import javax.persistence.*; import java.util.Objects; import java.util.ArrayList; diff --git a/demo/src/main/java/com/example/demo/supply/Supplier/SupplierController.java b/demo/src/main/java/com/example/demo/supply/Supplier/SupplierController.java index 0eaa6a8..bae742e 100644 --- a/demo/src/main/java/com/example/demo/supply/Supplier/SupplierController.java +++ b/demo/src/main/java/com/example/demo/supply/Supplier/SupplierController.java @@ -6,7 +6,7 @@ import java.util.List; @RestController @CrossOrigin -@RequestMapping("/api/supplier") +@RequestMapping("/supplier") public class SupplierController { private final SupplierService supplierService; diff --git a/demo/src/main/java/com/example/demo/supply/Supplier/SupplierMvcController.java b/demo/src/main/java/com/example/demo/supply/Supplier/SupplierMvcController.java deleted file mode 100644 index 9d7e33d..0000000 --- a/demo/src/main/java/com/example/demo/supply/Supplier/SupplierMvcController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.example.demo.supply.Supplier; - -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; - -@Controller -@RequestMapping("/supplier") -public class SupplierMvcController { - private final SupplierService supplierService; - - public SupplierMvcController(SupplierService supplierService){ this.supplierService = supplierService;} - - @GetMapping - public String getSuppliers(Model model) { - model.addAttribute("suppliers", - supplierService.findAllSuppliers().stream() - .map(SupplierDto::new) - .toList()); - return "supplier"; - } - - @GetMapping(value = {"/edit", "/edit/{id}"}) - public String editSuppliers(@PathVariable(required = false) Long id, - Model model) { - if (id == null || id <= 0) { - model.addAttribute("supplierDto", new SupplierDto()); - } else { - model.addAttribute("supplierId", id); - model.addAttribute("supplierDto", new SupplierDto(supplierService.findSupplier(id))); - } - return "supplier-edit"; - } - - @PostMapping(value = {"", "/{id}"}) - public String saveSuppliers(@PathVariable(required = false) Long id, - @ModelAttribute @Valid SupplierDto supplierDto, - BindingResult bindingResult, - Model model) { - if (bindingResult.hasErrors()) { - model.addAttribute("errors", bindingResult.getAllErrors()); - return "supplier-edit"; - } - if (id == null || id <= 0) { - supplierService.addSupplier(supplierDto.getName(), supplierDto.getLicense()); - } else { - supplierService.updateSupplier(id, supplierDto.getName(), supplierDto.getLicense()); - } - return "redirect:/supplier"; - } - - @PostMapping("/delete/{id}") - public String deleteSuppliers(@PathVariable Long id) { - supplierService.deleteSupplier(id); - return "redirect:/supplier"; - } -} diff --git a/demo/src/main/java/com/example/demo/supply/User/UserMvcController.java b/demo/src/main/java/com/example/demo/supply/User/UserMvcController.java deleted file mode 100644 index e716000..0000000 --- a/demo/src/main/java/com/example/demo/supply/User/UserMvcController.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.example.demo.supply.User; - -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 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/demo/src/main/java/com/example/demo/supply/User/UserSignupMvcController.java b/demo/src/main/java/com/example/demo/supply/User/UserSignupMvcController.java deleted file mode 100644 index 2f53c64..0000000 --- a/demo/src/main/java/com/example/demo/supply/User/UserSignupMvcController.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.example.demo.supply.User; - -import com.example.demo.supply.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.validation.Valid; - -@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/demo/src/main/java/com/example/demo/supply/User/controller/UserController.java b/demo/src/main/java/com/example/demo/supply/User/controller/UserController.java new file mode 100644 index 0000000..334e2d4 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/User/controller/UserController.java @@ -0,0 +1,64 @@ +package com.example.demo.supply.User.controller; + +import com.example.demo.supply.Configuration.OpenAPI30Configuration; +import com.example.demo.supply.User.model.User; +import com.example.demo.supply.User.model.UserRole; +import com.example.demo.supply.User.service.UserService; +import com.example.demo.supply.util.validation.ValidationException; +import jakarta.validation.Valid; +import org.springframework.data.domain.Page; +import org.springframework.data.util.Pair; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.IntStream; + + +@RestController +public class UserController { + public static final String URL_LOGIN = "/jwt/login"; + public static final String URL_SIGNUP = "/signup"; + public static final String URL_MAIN = "/users"; + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + @GetMapping(OpenAPI30Configuration.API_PREFIX + URL_MAIN) + @Secured({UserRole.AsString.ADMIN}) + public Pair, List> getUsers(@RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "5") int size){ + final Page users = userService.findAllPages(page, size).map(UserDto::new); + final int totalPages = users.getTotalPages(); + final List pageNumbers = IntStream.rangeClosed(1, totalPages) + .boxed() + .toList(); + return Pair.of(users, pageNumbers); + } + @PostMapping(URL_LOGIN) + public UserInfoDto login(@RequestBody @Valid UserLoginDto userLoginDto) { + return userService.loginAndGetToken(userLoginDto); + } + @PostMapping(URL_SIGNUP) + public String signup(@RequestBody @Valid UserSignupDto userSignupDto){ + try { + User user = userService.createUser(userSignupDto.getLogin(), userSignupDto.getPassword(), userSignupDto.getPasswordConfirm()); + return user.getLogin() + "was created"; + } + catch(ValidationException e){ + return e.getMessage(); + } + } + @GetMapping(URL_MAIN + "/{login}") + public UserDetails getCurrentUser(@PathVariable String login){ + try{ + return userService.loadUserByUsername(login); + } + catch(Exception e){ + return null; + } + } +} diff --git a/demo/src/main/java/com/example/demo/supply/User/UserDto.java b/demo/src/main/java/com/example/demo/supply/User/controller/UserDto.java similarity index 74% rename from demo/src/main/java/com/example/demo/supply/User/UserDto.java rename to demo/src/main/java/com/example/demo/supply/User/controller/UserDto.java index f09f2f6..e6a0c18 100644 --- a/demo/src/main/java/com/example/demo/supply/User/UserDto.java +++ b/demo/src/main/java/com/example/demo/supply/User/controller/UserDto.java @@ -1,4 +1,8 @@ -package com.example.demo.supply.User; +package com.example.demo.supply.User.controller; + + +import com.example.demo.supply.User.model.User; +import com.example.demo.supply.User.model.UserRole; public class UserDto { private final long id; diff --git a/demo/src/main/java/com/example/demo/supply/User/controller/UserInfoDto.java b/demo/src/main/java/com/example/demo/supply/User/controller/UserInfoDto.java new file mode 100644 index 0000000..73d6cd9 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/User/controller/UserInfoDto.java @@ -0,0 +1,27 @@ +package com.example.demo.supply.User.controller; + + +import com.example.demo.supply.User.model.UserRole; + +public class UserInfoDto { + private final String token; + private final String login; + private final UserRole role; + public UserInfoDto(String token, String login, UserRole role) { + this.token = token; + this.login = login; + this.role = role; + } + + public String getLogin() { + return login; + } + + public String getToken() { + return token; + } + + public UserRole getRole() { + return role; + } +} diff --git a/demo/src/main/java/com/example/demo/supply/User/controller/UserLoginDto.java b/demo/src/main/java/com/example/demo/supply/User/controller/UserLoginDto.java new file mode 100644 index 0000000..df2d139 --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/User/controller/UserLoginDto.java @@ -0,0 +1,25 @@ +package com.example.demo.supply.User.controller; + +import jakarta.validation.constraints.NotBlank; + +public class UserLoginDto { + @NotBlank + private String login; + @NotBlank + private String password; + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} diff --git a/demo/src/main/java/com/example/demo/supply/User/UserSignupDto.java b/demo/src/main/java/com/example/demo/supply/User/controller/UserSignupDto.java similarity index 83% rename from demo/src/main/java/com/example/demo/supply/User/UserSignupDto.java rename to demo/src/main/java/com/example/demo/supply/User/controller/UserSignupDto.java index 353106b..8dadc98 100644 --- a/demo/src/main/java/com/example/demo/supply/User/UserSignupDto.java +++ b/demo/src/main/java/com/example/demo/supply/User/controller/UserSignupDto.java @@ -1,7 +1,7 @@ -package com.example.demo.supply.User; +package com.example.demo.supply.User.controller; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; public class UserSignupDto { @NotBlank diff --git a/demo/src/main/java/com/example/demo/supply/User/User.java b/demo/src/main/java/com/example/demo/supply/User/model/User.java similarity index 72% rename from demo/src/main/java/com/example/demo/supply/User/User.java rename to demo/src/main/java/com/example/demo/supply/User/model/User.java index 6927f87..c3cf955 100644 --- a/demo/src/main/java/com/example/demo/supply/User/User.java +++ b/demo/src/main/java/com/example/demo/supply/User/model/User.java @@ -1,8 +1,9 @@ -package com.example.demo.supply.User; +package com.example.demo.supply.User.model; + +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; -import javax.persistence.*; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; import java.util.Objects; @Entity @@ -17,7 +18,7 @@ public class User { private String login; @Column(nullable = false, length = 64) @NotBlank - @Size(min = 4, max = 64) + @Size(min = 6, max = 64) private String password; private UserRole role; @@ -63,11 +64,20 @@ public class User { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; - return Objects.equals(id, user.id) && Objects.equals(login, user.login); + return Objects.equals(id, user.id); } @Override public int hashCode() { - return Objects.hash(id, login); + return Objects.hash(id); + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", login='" + login + '\'' + + ", password='" + password + '\'' + + '}'; } } diff --git a/demo/src/main/java/com/example/demo/supply/User/UserRole.java b/demo/src/main/java/com/example/demo/supply/User/model/UserRole.java similarity index 91% rename from demo/src/main/java/com/example/demo/supply/User/UserRole.java rename to demo/src/main/java/com/example/demo/supply/User/model/UserRole.java index 19726e7..5371550 100644 --- a/demo/src/main/java/com/example/demo/supply/User/UserRole.java +++ b/demo/src/main/java/com/example/demo/supply/User/model/UserRole.java @@ -1,4 +1,4 @@ -package com.example.demo.supply.User; +package com.example.demo.supply.User.model; import org.springframework.security.core.GrantedAuthority; diff --git a/demo/src/main/java/com/example/demo/supply/User/UserRepository.java b/demo/src/main/java/com/example/demo/supply/User/repository/UserRepository.java similarity index 65% rename from demo/src/main/java/com/example/demo/supply/User/UserRepository.java rename to demo/src/main/java/com/example/demo/supply/User/repository/UserRepository.java index dcb3897..753e34d 100644 --- a/demo/src/main/java/com/example/demo/supply/User/UserRepository.java +++ b/demo/src/main/java/com/example/demo/supply/User/repository/UserRepository.java @@ -1,5 +1,6 @@ -package com.example.demo.supply.User; +package com.example.demo.supply.User.repository; +import com.example.demo.supply.User.model.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository { diff --git a/demo/src/main/java/com/example/demo/supply/User/service/UserExistsException.java b/demo/src/main/java/com/example/demo/supply/User/service/UserExistsException.java new file mode 100644 index 0000000..cd87cfb --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/User/service/UserExistsException.java @@ -0,0 +1,7 @@ +package com.example.demo.supply.User.service; + +public class UserExistsException extends RuntimeException { + public UserExistsException(String login) { + super(String.format("User '%s' already exists", login)); + } +} diff --git a/demo/src/main/java/com/example/demo/supply/User/service/UserNotFoundException.java b/demo/src/main/java/com/example/demo/supply/User/service/UserNotFoundException.java new file mode 100644 index 0000000..798b21c --- /dev/null +++ b/demo/src/main/java/com/example/demo/supply/User/service/UserNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.demo.supply.User.service; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String login) { + super(String.format("User not found '%s'", login)); + } +} diff --git a/demo/src/main/java/com/example/demo/supply/User/UserService.java b/demo/src/main/java/com/example/demo/supply/User/service/UserService.java similarity index 61% rename from demo/src/main/java/com/example/demo/supply/User/UserService.java rename to demo/src/main/java/com/example/demo/supply/User/service/UserService.java index 4ce59b5..930f20c 100644 --- a/demo/src/main/java/com/example/demo/supply/User/UserService.java +++ b/demo/src/main/java/com/example/demo/supply/User/service/UserService.java @@ -1,5 +1,13 @@ -package com.example.demo.supply.User; +package com.example.demo.supply.User.service; + +import com.example.demo.supply.Configuration.jwt.JwtException; +import com.example.demo.supply.Configuration.jwt.JwtProvider; +import com.example.demo.supply.User.controller.UserInfoDto; +import com.example.demo.supply.User.controller.UserLoginDto; +import com.example.demo.supply.User.model.User; +import com.example.demo.supply.User.model.UserRole; +import com.example.demo.supply.User.repository.UserRepository; import com.example.demo.supply.util.validation.ValidationException; import com.example.demo.supply.util.validation.ValidatorUtil; import org.springframework.data.domain.Page; @@ -11,7 +19,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; - import java.util.Collections; import java.util.Objects; @@ -20,13 +27,16 @@ public class UserService implements UserDetailsService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; private final ValidatorUtil validatorUtil; + private final JwtProvider jwtProvider; public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, - ValidatorUtil validatorUtil) { + ValidatorUtil validatorUtil, + JwtProvider jwtProvider) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; this.validatorUtil = validatorUtil; + this.jwtProvider = jwtProvider; } public Page findAllPages(int page, int size) { @@ -43,7 +53,7 @@ public class UserService implements UserDetailsService { public User createUser(String login, String password, String passwordConfirm, UserRole role) { if (findByLogin(login) != null) { - throw new ValidationException(String.format("User '%s' already exists", login)); + throw new UserExistsException(login); } final User user = new User(login, passwordEncoder.encode(password), role); validatorUtil.validate(user); @@ -53,6 +63,26 @@ public class UserService implements UserDetailsService { return userRepository.save(user); } + public UserInfoDto loginAndGetToken(UserLoginDto userDto) { + final User user = findByLogin(userDto.getLogin()); + if (user == null) { + throw new UserNotFoundException(userDto.getLogin()); + } + if (!passwordEncoder.matches(userDto.getPassword(), user.getPassword())) { + throw new UserNotFoundException(user.getLogin()); + } + return new UserInfoDto(jwtProvider.generateToken(user.getLogin()), user.getLogin(), user.getRole()); + } + + 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); + } + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { final User userEntity = findByLogin(username); diff --git a/demo/src/main/java/com/example/demo/supply/util/validation/ValidatorUtil.java b/demo/src/main/java/com/example/demo/supply/util/validation/ValidatorUtil.java index b10919a..6be3baa 100644 --- a/demo/src/main/java/com/example/demo/supply/util/validation/ValidatorUtil.java +++ b/demo/src/main/java/com/example/demo/supply/util/validation/ValidatorUtil.java @@ -1,11 +1,11 @@ package com.example.demo.supply.util.validation; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; import org.springframework.stereotype.Component; -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/demo/src/main/resources/application.properties b/demo/src/main/resources/application.properties index 14d2c47..5deb13d 100644 --- a/demo/src/main/resources/application.properties +++ b/demo/src/main/resources/application.properties @@ -1,5 +1,6 @@ spring.main.banner-mode=off server.port=8080 +server.tomcat.relaxed-query-chars=|,{,},[,] spring.datasource.url=jdbc:h2:file:./data spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa @@ -8,4 +9,6 @@ spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 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 \ No newline at end of file +spring.h2.console.settings.web-allow-others=false +jwt.dev-token=my-secret-jwt +jwt.dev=true \ No newline at end of file diff --git a/demo/src/main/resources/templates/common/header.html b/demo/src/main/resources/templates/common/header.html deleted file mode 100644 index 566549b..0000000 --- a/demo/src/main/resources/templates/common/header.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Title - - - - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/default.html b/demo/src/main/resources/templates/default.html deleted file mode 100644 index 2d87939..0000000 --- a/demo/src/main/resources/templates/default.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - Поставки - - - - - - - - - -
-
-
-
- - - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/index.html b/demo/src/main/resources/templates/index.html deleted file mode 100644 index be8545f..0000000 --- a/demo/src/main/resources/templates/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - -
-
It's works!
-
- - \ No newline at end of file diff --git a/demo/src/main/resources/templates/login.html b/demo/src/main/resources/templates/login.html deleted file mode 100644 index 6c07f67..0000000 --- a/demo/src/main/resources/templates/login.html +++ /dev/null @@ -1,34 +0,0 @@ - - -> - - - -
-
- Пользователь не найден или пароль указан не верно -
-
- Выход успешно произведен -
-
- Пользователь '' успешно создан -
-
-

Вход

-

Логин

- -

Пароль

- - -
-
- - \ No newline at end of file diff --git a/demo/src/main/resources/templates/order-add.html b/demo/src/main/resources/templates/order-add.html deleted file mode 100644 index e7a699a..0000000 --- a/demo/src/main/resources/templates/order-add.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - - -
-
- -
- - - Назад - -
- -
- - -
-

- -

- -
-

- -
-
- - \ No newline at end of file diff --git a/demo/src/main/resources/templates/order-dop.html b/demo/src/main/resources/templates/order-dop.html deleted file mode 100644 index 04877a5..0000000 --- a/demo/src/main/resources/templates/order-dop.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - -
-
-
- - - -
- -
- -
- - - - - - - - - - - - - - - - -
#IDНазваниеЛицензия
- - - - -
- - Изменить - - -
-
- -
-
-
-
- - \ No newline at end of file diff --git a/demo/src/main/resources/templates/order.html b/demo/src/main/resources/templates/order.html deleted file mode 100644 index 3bbb74f..0000000 --- a/demo/src/main/resources/templates/order.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - - -
- -
- - - - - - - - - - - - - - - -
#IDДата созданияПоставщикПродукты
- - - - -
  • -
    -
    -
    - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/product-edit.html b/demo/src/main/resources/templates/product-edit.html deleted file mode 100644 index 3819f92..0000000 --- a/demo/src/main/resources/templates/product-edit.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -
    -
    -
    - - -
    -
    - - -
    -
    - - - Назад - -
    -
    -
    - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/product.html b/demo/src/main/resources/templates/product.html deleted file mode 100644 index 7add966..0000000 --- a/demo/src/main/resources/templates/product.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - -
    - -
    - - - - - - - - - - - - - - - -
    #IDНазваниеЦена
    - - - - -
    - - Изменить - - -
    -
    - -
    -
    -
    -
    - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/signup.html b/demo/src/main/resources/templates/signup.html deleted file mode 100644 index 5018bd8..0000000 --- a/demo/src/main/resources/templates/signup.html +++ /dev/null @@ -1,29 +0,0 @@ - - - -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    - - Назад -
    -
    -
    - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/supplier-edit.html b/demo/src/main/resources/templates/supplier-edit.html deleted file mode 100644 index a7d8297..0000000 --- a/demo/src/main/resources/templates/supplier-edit.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -
    -
    -
    - - -
    -
    - - -
    -
    - - - Назад - -
    -
    -
    - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/supplier.html b/demo/src/main/resources/templates/supplier.html deleted file mode 100644 index 15f3ccc..0000000 --- a/demo/src/main/resources/templates/supplier.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - -
    - -
    - - - - - - - - - - - - - - - -
    #IDНазваниеЛицензия
    - - - - -
    - - Изменить - - -
    -
    - -
    -
    -
    -
    - - \ No newline at end of file diff --git a/demo/src/main/resources/templates/users.html b/demo/src/main/resources/templates/users.html deleted file mode 100644 index 34c6cfa..0000000 --- a/demo/src/main/resources/templates/users.html +++ /dev/null @@ -1,38 +0,0 @@ - - - -
    -
    - - - - - - - - - - - - - - - - - -
    #IDЛогинРоль
    -
    - -
    - - \ No newline at end of file diff --git a/front/package.json b/front/package.json index 62401d7..00a12cc 100644 --- a/front/package.json +++ b/front/package.json @@ -5,11 +5,11 @@ "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", - "axios": "^1.3.6", - "bootstrap": "^5.2.2", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.10.0", + "react-router-dom": "^6.4.4", + "axios": "^1.1.3", + "bootstrap": "^5.2.2", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/front/src/App.js b/front/src/App.js index ccea127..d2c9423 100644 --- a/front/src/App.js +++ b/front/src/App.js @@ -4,20 +4,32 @@ import CatalogSuppliers from "./Pages/CatalogSuppliers"; import OrderPage from "./Pages/OrdersPage"; import CreateOrderPage from "./Pages/CreateOrderPage"; import Header from "./general/Header"; +import SignUpPage from "./Pages/SignUpPage"; +import LoginPage from "./Pages/LoginPage"; +import PrivateRoutes from './utils/PrivateRoutes'; + function App() { return (
    - - - - - - - - +
    + + }> + + + + + + + }> + {/* } path="/Users"/> */} + + + + +
    ); } diff --git a/front/src/AuthService.js b/front/src/AuthService.js new file mode 100644 index 0000000..650ed8b --- /dev/null +++ b/front/src/AuthService.js @@ -0,0 +1,36 @@ +import axios from "axios"; +import UserSignUp from "./models/UserSignUp"; +import UserLogin from "./models/UserLogin"; + +const API_URL = "http://localhost:8080"; + +const register = (username, password, passwordConfirm) => { + return axios.post(API_URL + "/signup", new UserSignUp({login: username, + password: password, + passwordConfirm: passwordConfirm})); +}; + +const login = (username, password) => { + return axios + .post(API_URL + "/jwt/login", new UserLogin({login: username, password: password})) + .then((response) => { + if(response.status === 200){ + localStorage.setItem("token", response.data.token); + localStorage.setItem("role", response.data.role); + localStorage.setItem("login", response.data.login); + } + }); +}; + +const logout = () => { + localStorage.removeItem("token"); + localStorage.removeItem("role"); + localStorage.removeItem("login"); +}; +const AuthService = { + register, + login, + logout, + }; + +export default AuthService; \ No newline at end of file diff --git a/front/src/DataService.js b/front/src/DataService.js index 4b07418..259f039 100644 --- a/front/src/DataService.js +++ b/front/src/DataService.js @@ -38,6 +38,20 @@ export default class DataService { return transformer(response.data); } + static async readUsersPage(dataUrlPrefix, url, page) { + const response = await axios.get(dataUrlPrefix + url + `?page=${page}`,{ + headers:{ + "Authorization": "Bearer " + localStorage.getItem("token") + } + }); + return response.data; + } + + static async readUser(dataUrlPrefix, url, login){ + const response = await axios.get(dataUrlPrefix + url + `/${login}`); + return response.data; + } + static async create(url, data) { const response = await axios.post(getFullUrl(this.mainUrl + url, data)) const res = response.data @@ -51,28 +65,6 @@ export default class DataService { return true; } - static async getSomeSuppliers(){ - const arr =[ - { - "id":104, - "name":"prod3", - "cost":3333 - }, - { - "id":153, - "name":"prod3", - "cost":5555 - } - ] - - //const resArr = JSON.stringify(arr) - //console.log(resArr) - const response = await axios.post(this.mainUrl + `order/someSuppliers/`, { - body: {arr} - }); - console.log(response) - } - static async update(url, data) { await fetch(getFullUrl(this.mainUrl + url, data), { method: 'PATCH', diff --git a/front/src/Pages/LoginPage.jsx b/front/src/Pages/LoginPage.jsx new file mode 100644 index 0000000..32762e8 --- /dev/null +++ b/front/src/Pages/LoginPage.jsx @@ -0,0 +1,72 @@ +import React, { useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import { useState } from "react"; +import AuthService from "../AuthService" + +export default function LoginPage(props) { + const formRef = React.createRef(); + const navigate = useNavigate(); + const [message, setMessage] = useState(""); + const [state, setState] = useState({ + login: "", + password: "" + }); + const handleChange = (event) => { + setState({ ...state, [event.target.name]: event.target.value}); + }; + const handleSubmit = (event) =>{ + event.preventDefault(); + setMessage(""); + AuthService.login(state.login, state.password).then( + () => { + navigate("/"); + window.location.reload(); + }, + (error) => { + const resMessage = + (error.response && + error.response.data && + error.response.data.message) || + error.message || + error.toString(); + + setMessage(resMessage); + } + ); + } + return ( +
    +
    +
    handleSubmit(e)}> +

    Вход

    +

    Логин

    + handleChange(e)} type="text" required /> +

    Пароль

    + handleChange(e)} + required + /> + +
    + {message && ( +
    +
    + {message} +
    +
    + )} +
    +
    + ); +} diff --git a/front/src/Pages/SignUpPage.jsx b/front/src/Pages/SignUpPage.jsx new file mode 100644 index 0000000..0693787 --- /dev/null +++ b/front/src/Pages/SignUpPage.jsx @@ -0,0 +1,82 @@ +import React, { useState } from "react"; +import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import AuthService from "../AuthService" + +export default function SignUpPage(props) { + const formRef = React.createRef(); + const navigate = useNavigate(); + const [message, setMessage] = useState(""); + const [state, setState] = useState({ + login: "", + password: "", + passwordConfirm: "" + }); + const handleChange = (event) => { + setState({ ...state, [event.target.name]: event.target.value}); + }; + const handleSubmit = (event) =>{ + event.preventDefault(); + setMessage(""); + AuthService.register(state.login, state.password, state.passwordConfirm).then( + () => { + navigate("/Login"); + window.location.reload(); + }, + (error) => { + const resMessage = + (error.response && + error.response.data && + error.response.data.message) || + error.message || + error.toString(); + + setMessage(resMessage); + } + ); + } + return ( +
    +
    +
    handleSubmit(e)}> +

    Вход

    +

    Логин

    + handleChange(e)} type="text" required /> +

    Пароль

    + handleChange(e)} + required + /> +

    Подтверждение пароля

    + handleChange(e)} + required + /> +
    + + + Назад + +
    +
    + {message && ( +
    +
    + {message} +
    +
    + )} +
    +
    + ); +} \ No newline at end of file diff --git a/front/src/general/Header.jsx b/front/src/general/Header.jsx index 22b05f1..e30e735 100644 --- a/front/src/general/Header.jsx +++ b/front/src/general/Header.jsx @@ -1,6 +1,18 @@ -import { Link } from 'react-router-dom'; +import { useState } from "react"; +import { Link } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; +import AuthService from "../AuthService"; export default function Header() { + + const navigate = useNavigate(); + + const handleLogout = () =>{ + AuthService.logout(); + navigate("/Login"); + window.location.reload(); + }; + return ( ); } \ No newline at end of file diff --git a/front/src/models/UserLogin.js b/front/src/models/UserLogin.js new file mode 100644 index 0000000..fb619fd --- /dev/null +++ b/front/src/models/UserLogin.js @@ -0,0 +1,6 @@ +export default class UserLogin { + constructor(data) { + this.login = data?.login; + this.password = data?.password; + } +} \ No newline at end of file diff --git a/front/src/models/UserSignUp.js b/front/src/models/UserSignUp.js new file mode 100644 index 0000000..6a3f9d6 --- /dev/null +++ b/front/src/models/UserSignUp.js @@ -0,0 +1,7 @@ +export default class UserSignUp { + constructor(data) { + this.login = data?.login; + this.password = data?.password; + this.passwordConfirm = data?.passwordConfirm; + } +} \ No newline at end of file diff --git a/front/src/utils/PrivateRoutes.jsx b/front/src/utils/PrivateRoutes.jsx new file mode 100644 index 0000000..2d2a3b0 --- /dev/null +++ b/front/src/utils/PrivateRoutes.jsx @@ -0,0 +1,36 @@ +import { useEffect } from 'react'; +import { Outlet, Navigate, useLocation } from 'react-router-dom' +import DataService from '../DataService'; + +const PrivateRoutes = (props) => { + let location = useLocation() + useEffect(()=>{ + try{ + DataService.readUser("http://localhost:8080","/users",localStorage.getItem("login")).then((data)=>{ + if(data.authorities[0] !== localStorage.getItem("role")){ + throw new SyntaxError("Данные не совпадают") + } + }).catch((e) => { + localStorage.clear(); + window.location.reload(); + console.log(e) + }) + } + catch(e){ + localStorage.clear(); + window.location.reload(); + console.log(e) + } + },[location]) + let getPermission = false; + let userToken = localStorage.getItem("token"); + let userRole = localStorage.getItem("role"); + if(userToken && (props.role === userRole || userRole === "ADMIN")){ + getPermission = true; + } + return( + getPermission ? : + ) +} + +export default PrivateRoutes \ No newline at end of file