-
-
-
-
-
-
{item["price"]}
+ }
+ return (
+
+ {props.items.map((item) => (
+
+
+
+
+
+
+ href="#"
+ type="button"
+ className="btn btn-outline-dark text-center d-flex justify-content-md-center mx-5"
+ data-bs-toggle="modal"
+ data-bs-target="#staticBackdrop"
+ onClick={(e) => edit(item.id, e)}
+ >
+ Изменить
+
+ >
+ )}
+
mess(item)}
+ >
+ в корзину
+
- ))}
-
- );
- }
-
\ No newline at end of file
+
+ ))}
+
+ );
+}
diff --git a/front/src/components/common/Header.jsx b/front/src/components/common/Header.jsx
index a11a900..e27b5ce 100644
--- a/front/src/components/common/Header.jsx
+++ b/front/src/components/common/Header.jsx
@@ -1,35 +1,67 @@
-import { NavLink } from "react-router-dom";
+import {NavLink, useNavigate} from "react-router-dom";
+import {useEffect, useState} from "react";
export default function Header(props) {
- return (
-
+ );
}
diff --git a/front/src/components/common/PrivateRoute.jsx b/front/src/components/common/PrivateRoute.jsx
new file mode 100644
index 0000000..f97ba81
--- /dev/null
+++ b/front/src/components/common/PrivateRoute.jsx
@@ -0,0 +1,45 @@
+import { Outlet, Navigate, useNavigate } from "react-router-dom";
+import { useEffect, useState } from "react";
+export default function PrivateRoute(props) {
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ window.addEventListener("storage", () => {
+ let token = localStorage.getItem("token");
+ if (token) {
+ getRole(token).then((role) => {
+ if (localStorage.getItem("role") != role) {
+ localStorage.removeItem("token");
+ localStorage.removeItem("user");
+ localStorage.removeItem("role");
+ window.dispatchEvent(new Event("storage"));
+ navigate("/catalog/main");
+ }
+ });
+ }
+ });
+ }, []);
+
+ const getRole = async function (token) {
+ const requestParams = {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ };
+ const requestUrl = `http://localhost:8080/user?token=${token}`;
+ const response = await fetch(requestUrl, requestParams);
+ const result = await response.text();
+ return result;
+ };
+
+ let isAllowed = false;
+ let userRole = localStorage.getItem("role");
+ if (
+ props.role === userRole || userRole == "ADMIN"
+ ) {
+ isAllowed = true;
+ }
+
+ return isAllowed ?
:
;
+}
diff --git a/front/src/components/common/TableOrder.jsx b/front/src/components/common/TableOrder.jsx
index f9a5d18..5357045 100644
--- a/front/src/components/common/TableOrder.jsx
+++ b/front/src/components/common/TableOrder.jsx
@@ -44,7 +44,7 @@ export default function TableOrder(props) {
}
}
async function acceptOrder(){
- await DataService.create("/order",{...order, ["price"]:cost, ["status"]: "1"} ).then(data => {
+ await DataService.create("/order",{...order, ["price"]:cost, ["status"]: "1", ["user"]:localStorage.getItem("user")} ).then(data => {
props.setProduct([]);
setCost(0);
});
diff --git a/front/src/components/common/ToolbarProduct.jsx b/front/src/components/common/ToolbarProduct.jsx
index 6c4952c..9e1a3cb 100644
--- a/front/src/components/common/ToolbarProduct.jsx
+++ b/front/src/components/common/ToolbarProduct.jsx
@@ -1,17 +1,21 @@
export default function ToolbarProduct(props) {
- function add() {
- props.onAdd();
- }
- return (
-
-
-
- );
+ function add() {
+ props.onAdd();
}
-
\ No newline at end of file
+ return (
+ <>
+
+
+ {localStorage.getItem("role") == "ADMIN" &&
+
+ }
+
+ >
+ );
+}
diff --git a/front/src/models/Order.js b/front/src/models/Order.js
index d3e2fa7..2f4b720 100644
--- a/front/src/models/Order.js
+++ b/front/src/models/Order.js
@@ -5,5 +5,6 @@ export default class Order {
this.price = data?.price || 0;
this.productDTOList = data?.productDTOList || [];
this.status = data?.status || "0";
+ this.user = data?.user || "";
}
}
\ No newline at end of file
diff --git a/front/src/models/User.js b/front/src/models/User.js
new file mode 100644
index 0000000..deeb2ad
--- /dev/null
+++ b/front/src/models/User.js
@@ -0,0 +1,7 @@
+export default class User {
+ constructor(data) {
+ this.id = data?.id;
+ this.login = data?.login || "";
+ this.role = data?.role || "";
+ }
+ }
\ No newline at end of file
diff --git a/front/src/services/DataService.js b/front/src/services/DataService.js
index e4dd6d3..05903ef 100644
--- a/front/src/services/DataService.js
+++ b/front/src/services/DataService.js
@@ -4,13 +4,32 @@ export default class DataService {
static dataUrlPrefix = 'http://localhost:8080';
static async readAll(url, transformer) {
- const response = await fetch(this.dataUrlPrefix + url);
+ const response = await fetch(this.dataUrlPrefix + url, {headers: {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token")
+ }});
const data = await response.json();
return data.map(item => transformer(item));
}
+ 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 read(url, transformer) {
- const response = await axios.get(this.dataUrlPrefix + url);
+ const response = await axios.get(this.dataUrlPrefix + url,{headers: {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token")
+ }});
return transformer(response.data);
}
@@ -30,6 +49,7 @@ export default class DataService {
method: "PUT",
headers: {
"Content-Type": "application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token")
},
body: JSON.stringify(data),
};
@@ -38,7 +58,23 @@ export default class DataService {
}
static async delete(url) {
- const response = await axios.delete(this.dataUrlPrefix + url);
+ const response = await axios.delete(this.dataUrlPrefix + url,{headers: {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token")
+ }});
return response.data.id;
}
+
+ static async readUser(url, data) {
+ const response = await axios.get(this.dataUrlPrefix + url + `/${data}`);
+ return response.data;
+ }
+ static async readAllOrders(url, transformer) {
+ const response = await fetch(this.dataUrlPrefix + url, {headers: {
+ "Content-Type": "application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token")
+ }});
+ const data = await response.json();
+ return data.map(item => transformer(item));
+ }
}
\ No newline at end of file
diff --git a/src/main/java/ip/labwork/LabworkApplication.java b/src/main/java/ip/labwork/LabworkApplication.java
index d709175..bf1c820 100644
--- a/src/main/java/ip/labwork/LabworkApplication.java
+++ b/src/main/java/ip/labwork/LabworkApplication.java
@@ -2,12 +2,10 @@ package ip.labwork;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
-import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
-@RestController
public class LabworkApplication {
- public static void main(String[] args) {
- SpringApplication.run(LabworkApplication.class, args);
- }
+ public static void main(String[] args) {
+ SpringApplication.run(LabworkApplication.class, args);
+ }
}
diff --git a/src/main/java/ip/labwork/configuration/OpenAPI30Configuration.java b/src/main/java/ip/labwork/configuration/OpenAPI30Configuration.java
new file mode 100644
index 0000000..cd2e737
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/OpenAPI30Configuration.java
@@ -0,0 +1,28 @@
+package ip.labwork.configuration;
+
+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 ip.labwork.configuration.jwt.JwtFilter;
+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/src/main/java/ip/labwork/configuration/PasswordEncoderConfiguration.java b/src/main/java/ip/labwork/configuration/PasswordEncoderConfiguration.java
new file mode 100644
index 0000000..a3253c6
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/PasswordEncoderConfiguration.java
@@ -0,0 +1,14 @@
+package ip.labwork.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Configuration
+public class PasswordEncoderConfiguration {
+ @Bean
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
diff --git a/src/main/java/ip/labwork/configuration/SecurityConfiguration.java b/src/main/java/ip/labwork/configuration/SecurityConfiguration.java
new file mode 100644
index 0000000..7d82080
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/SecurityConfiguration.java
@@ -0,0 +1,92 @@
+package ip.labwork.configuration;
+
+import ip.labwork.configuration.jwt.JwtFilter;
+import ip.labwork.user.controller.UserController;
+import ip.labwork.user.model.UserRole;
+import ip.labwork.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.web.builders.HttpSecurity;
+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
+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.passwordEncoder())
+ .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/src/main/java/ip/labwork/WebConfiguration.java b/src/main/java/ip/labwork/configuration/WebConfiguration.java
similarity index 83%
rename from src/main/java/ip/labwork/WebConfiguration.java
rename to src/main/java/ip/labwork/configuration/WebConfiguration.java
index 29b08a4..7ff6402 100644
--- a/src/main/java/ip/labwork/WebConfiguration.java
+++ b/src/main/java/ip/labwork/configuration/WebConfiguration.java
@@ -1,24 +1,27 @@
-package ip.labwork;
+package ip.labwork.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.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
-import org.springframework.http.HttpStatus;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
class WebConfiguration implements WebMvcConfigurer {
@Override
- public void addCorsMappings(CorsRegistry registry){
+ 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);
diff --git a/src/main/java/ip/labwork/configuration/jwt/JwtException.java b/src/main/java/ip/labwork/configuration/jwt/JwtException.java
new file mode 100644
index 0000000..57119ed
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/jwt/JwtException.java
@@ -0,0 +1,11 @@
+package ip.labwork.configuration.jwt;
+
+public class JwtException extends RuntimeException {
+ public JwtException(Throwable throwable) {
+ super(throwable);
+ }
+
+ public JwtException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/ip/labwork/configuration/jwt/JwtFilter.java b/src/main/java/ip/labwork/configuration/jwt/JwtFilter.java
new file mode 100644
index 0000000..2569c27
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/jwt/JwtFilter.java
@@ -0,0 +1,72 @@
+package ip.labwork.configuration.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import ip.labwork.user.service.UserService;
+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/src/main/java/ip/labwork/configuration/jwt/JwtProperties.java b/src/main/java/ip/labwork/configuration/jwt/JwtProperties.java
new file mode 100644
index 0000000..d61378c
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/jwt/JwtProperties.java
@@ -0,0 +1,27 @@
+package ip.labwork.configuration.jwt;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConfigurationProperties(prefix = "jwt", ignoreInvalidFields = true)
+public class JwtProperties {
+ private String devToken = "";
+ private Boolean isDev = true;
+
+ public String getDevToken() {
+ return devToken;
+ }
+
+ public void setDevToken(String devToken) {
+ this.devToken = devToken;
+ }
+
+ public Boolean isDev() {
+ return isDev;
+ }
+
+ public void setDev(Boolean dev) {
+ isDev = dev;
+ }
+}
diff --git a/src/main/java/ip/labwork/configuration/jwt/JwtProvider.java b/src/main/java/ip/labwork/configuration/jwt/JwtProvider.java
new file mode 100644
index 0000000..f537784
--- /dev/null
+++ b/src/main/java/ip/labwork/configuration/jwt/JwtProvider.java
@@ -0,0 +1,107 @@
+package ip.labwork.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/src/main/java/ip/labwork/shop/controller/ComponentController.java b/src/main/java/ip/labwork/shop/controller/ComponentController.java
index 4cd5eed..9b8ae40 100644
--- a/src/main/java/ip/labwork/shop/controller/ComponentController.java
+++ b/src/main/java/ip/labwork/shop/controller/ComponentController.java
@@ -1,7 +1,9 @@
package ip.labwork.shop.controller;
import ip.labwork.shop.service.ComponentService;
+import ip.labwork.user.model.UserRole;
import jakarta.validation.Valid;
+import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -17,31 +19,37 @@ public class ComponentController {
}
@PostMapping
+ @Secured({UserRole.AsString.ADMIN})
public ComponentDTO createComponent(@RequestBody @Valid ComponentDTO componentDTO) {
return componentService.create(componentDTO);
}
@PutMapping("/{id}")
- public ComponentDTO updateComponent(@PathVariable Long id,@RequestBody @Valid ComponentDTO componentDTO) {
- return componentService.updateComponent(id,componentDTO);
+ @Secured({UserRole.AsString.ADMIN})
+ public ComponentDTO updateComponent(@PathVariable Long id, @RequestBody @Valid ComponentDTO componentDTO) {
+ return componentService.updateComponent(id, componentDTO);
}
@DeleteMapping("/{id}")
+ @Secured({UserRole.AsString.ADMIN})
public ComponentDTO removeComponent(@PathVariable Long id) {
return componentService.deleteComponent(id);
}
@DeleteMapping
+ @Secured({UserRole.AsString.ADMIN})
public void removeAllComponent() {
componentService.deleteAllComponent();
}
@GetMapping("/{id}")
+ @Secured({UserRole.AsString.ADMIN})
public ComponentDTO findComponent(@PathVariable Long id) {
return new ComponentDTO(componentService.findComponent(id));
}
@GetMapping
+ @Secured({UserRole.AsString.ADMIN})
public List findAllComponent() {
return componentService.findAllComponent();
}
diff --git a/src/main/java/ip/labwork/shop/controller/OrderController.java b/src/main/java/ip/labwork/shop/controller/OrderController.java
index a24b2c6..35dc8c8 100644
--- a/src/main/java/ip/labwork/shop/controller/OrderController.java
+++ b/src/main/java/ip/labwork/shop/controller/OrderController.java
@@ -1,7 +1,9 @@
package ip.labwork.shop.controller;
import ip.labwork.shop.service.OrderService;
+import ip.labwork.user.model.UserRole;
import jakarta.validation.Valid;
+import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -10,32 +12,47 @@ import java.util.List;
@RequestMapping("/order")
public class OrderController {
private final OrderService orderService;
+
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
@PostMapping
- public OrderDTO createOrder(@RequestBody @Valid OrderDTO orderDTO){
+ @Secured({UserRole.AsString.USER, UserRole.AsString.ADMIN})
+ public OrderDTO createOrder(@RequestBody @Valid OrderDTO orderDTO) {
return orderService.create(orderDTO);
}
+
@PutMapping("/{id}")
- public OrderDTO updateOrder(@PathVariable Long id, @RequestBody @Valid OrderDTO orderDTO){
+ public OrderDTO updateOrder(@PathVariable Long id, @RequestBody @Valid OrderDTO orderDTO) {
return orderService.update(id, orderDTO);
}
+
@DeleteMapping("/{id}")
- public OrderDTO removeOrder(@PathVariable Long id){
+ public OrderDTO removeOrder(@PathVariable Long id) {
return orderService.deleteOrder(id);
}
+
@DeleteMapping
- public void removeAllOrder(){
+ public void removeAllOrder() {
orderService.deleteAllOrder();
}
+
@GetMapping("/{id}")
- public OrderDTO findOrder(@PathVariable Long id){
+ @Secured({UserRole.AsString.USER, UserRole.AsString.ADMIN})
+ public OrderDTO findOrder(@PathVariable Long id) {
return new OrderDTO(orderService.findOrder(id));
}
+
@GetMapping
- public List findAllOrder(){
+ @Secured({UserRole.AsString.USER, UserRole.AsString.ADMIN})
+ public List findAllOrder() {
return orderService.findAllOrder();
}
+
+ @GetMapping("/all/{login}")
+ @Secured({UserRole.AsString.USER, UserRole.AsString.ADMIN})
+ public List findFiltredOrder(@PathVariable String login) {
+ return orderService.findFiltredOrder(login);
+ }
}
diff --git a/src/main/java/ip/labwork/shop/controller/OrderDTO.java b/src/main/java/ip/labwork/shop/controller/OrderDTO.java
index ef9305d..7fc542b 100644
--- a/src/main/java/ip/labwork/shop/controller/OrderDTO.java
+++ b/src/main/java/ip/labwork/shop/controller/OrderDTO.java
@@ -11,8 +11,11 @@ public class OrderDTO {
private long id;
private Date date = new Date();
private int price;
+ private long user_id;
+ private String user;
private OrderStatus status = OrderStatus.Неизвестен;
private List productDTOList;
+
public OrderDTO(Order order) {
this.id = order.getId();
this.date = order.getDate();
@@ -22,6 +25,7 @@ public class OrderDTO {
.map(y -> new ProductDTO(y.getProduct(), y.getCount()))
.toList();
this.status = Objects.equals(order.getStatus().toString(), "") ? OrderStatus.Неизвестен : order.getStatus();
+ this.user_id = order.getUser_id() == null ? -1 : order.getUser_id();
}
public OrderDTO() {
@@ -55,6 +59,22 @@ public class OrderDTO {
this.status = status;
}
+ public long getUser_id() {
+ return user_id;
+ }
+
+ public void setUser_id(long user_id) {
+ this.user_id = user_id;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
public List getProductDTOList() {
return productDTOList;
}
diff --git a/src/main/java/ip/labwork/shop/controller/ProductController.java b/src/main/java/ip/labwork/shop/controller/ProductController.java
index 4199caf..7a29e78 100644
--- a/src/main/java/ip/labwork/shop/controller/ProductController.java
+++ b/src/main/java/ip/labwork/shop/controller/ProductController.java
@@ -1,7 +1,9 @@
package ip.labwork.shop.controller;
import ip.labwork.shop.service.ProductService;
+import ip.labwork.user.model.UserRole;
import jakarta.validation.Valid;
+import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -16,27 +18,38 @@ public class ProductController {
}
@PostMapping
- public ProductDTO createProduct(@RequestBody @Valid ProductDTO productDTO){
+ @Secured({UserRole.AsString.ADMIN})
+ public ProductDTO createProduct(@RequestBody @Valid ProductDTO productDTO) {
return productService.create(productDTO);
}
+
@PutMapping("/{id}")
- public ProductDTO updateProduct(@PathVariable Long id,@RequestBody @Valid ProductDTO productDTO){
+ @Secured({UserRole.AsString.ADMIN})
+ public ProductDTO updateProduct(@PathVariable Long id, @RequestBody @Valid ProductDTO productDTO) {
return productService.updateProduct(id, productDTO);
}
+
@DeleteMapping("/{id}")
- public ProductDTO removeProduct(@PathVariable Long id){
+ @Secured({UserRole.AsString.ADMIN})
+ public ProductDTO removeProduct(@PathVariable Long id) {
return productService.deleteProduct(id);
}
+
@DeleteMapping
- public void removeAllProduct(){
+ @Secured({UserRole.AsString.ADMIN})
+ public void removeAllProduct() {
productService.deleteAllProduct();
}
+
@GetMapping("/{id}")
- public ProductDTO findProduct(@PathVariable Long id){
+ @Secured({UserRole.AsString.ADMIN})
+ public ProductDTO findProduct(@PathVariable Long id) {
return new ProductDTO(productService.findProduct(id));
}
+
@GetMapping
- public List findAllProduct(){
+ @Secured({UserRole.AsString.ADMIN})
+ public List findAllProduct() {
return productService.findAllProduct();
}
}
diff --git a/src/main/java/ip/labwork/shop/model/Order.java b/src/main/java/ip/labwork/shop/model/Order.java
index 9b0e535..c622757 100644
--- a/src/main/java/ip/labwork/shop/model/Order.java
+++ b/src/main/java/ip/labwork/shop/model/Order.java
@@ -20,16 +20,19 @@ public class Order {
@NotNull(message = "Price can't be null or empty")
@Column(name = "price")
private Integer price;
+ private Long user_id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List products;
private OrderStatus status;
- public Order(){
+
+ public Order() {
}
- public Order(Date date, Integer price, OrderStatus status) {
+ public Order(Date date, Integer price, OrderStatus status, Long user_id) {
this.date = date;
this.price = price;
+ this.user_id = user_id;
this.status = status;
}
@@ -56,6 +59,7 @@ public class Order {
public void setPrice(Integer price) {
this.price = price;
}
+
public List getProducts() {
return products;
}
@@ -64,14 +68,15 @@ public class Order {
this.products = products;
}
- public void addProduct(OrderProducts orderProducts){
- if (products == null){
+ public void addProduct(OrderProducts orderProducts) {
+ if (products == null) {
this.products = new ArrayList<>();
}
if (!products.contains(orderProducts))
this.products.add(orderProducts);
}
- public void removeProducts(OrderProducts orderProducts){
+
+ public void removeProducts(OrderProducts orderProducts) {
if (products.contains(orderProducts))
this.products.remove(orderProducts);
}
@@ -84,11 +89,19 @@ public class Order {
this.status = status;
}
+ public Long getUser_id() {
+ return user_id;
+ }
+
+ public void setUser_id(Long user_id) {
+ this.user_id = user_id;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Order order)) return false;
- return Objects.equals(getId(), order.getId()) && Objects.equals(getDate(), order.getDate()) && Objects.equals(getPrice(), order.getPrice());
+ return Objects.equals(getId(), order.getId()) && Objects.equals(getDate(), order.getDate()) && Objects.equals(getPrice(), order.getPrice()) && Objects.equals(getUser_id(), order.getUser_id()) && Objects.equals(getProducts(), order.getProducts()) && getStatus() == order.getStatus();
}
@Override
diff --git a/src/main/java/ip/labwork/shop/service/OrderService.java b/src/main/java/ip/labwork/shop/service/OrderService.java
index aede5b7..81c3b31 100644
--- a/src/main/java/ip/labwork/shop/service/OrderService.java
+++ b/src/main/java/ip/labwork/shop/service/OrderService.java
@@ -1,9 +1,12 @@
package ip.labwork.shop.service;
import ip.labwork.shop.controller.OrderDTO;
-import ip.labwork.shop.model.*;
+import ip.labwork.shop.model.Order;
+import ip.labwork.shop.model.OrderProducts;
+import ip.labwork.shop.model.Product;
import ip.labwork.shop.repository.OrderRepository;
import ip.labwork.shop.repository.ProductRepository;
+import ip.labwork.user.service.UserService;
import ip.labwork.util.validation.ValidatorUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -14,21 +17,24 @@ import java.util.*;
public class OrderService {
private final OrderRepository orderRepository;
private final ProductRepository productRepository;
+ private final UserService userService;
private final ValidatorUtil validatorUtil;
public OrderService(OrderRepository orderRepository,
- ValidatorUtil validatorUtil, ProductRepository productRepository) {
+ ValidatorUtil validatorUtil, ProductRepository productRepository, UserService userService) {
this.orderRepository = orderRepository;
this.validatorUtil = validatorUtil;
this.productRepository = productRepository;
+ this.userService = userService;
}
+
@Transactional
public OrderDTO create(OrderDTO orderDTO) {
int price = 0;
- for(int i = 0; i < orderDTO.getProductDTOList().size(); i++){
+ for (int i = 0; i < orderDTO.getProductDTOList().size(); i++) {
price += orderDTO.getProductDTOList().get(i).getPrice() * orderDTO.getProductDTOList().get(i).getCount();
}
- final Order order = new Order(new Date(), price, orderDTO.getStatus());
+ final Order order = new Order(new Date(), price, orderDTO.getStatus(), userService.findByLogin(orderDTO.getUser()).getId());
validatorUtil.validate(order);
orderRepository.save(order);
for (int i = 0; i < orderDTO.getProductDTOList().size(); i++) {
@@ -38,15 +44,23 @@ public class OrderService {
orderRepository.save(order);
return new OrderDTO(findOrder(order.getId()));
}
+
@Transactional(readOnly = true)
public Order findOrder(Long id) {
final Optional order = orderRepository.findById(id);
return order.orElseThrow(() -> new OrderNotFoundException(id));
}
+
@Transactional(readOnly = true)
public List findAllOrder() {
return orderRepository.findAll().stream().map(x -> new OrderDTO(x)).toList();
}
+
+ @Transactional(readOnly = true)
+ public List findFiltredOrder(String login) {
+ return orderRepository.findAll().stream().filter(x -> Objects.equals(x.getUser_id(), userService.findByLogin(login).getId())).map(x -> new OrderDTO(x)).toList();
+ }
+
@Transactional
public OrderDTO update(Long id, OrderDTO orderDTO) {
final Order currentOrder = findOrder(id);
@@ -68,8 +82,7 @@ public class OrderService {
product_id = product_id.stream().filter(x -> !Objects.equals(x, newProducts.get(finalI).getId())).toList();
orderProducts.setCount(orderDTO.getProductDTOList().stream().filter(x -> x.getId() == currentId).toList().get(0).getCount());
orderRepository.saveAndFlush(currentOrder);
- }
- else {
+ } else {
final OrderProducts orderProducts = new OrderProducts(currentOrder, newProducts.get(i), orderDTO.getProductDTOList().stream().filter(x -> x.getId() == currentId).toList().get(0).getCount());
currentOrder.addProduct(orderProducts);
orderRepository.saveAndFlush(currentOrder);
@@ -82,6 +95,7 @@ public class OrderService {
orderRepository.saveAndFlush(currentOrder);
return new OrderDTO(currentOrder);
}
+
@Transactional
public OrderDTO deleteOrder(Long id) {
final Order currentOrder = findOrder(id);
@@ -94,6 +108,7 @@ public class OrderService {
orderRepository.delete(currentOrder);
return new OrderDTO(currentOrder);
}
+
@Transactional
public void deleteAllOrder() {
orderRepository.deleteAll();
diff --git a/src/main/java/ip/labwork/user/controller/UserController.java b/src/main/java/ip/labwork/user/controller/UserController.java
new file mode 100644
index 0000000..b0a96f7
--- /dev/null
+++ b/src/main/java/ip/labwork/user/controller/UserController.java
@@ -0,0 +1,64 @@
+package ip.labwork.user.controller;
+
+import ip.labwork.user.model.User;
+import ip.labwork.user.model.UserRole;
+import ip.labwork.user.service.UserService;
+import jakarta.validation.Valid;
+import org.springframework.data.domain.Page;
+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 = "/jwt/signup";
+
+ private final UserService userService;
+
+ public UserController(UserService userService) {
+ this.userService = userService;
+ }
+
+ @PostMapping(URL_LOGIN)
+ public String login(@RequestBody @Valid UserDto userDto) {
+ return userService.loginAndGetToken(userDto);
+ }
+
+ @PostMapping(URL_SIGNUP)
+ public UserInfoDto signup(@RequestBody @Valid UserDto userDto) {
+ return userService.signupAndGetToken(userDto);
+ }
+
+ @GetMapping("/users/{login}")
+ public UserDetails getCurrentUser(@PathVariable String login) {
+ try {
+ return userService.loadUserByUsername(login);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @GetMapping("/user")
+ public String findUser(@RequestParam("token") String token) {
+ UserDetails userDetails = userService.loadUserByToken(token);
+ User user = userService.findByLogin(userDetails.getUsername());
+ return user.getRole().toString();
+ }
+
+ @GetMapping("/users")
+ @Secured({UserRole.AsString.ADMIN})
+ public UsersPageDTO 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 new UsersPageDTO(users, pageNumbers, totalPages);
+ }
+}
diff --git a/src/main/java/ip/labwork/user/controller/UserDto.java b/src/main/java/ip/labwork/user/controller/UserDto.java
new file mode 100644
index 0000000..4c24d42
--- /dev/null
+++ b/src/main/java/ip/labwork/user/controller/UserDto.java
@@ -0,0 +1,44 @@
+package ip.labwork.user.controller;
+
+import ip.labwork.user.model.User;
+import ip.labwork.user.model.UserRole;
+import jakarta.validation.constraints.NotEmpty;
+
+public class UserDto {
+ private long id;
+ @NotEmpty
+ private String login;
+ @NotEmpty
+ private String password;
+ private String passwordConfirm;
+ private UserRole role;
+
+ public UserDto() {
+ }
+
+ public UserDto(User user) {
+ this.id = user.getId();
+ this.login = user.getLogin();
+ this.role = user.getRole();
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getPasswordConfirm() {
+ return passwordConfirm;
+ }
+
+ public UserRole getRole() {
+ return role;
+ }
+}
diff --git a/src/main/java/ip/labwork/user/controller/UserInfoDto.java b/src/main/java/ip/labwork/user/controller/UserInfoDto.java
new file mode 100644
index 0000000..4682ae4
--- /dev/null
+++ b/src/main/java/ip/labwork/user/controller/UserInfoDto.java
@@ -0,0 +1,34 @@
+package ip.labwork.user.controller;
+
+import ip.labwork.user.model.UserRole;
+import jakarta.validation.constraints.NotEmpty;
+
+public class UserInfoDto {
+ @NotEmpty
+ private String token;
+ @NotEmpty
+ private String login;
+ @NotEmpty
+ private UserRole role;
+
+ public UserInfoDto(String token, String login, UserRole role) {
+ this.token = token;
+ this.login = login;
+ this.role = role;
+ }
+
+ public UserInfoDto() {
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public UserRole getRole() {
+ return role;
+ }
+}
diff --git a/src/main/java/ip/labwork/user/controller/UsersPageDTO.java b/src/main/java/ip/labwork/user/controller/UsersPageDTO.java
new file mode 100644
index 0000000..7f32378
--- /dev/null
+++ b/src/main/java/ip/labwork/user/controller/UsersPageDTO.java
@@ -0,0 +1,29 @@
+package ip.labwork.user.controller;
+
+import org.springframework.data.domain.Page;
+
+import java.util.List;
+
+public class UsersPageDTO {
+ private Page users;
+ private List pageNumbers;
+ private int totalPages;
+
+ public UsersPageDTO(Page users, List pageNumbers, int totalPages) {
+ this.users = users;
+ this.pageNumbers = pageNumbers;
+ this.totalPages = totalPages;
+ }
+
+ public Page getUsers() {
+ return users;
+ }
+
+ public List getPageNumbers() {
+ return pageNumbers;
+ }
+
+ public int getTotalPages() {
+ return totalPages;
+ }
+}
diff --git a/src/main/java/ip/labwork/user/model/User.java b/src/main/java/ip/labwork/user/model/User.java
new file mode 100644
index 0000000..d8095b5
--- /dev/null
+++ b/src/main/java/ip/labwork/user/model/User.java
@@ -0,0 +1,83 @@
+package ip.labwork.user.model;
+
+import jakarta.persistence.*;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+import java.util.Objects;
+
+@Entity
+@Table(name = "users")
+public class User {
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ private Long id;
+ @Column(nullable = false, unique = true, length = 64)
+ @NotBlank
+ @Size(min = 3, max = 64)
+ private String login;
+ @Column(nullable = false, length = 64)
+ @NotBlank
+ @Size(min = 6, max = 64)
+ private String password;
+ private UserRole role;
+
+ public User() {
+ }
+
+ public User(String login, String password) {
+ this(login, password, UserRole.USER);
+ }
+
+ public User(String login, String password, UserRole role) {
+ this.login = login;
+ this.password = password;
+ this.role = role;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getLogin() {
+ return login;
+ }
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public UserRole getRole() {
+ return role;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ User user = (User) o;
+ return Objects.equals(id, user.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+
+ @Override
+ public String toString() {
+ return "User{" +
+ "id=" + id +
+ ", login='" + login + '\'' +
+ ", password='" + password + '\'' +
+ '}';
+ }
+}
diff --git a/src/main/java/ip/labwork/user/model/UserRole.java b/src/main/java/ip/labwork/user/model/UserRole.java
new file mode 100644
index 0000000..38d32e6
--- /dev/null
+++ b/src/main/java/ip/labwork/user/model/UserRole.java
@@ -0,0 +1,20 @@
+package ip.labwork.user.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/ip/labwork/user/repository/UserRepository.java b/src/main/java/ip/labwork/user/repository/UserRepository.java
new file mode 100644
index 0000000..8899a68
--- /dev/null
+++ b/src/main/java/ip/labwork/user/repository/UserRepository.java
@@ -0,0 +1,8 @@
+package ip.labwork.user.repository;
+
+import ip.labwork.user.model.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface UserRepository extends JpaRepository {
+ User findOneByLoginIgnoreCase(String login);
+}
diff --git a/src/main/java/ip/labwork/user/service/UserExistsException.java b/src/main/java/ip/labwork/user/service/UserExistsException.java
new file mode 100644
index 0000000..925a055
--- /dev/null
+++ b/src/main/java/ip/labwork/user/service/UserExistsException.java
@@ -0,0 +1,7 @@
+package ip.labwork.user.service;
+
+public class UserExistsException extends RuntimeException {
+ public UserExistsException(String login) {
+ super(String.format("User '%s' already exists", login));
+ }
+}
diff --git a/src/main/java/ip/labwork/user/service/UserNotFoundException.java b/src/main/java/ip/labwork/user/service/UserNotFoundException.java
new file mode 100644
index 0000000..ec188ae
--- /dev/null
+++ b/src/main/java/ip/labwork/user/service/UserNotFoundException.java
@@ -0,0 +1,7 @@
+package ip.labwork.user.service;
+
+public class UserNotFoundException extends RuntimeException {
+ public UserNotFoundException(String login) {
+ super(String.format("User not found '%s'", login));
+ }
+}
diff --git a/src/main/java/ip/labwork/user/service/UserService.java b/src/main/java/ip/labwork/user/service/UserService.java
new file mode 100644
index 0000000..540f6bc
--- /dev/null
+++ b/src/main/java/ip/labwork/user/service/UserService.java
@@ -0,0 +1,99 @@
+package ip.labwork.user.service;
+
+import ip.labwork.configuration.jwt.JwtException;
+import ip.labwork.configuration.jwt.JwtProvider;
+import ip.labwork.user.controller.UserDto;
+import ip.labwork.user.controller.UserInfoDto;
+import ip.labwork.user.model.User;
+import ip.labwork.user.model.UserRole;
+import ip.labwork.user.repository.UserRepository;
+import ip.labwork.util.validation.ValidationException;
+import ip.labwork.util.validation.ValidatorUtil;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.Objects;
+
+@Service
+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,
+ JwtProvider jwtProvider) {
+ this.userRepository = userRepository;
+ this.passwordEncoder = passwordEncoder;
+ this.validatorUtil = validatorUtil;
+ this.jwtProvider = jwtProvider;
+ }
+
+ public Page findAllPages(int page, int size) {
+ return userRepository.findAll(PageRequest.of(page - 1, size, Sort.by("id").ascending()));
+ }
+
+ public User findByLogin(String login) {
+ return userRepository.findOneByLoginIgnoreCase(login);
+ }
+
+ public User createUser(String login, String password, String passwordConfirm) {
+ return createUser(login, password, passwordConfirm, UserRole.USER);
+ }
+
+ public User createUser(String login, String password, String passwordConfirm, UserRole role) {
+ if (findByLogin(login) != null) {
+ throw new UserExistsException(login);
+ }
+ final User user = new User(login, passwordEncoder.encode(password), role);
+ validatorUtil.validate(user);
+ if (!Objects.equals(password, passwordConfirm)) {
+ throw new ValidationException("Passwords not equals");
+ }
+ return userRepository.save(user);
+ }
+
+ public String loginAndGetToken(UserDto 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 jwtProvider.generateToken(user.getLogin());
+ }
+
+ public UserInfoDto signupAndGetToken(UserDto userDto) {
+ final User user = createUser(userDto.getLogin(), userDto.getPassword(), userDto.getPasswordConfirm(), UserRole.USER);
+ return new UserInfoDto(jwtProvider.generateToken(user.getLogin()), user.getLogin(), UserRole.USER);
+ }
+
+ 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);
+ if (userEntity == null) {
+ throw new UsernameNotFoundException(username);
+ }
+ return new org.springframework.security.core.userdetails.User(
+ userEntity.getLogin(), userEntity.getPassword(), Collections.singleton(userEntity.getRole()));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ip/labwork/util/validation/ValidationException.java b/src/main/java/ip/labwork/util/validation/ValidationException.java
index a1e6e3d..c4a670c 100644
--- a/src/main/java/ip/labwork/util/validation/ValidationException.java
+++ b/src/main/java/ip/labwork/util/validation/ValidationException.java
@@ -3,6 +3,10 @@ package ip.labwork.util.validation;
import java.util.Set;
public class ValidationException extends RuntimeException {
+ public ValidationException(String message) {
+ super(message);
+ }
+
public ValidationException(Set errors) {
super(String.join("\n", errors));
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 14d2c47..cd217cd 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -8,4 +8,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