diff --git a/Frontend/vue-project/src/components/CatalogCollections.vue b/Frontend/vue-project/src/components/CatalogCollections.vue
index bd13872..bc3d0ed 100644
--- a/Frontend/vue-project/src/components/CatalogCollections.vue
+++ b/Frontend/vue-project/src/components/CatalogCollections.vue
@@ -19,14 +19,46 @@
data() {
return{
collections: [],
- URL: "http://localhost:8080/",
+ URL: "http://localhost:8080/api/1.0/",
collection: new Collection(),
- films: []
+ films: [],
+ postParams: {
+ method:"POST",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ },
+ putParams: {
+ method:"PUT",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ },
+ },
+ delParams: {
+ method:"DELETE",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ },
+ getParams: {
+ method:"GET",
+ headers:{
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ }
+ }
+ },
+ beforeCreate() {
+ if (localStorage.getItem("token") == null) {
+ this.$router.push("/login");
}
},
methods: {
getCollections(){
- axios.get(this.URL + "collection")
+ axios.get(this.URL + "collection", this.getParams)
.then(response => {
this.collections = response.data;
})
@@ -36,7 +68,7 @@
},
addCollection(collection){
console.log(collection);
- axios.post(this.URL + "collection", collection)
+ axios.post(this.URL + "collection", collection, this.postParams)
.then(() => {
this.getCollections();
this.closeModal();
@@ -46,13 +78,13 @@
});
},
deleteCollection(id){
- axios.delete(this.URL + `collection/${id}`)
+ axios.delete(this.URL + `collection/${id}`, this.delParams)
.then(() =>{
this.getCollections();
})
},
editCollection(collection){
- axios.put(this.URL + `collection/${collection.id}`, collection)
+ axios.put(this.URL + `collection/${collection.id}`, collection, this.putParams)
.then(() =>{
const index = this.collections.findIndex((s) => s.id === collection.id);
if (index !== -1) {
@@ -91,7 +123,7 @@
document.getElementById("editModal").style.display = "none";
},
getFilms(){
- axios.get(this.URL + "film")
+ axios.get(this.URL + "film", this.getParams)
.then(response => {
this.films = response.data;
console.log(response.data);
@@ -100,11 +132,9 @@
console.log(error);
});
},
-
addCollectionFilm(id) {
let filmId = document.getElementById('films').value;
- axios
- .post(this.URL + `collection/add_film/${id}?film_id=${filmId}`)
+ axios.post(this.URL + `collection/add_film/${id}`, filmId, this.postParams)
.then(() => {
this.closeModalForAdd();
this.getCollections();
@@ -115,8 +145,7 @@
},
delCollectionFilm(id) {
let filmId = document.getElementById('films').value;
- axios
- .delete(this.URL + `collection/del_film/${id}?film_id=${filmId}`)
+ axios.delete(this.URL + `collection/del_film/${id}?film_id=${filmId}`, this.delParams)
.then(() => {
this.closeModalForAdd();
this.getCollections();
diff --git a/Frontend/vue-project/src/components/CatalogFilms.vue b/Frontend/vue-project/src/components/CatalogFilms.vue
index 827fa9f..027d3b0 100644
--- a/Frontend/vue-project/src/components/CatalogFilms.vue
+++ b/Frontend/vue-project/src/components/CatalogFilms.vue
@@ -19,16 +19,48 @@
data() {
return{
films: [],
- URL: "http://localhost:8080/",
+ URL: "http://localhost:8080/api/1.0/",
film: new Film(),
genres: [],
selectedGenres: [],
- open: []
+ open: [],
+ postParams: {
+ method:"POST",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ },
+ putParams: {
+ method:"PUT",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ },
+ },
+ delParams: {
+ method:"DELETE",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ },
+ getParams: {
+ method:"GET",
+ headers:{
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ }
+ }
+ },
+ beforeCreate() {
+ if (localStorage.getItem("token") == null) {
+ this.$router.push("/login");
}
},
methods: {
getFilms(){
- axios.get(this.URL + "film")
+ axios.get(this.URL + "film", this.getParams)
.then(response => {
this.films = response.data;
})
@@ -38,7 +70,7 @@
},
addFilm(film){
console.log(film);
- axios.post(this.URL + "film", film)
+ axios.post(this.URL + "film", film, this.postParams)
.then(() => {
this.getFilms();
this.closeModal();
@@ -48,13 +80,13 @@
});
},
deleteFilm(id){
- axios.delete(this.URL + `film/${id}`)
+ axios.delete(this.URL + `film/${id}`, this.delParams)
.then(() =>{
this.getFilms();
})
},
editFilm(film){
- axios.put(this.URL + `film/${film.id}`, film)
+ axios.put(this.URL + `film/${film.id}`, film, this.putParams)
.then(() =>{
const index = this.films.findIndex((s) => s.id === film.id);
if (index !== -1) {
@@ -97,7 +129,7 @@
document.getElementById("editModal").style.display = "none";
},
getGenres(){
- axios.get(this.URL + "genre")
+ axios.get(this.URL + "genre", this.getParams)
.then(response => {
this.genres = response.data;
console.log(response.data);
@@ -116,7 +148,7 @@
addFilmGenre(id, list) {
axios
- .post(this.URL + `film/add_genres/${id}`, list)
+ .post(this.URL + `film/add_genres/${id}`, list, this.postParams)
.then(() => {
this.closeModalForAdd();
this.getFilms();
diff --git a/Frontend/vue-project/src/components/CatalogGenres.vue b/Frontend/vue-project/src/components/CatalogGenres.vue
index 2412d6e..5851796 100644
--- a/Frontend/vue-project/src/components/CatalogGenres.vue
+++ b/Frontend/vue-project/src/components/CatalogGenres.vue
@@ -15,14 +15,46 @@
data() {
return {
genres: [],
- URL: "http://localhost:8080/",
+ URL: "http://localhost:8080/api/1.0/",
genre: new Genre(),
- editedGenre: new Genre()
+ editedGenre: new Genre(),
+ postParams: {
+ method:"POST",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ },
+ putParams: {
+ method:"PUT",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ },
+ },
+ delParams: {
+ method:"DELETE",
+ headers:{
+ "Content-Type":"application/json",
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ },
+ getParams: {
+ method:"GET",
+ headers:{
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ }
+ }
+ },
+ beforeCreate() {
+ if (localStorage.getItem("token") == null) {
+ this.$router.push("/login");
}
},
methods: {
getGenres(){
- axios.get(this.URL + "genre")
+ axios.get(this.URL + "genre", this.getParams)
.then(response => {
this.genres = response.data;
console.log(response.data);
@@ -34,7 +66,7 @@
addGenre(genre) {
console.log(genre);
axios
- .post(this.URL + "genre", genre)
+ .post(this.URL + "genre", genre, this.postParams)
.then(() => {
this.getGenres();
this.closeModal();
@@ -44,7 +76,7 @@
});
},
deleteGenre(id){
- axios.delete(this.URL + `genre/${id}`)
+ axios.delete(this.URL + `genre/${id}`, this.delParams)
.then(() =>{
this.getGenres();
})
@@ -64,7 +96,7 @@
document.getElementById("editModal").style.display = "none";
},
editGenre(genre) {
- axios.put(this.URL + `genre/${genre.id}`, genre)
+ axios.put(this.URL + `genre/${genre.id}`, genre, this.putParams)
.then(() => {
const index = this.genres.findIndex((s) => s.id === genre.id);
if (index !== -1) {
diff --git a/Frontend/vue-project/src/components/Catalogs.vue b/Frontend/vue-project/src/components/Catalogs.vue
deleted file mode 100644
index 427bb95..0000000
--- a/Frontend/vue-project/src/components/Catalogs.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
- {{ catalog.label }}
-
-
-
\ No newline at end of file
diff --git a/Frontend/vue-project/src/components/DataTable.vue b/Frontend/vue-project/src/components/DataTable.vue
deleted file mode 100644
index 0706ab3..0000000
--- a/Frontend/vue-project/src/components/DataTable.vue
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
- #
- {{ header.label }}
-
-
-
-
- {{ index + 1 }}
-
- {{ item[header.name] }}
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Frontend/vue-project/src/components/Header.vue b/Frontend/vue-project/src/components/Header.vue
index 9275459..b74bf60 100644
--- a/Frontend/vue-project/src/components/Header.vue
+++ b/Frontend/vue-project/src/components/Header.vue
@@ -23,6 +23,12 @@
Отчет
+
+ Пользователи
+
+
+ Выход
+
@@ -34,4 +40,15 @@
header{
font-size: 28px;
}
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/Frontend/vue-project/src/components/Index.vue b/Frontend/vue-project/src/components/Index.vue
index 6f22571..d3f2f19 100644
--- a/Frontend/vue-project/src/components/Index.vue
+++ b/Frontend/vue-project/src/components/Index.vue
@@ -11,6 +11,11 @@ export default{
banner: "/src/images/banner1.jpg"
}
},
+ beforeCreate() {
+ if (localStorage.getItem("token") == null) {
+ this.$router.push("/login");
+ }
+ },
methods: {
change(){
this.banner = this.images[this.count].image;
diff --git a/Frontend/vue-project/src/components/Login.vue b/Frontend/vue-project/src/components/Login.vue
new file mode 100644
index 0000000..0f45573
--- /dev/null
+++ b/Frontend/vue-project/src/components/Login.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+ Регистрация
+
+
\ No newline at end of file
diff --git a/Frontend/vue-project/src/components/Report.vue b/Frontend/vue-project/src/components/Report.vue
index e34b8ac..2729018 100644
--- a/Frontend/vue-project/src/components/Report.vue
+++ b/Frontend/vue-project/src/components/Report.vue
@@ -12,14 +12,25 @@
data() {
return{
films: [],
- URL: "http://localhost:8080/",
+ URL: "http://localhost:8080/api/1.0/",
genres: [],
- genreId: undefined
+ genreId: undefined,
+ getParams: {
+ method:"GET",
+ headers:{
+ "Authorization": "Bearer " + localStorage.getItem("token"),
+ }
+ }
+ }
+ },
+ beforeCreate() {
+ if (localStorage.getItem("token") == null) {
+ this.$router.push("/login");
}
},
methods: {
getFilms(){
- axios.get(this.URL + "film")
+ axios.get(this.URL + "film", this.getParams)
.then(response => {
this.films = response.data;
})
@@ -28,7 +39,7 @@
});
},
getGenres(){
- axios.get(this.URL + "genre")
+ axios.get(this.URL + "genre", this.getParams)
.then(response => {
this.genres = response.data;
console.log(response.data);
@@ -39,7 +50,7 @@
},
filter() {
let genreId = document.getElementById('genreFilterSelect').value;
- axios.get(this.URL + `genre/film/${genreId}`)
+ axios.get(this.URL + `genre/film/${genreId}`, this.getParams)
.then(response => {
this.films = response.data;
})
diff --git a/Frontend/vue-project/src/components/Signup.vue b/Frontend/vue-project/src/components/Signup.vue
new file mode 100644
index 0000000..4fde2eb
--- /dev/null
+++ b/Frontend/vue-project/src/components/Signup.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Frontend/vue-project/src/components/Users.vue b/Frontend/vue-project/src/components/Users.vue
new file mode 100644
index 0000000..8581e4d
--- /dev/null
+++ b/Frontend/vue-project/src/components/Users.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+ ID
+ Логин
+ Роль
+
+
+
+
+ {{ user.id }}
+ {{ user.login }}
+ {{ user.role }}
+
+
+
+
+
+
Эта страница доступна только администраторам!
+
+
\ No newline at end of file
diff --git a/Frontend/vue-project/src/main.js b/Frontend/vue-project/src/main.js
index 41edc9e..25c887e 100644
--- a/Frontend/vue-project/src/main.js
+++ b/Frontend/vue-project/src/main.js
@@ -6,6 +6,9 @@ import CatalogGenres from './components/CatalogGenres.vue'
import CatalogFilms from './components/CatalogFilms.vue'
import CatalogCollections from './components/CatalogCollections.vue'
import Report from './components/Report.vue'
+import Users from './components/Users.vue'
+import Login from './components/Login.vue'
+import Signup from './components/Signup.vue'
const routes = [
{ path: '/', redirect: '/index' },
@@ -13,7 +16,10 @@ const routes = [
{ path: '/catalogs/genres', component: CatalogGenres},
{ path: '/catalogs/films', component: CatalogFilms},
{ path: '/catalogs/collections', component: CatalogCollections},
- { path: '/report', component: Report}
+ { path: '/report', component: Report},
+ { path: '/users', component: Users},
+ { path: '/login', component: Login},
+ { path: '/signup', component: Signup}
]
const router = createRouter({
diff --git a/Frontend/vue-project/src/models/User.js b/Frontend/vue-project/src/models/User.js
new file mode 100644
index 0000000..bd4d867
--- /dev/null
+++ b/Frontend/vue-project/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/build.gradle b/build.gradle
index b743061..96b4adc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,11 +12,21 @@ repositories {
mavenCentral()
}
+jar {
+ enabled = false
+}
+
dependencies {
+ annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
+
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'com.h2database:h2:2.1.210'
+
+ implementation 'org.springframework.boot:spring-boot-starter-security'
+ implementation 'com.auth0:java-jwt:4.4.0'
+
implementation 'org.hibernate.validator:hibernate-validator'
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.5'
diff --git a/data.mv.db b/data.mv.db
index 195bd6e..ebed62d 100644
Binary files a/data.mv.db and b/data.mv.db differ
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/Repository/IUserRepository.java b/src/main/java/ru/ulstu/is/lab1/DataBase/Repository/IUserRepository.java
new file mode 100644
index 0000000..d114795
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/Repository/IUserRepository.java
@@ -0,0 +1,8 @@
+package ru.ulstu.is.lab1.DataBase.Repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import ru.ulstu.is.lab1.DataBase.model.User;
+
+public interface IUserRepository extends JpaRepository {
+ User findOneByLoginIgnoreCase(String login);
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/OpenAPI30Configuration.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/OpenAPI30Configuration.java
new file mode 100644
index 0000000..500a7a6
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/OpenAPI30Configuration.java
@@ -0,0 +1,28 @@
+package ru.ulstu.is.lab1.DataBase.configuration;
+
+import ru.ulstu.is.lab1.DataBase.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/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/PasswordEncoderConfiguration.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/PasswordEncoderConfiguration.java
new file mode 100644
index 0000000..c4d7130
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/PasswordEncoderConfiguration.java
@@ -0,0 +1,14 @@
+package ru.ulstu.is.lab1.DataBase.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/ru/ulstu/is/lab1/DataBase/configuration/SecurityConfiguration.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/SecurityConfiguration.java
new file mode 100644
index 0000000..f8acd6a
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/SecurityConfiguration.java
@@ -0,0 +1,74 @@
+package ru.ulstu.is.lab1.DataBase.configuration;
+
+import ru.ulstu.is.lab1.DataBase.configuration.jwt.JwtFilter;
+import ru.ulstu.is.lab1.DataBase.controller.UserController;
+import ru.ulstu.is.lab1.DataBase.controller.UserSignupController;
+import ru.ulstu.is.lab1.DataBase.model.UserRole;
+import ru.ulstu.is.lab1.DataBase.service.UserService;
+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.EnableMethodSecurity;
+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;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity(securedEnabled = true)
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+ 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);
+ }
+ }
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ log.info("Creating security configuration");
+ http.cors()
+ .and()
+ .csrf().disable()
+ .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and()
+ .authorizeRequests()
+ .antMatchers("/", SPA_URL_MASK).permitAll()
+ .antMatchers(HttpMethod.POST, UserController.URL_LOGIN).permitAll()
+ .antMatchers(HttpMethod.POST, UserSignupController.URL_LOGIN).permitAll()
+ .anyRequest()
+ .authenticated()
+ .and()
+ .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
+ .anonymous();
+ }
+ @Override
+ protected void configure(AuthenticationManagerBuilder builder) throws Exception {
+ builder.userDetailsService(userService);
+ }
+ @Override
+ public void configure(WebSecurity web) {
+ web.ignoring()
+ .antMatchers(HttpMethod.OPTIONS, "/**")
+ .antMatchers("/**/*.{js,html,css,png,jpg}")
+ .antMatchers("/swagger-ui/index.html")
+ .antMatchers("/webjars/**")
+ .antMatchers("/swagger-resources/**")
+ .antMatchers("/v3/api-docs/**")
+ .antMatchers("/h2-console/**");
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/WebConfiguration.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/WebConfiguration.java
new file mode 100644
index 0000000..c1bfa11
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/WebConfiguration.java
@@ -0,0 +1,28 @@
+package ru.ulstu.is.lab1.DataBase.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.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+class WebConfiguration implements WebMvcConfigurer {
+ @Override
+ public void addViewControllers(ViewControllerRegistry registry) {
+ registry.addViewController(SecurityConfiguration.SPA_URL_MASK).setViewName("forward:/");
+ registry.addViewController("/notFound").setViewName("forward:/");
+ }
+ @Bean
+ public WebServerFactoryCustomizer containerCustomizer() {
+ return container -> container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notFound"));
+ }
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**").allowedMethods("*");
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtException.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtException.java
new file mode 100644
index 0000000..6442daf
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtException.java
@@ -0,0 +1,10 @@
+package ru.ulstu.is.lab1.DataBase.configuration.jwt;
+
+public class JwtException extends RuntimeException {
+ public JwtException(Throwable throwable) {
+ super(throwable);
+ }
+ public JwtException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtFilter.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtFilter.java
new file mode 100644
index 0000000..a83e6b7
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtFilter.java
@@ -0,0 +1,67 @@
+package ru.ulstu.is.lab1.DataBase.configuration.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import ru.ulstu.is.lab1.DataBase.service.UserService;
+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 javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class JwtFilter extends GenericFilterBean {
+ private static final String AUTHORIZATION = "Authorization";
+ public static final String TOKEN_BEGIN_STR = "Bearer ";
+ private final UserService userService;
+ public JwtFilter(UserService userService) {
+ this.userService = userService;
+ }
+ private String getTokenFromRequest(HttpServletRequest request) {
+ String bearer = request.getHeader(AUTHORIZATION);
+ if (StringUtils.hasText(bearer) && bearer.startsWith(TOKEN_BEGIN_STR)) {
+ return bearer.substring(TOKEN_BEGIN_STR.length());
+ }
+ return null;
+ }
+ private void raiseException(ServletResponse response, int status, String message) throws IOException {
+ if (response instanceof final HttpServletResponse httpResponse) {
+ httpResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ httpResponse.setStatus(status);
+ final byte[] body = new ObjectMapper().writeValueAsBytes(message);
+ response.getOutputStream().write(body);
+ }
+ }
+ @Override
+ public void doFilter(ServletRequest request,
+ ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ if (request instanceof final HttpServletRequest httpRequest) {
+ final String token = getTokenFromRequest(httpRequest);
+ if (StringUtils.hasText(token)) {
+ try {
+ final UserDetails user = userService.loadUserByToken(token);
+ final UsernamePasswordAuthenticationToken auth =
+ new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
+ SecurityContextHolder.getContext().setAuthentication(auth);
+ } catch (JwtException e) {
+ raiseException(response, HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
+ return;
+ } catch (Exception e) {
+ e.printStackTrace();
+ raiseException(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ String.format("Internal error: %s", e.getMessage()));
+ return;
+ }
+ }
+ }
+ chain.doFilter(request, response);
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtProperties.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtProperties.java
new file mode 100644
index 0000000..7eaf614
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtProperties.java
@@ -0,0 +1,27 @@
+package ru.ulstu.is.lab1.DataBase.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;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtProvider.java b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtProvider.java
new file mode 100644
index 0000000..d23ef98
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/configuration/jwt/JwtProvider.java
@@ -0,0 +1,99 @@
+package ru.ulstu.is.lab1.DataBase.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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/CollectionController.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/CollectionController.java
index 1841c54..ee7666f 100644
--- a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/CollectionController.java
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/CollectionController.java
@@ -1,13 +1,14 @@
package ru.ulstu.is.lab1.DataBase.controller;
import org.springframework.web.bind.annotation.*;
+import ru.ulstu.is.lab1.DataBase.configuration.OpenAPI30Configuration;
import ru.ulstu.is.lab1.DataBase.service.CollectionService;
import javax.validation.Valid;
import java.util.List;
@RestController
-@RequestMapping("/collection")
+@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/collection")
public class CollectionController {
private final CollectionService collectionService;
@@ -45,7 +46,7 @@ public class CollectionController {
}
@PostMapping("/add_film/{id}")
- public CollectionDTO addFilm(@PathVariable Long id, @RequestParam Long film_id) {
+ public CollectionDTO addFilm(@PathVariable Long id, @RequestBody @Valid Long film_id) {
return new CollectionDTO(collectionService.addFilm(id, film_id));
}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/FilmController.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/FilmController.java
index 63f6b6c..ff3976e 100644
--- a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/FilmController.java
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/FilmController.java
@@ -1,13 +1,14 @@
package ru.ulstu.is.lab1.DataBase.controller;
import org.springframework.web.bind.annotation.*;
+import ru.ulstu.is.lab1.DataBase.configuration.OpenAPI30Configuration;
import ru.ulstu.is.lab1.DataBase.service.FilmService;
import javax.validation.Valid;
import java.util.List;
@RestController
-@RequestMapping("/film")
+@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/film")
public class FilmController {
private final FilmService filmService;
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java
index d97f699..639535d 100644
--- a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/GenreController.java
@@ -1,13 +1,14 @@
package ru.ulstu.is.lab1.DataBase.controller;
import org.springframework.web.bind.annotation.*;
+import ru.ulstu.is.lab1.DataBase.configuration.OpenAPI30Configuration;
import ru.ulstu.is.lab1.DataBase.service.GenreService;
import javax.validation.Valid;
import java.util.List;
@RestController
-@RequestMapping("/genre")
+@RequestMapping(OpenAPI30Configuration.API_PREFIX + "/genre")
public class GenreController {
private final GenreService genreService;
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserController.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserController.java
new file mode 100644
index 0000000..c5d6d6f
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserController.java
@@ -0,0 +1,29 @@
+package ru.ulstu.is.lab1.DataBase.controller;
+
+import ru.ulstu.is.lab1.DataBase.configuration.OpenAPI30Configuration;
+import ru.ulstu.is.lab1.DataBase.model.User;
+import ru.ulstu.is.lab1.DataBase.service.UserService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@RestController
+public class UserController {
+ public static final String URL_LOGIN = "/jwt/login";
+ private final UserService userService;
+ public UserController(UserService userService) {
+ this.userService = userService;
+ }
+ @GetMapping(OpenAPI30Configuration.API_PREFIX + "/user")
+ public List getUsers() {
+ return userService.findAllUsers();
+ }
+ @PostMapping(URL_LOGIN)
+ public String login(@RequestBody @Valid UserDTO userDTO) {
+ return userService.loginAndGetToken(userDTO);
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserDTO.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserDTO.java
new file mode 100644
index 0000000..15fd961
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserDTO.java
@@ -0,0 +1,23 @@
+package ru.ulstu.is.lab1.DataBase.controller;
+
+import ru.ulstu.is.lab1.DataBase.model.User;
+
+import javax.validation.constraints.NotEmpty;
+
+public class UserDTO {
+ @NotEmpty
+ private String login;
+ @NotEmpty
+ private String password;
+ public UserDTO() {}
+ UserDTO(User user) {
+ login = user.getLogin();
+ password = user.getPassword();
+ }
+ public String getLogin() {
+ return login;
+ }
+ public String getPassword() {
+ return password;
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserSignupController.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserSignupController.java
new file mode 100644
index 0000000..7db7116
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserSignupController.java
@@ -0,0 +1,31 @@
+package ru.ulstu.is.lab1.DataBase.controller;
+
+import ru.ulstu.is.lab1.DataBase.model.User;
+import ru.ulstu.is.lab1.DataBase.service.UserService;
+import ru.ulstu.is.lab1.util.validation.ValidationException;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+
+@Controller
+@RestController
+public class UserSignupController {
+ public static final String URL_LOGIN = "/signup";
+ private final UserService userService;
+ public UserSignupController(UserService userService) {
+ this.userService = userService;
+ }
+ @PostMapping(URL_LOGIN)
+ public String signup(@RequestBody @Valid UserSignupDTO userSignupDTO) {
+ try {
+ final User user = userService.createUser(
+ userSignupDTO.getLogin(), userSignupDTO.getPassword(), userSignupDTO.getPasswordConfirm());
+ return user.getLogin();
+ } catch (ValidationException e) {
+ return "error";
+ }
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserSignupDTO.java b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserSignupDTO.java
new file mode 100644
index 0000000..0c449fc
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/controller/UserSignupDTO.java
@@ -0,0 +1,34 @@
+package ru.ulstu.is.lab1.DataBase.controller;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+
+public class UserSignupDTO {
+ @NotBlank
+ @Size(min = 3, max = 64)
+ private String login;
+ @NotBlank
+ @Size(min = 6, max = 64)
+ private String password;
+ @NotBlank
+ @Size(min = 6, max = 64)
+ private String passwordConfirm;
+ 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 String getPasswordConfirm() {
+ return passwordConfirm;
+ }
+ public void setPasswordConfirm(String passwordConfirm) {
+ this.passwordConfirm = passwordConfirm;
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/model/User.java b/src/main/java/ru/ulstu/is/lab1/DataBase/model/User.java
new file mode 100644
index 0000000..3f7cecd
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/model/User.java
@@ -0,0 +1,70 @@
+package ru.ulstu.is.lab1.DataBase.model;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotBlank;
+import javax.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/ru/ulstu/is/lab1/DataBase/model/UserRole.java b/src/main/java/ru/ulstu/is/lab1/DataBase/model/UserRole.java
new file mode 100644
index 0000000..8788472
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/model/UserRole.java
@@ -0,0 +1,17 @@
+package ru.ulstu.is.lab1.DataBase.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/ru/ulstu/is/lab1/DataBase/service/UserExistsException.java b/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserExistsException.java
new file mode 100644
index 0000000..1105976
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserExistsException.java
@@ -0,0 +1,7 @@
+package ru.ulstu.is.lab1.DataBase.service;
+
+public class UserExistsException extends RuntimeException {
+ public UserExistsException(String login) {
+ super(String.format("User '%s' already exists", login));
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserNotFoundException.java b/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserNotFoundException.java
new file mode 100644
index 0000000..ffa8788
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserNotFoundException.java
@@ -0,0 +1,7 @@
+package ru.ulstu.is.lab1.DataBase.service;
+
+public class UserNotFoundException extends RuntimeException {
+ public UserNotFoundException(String login) {
+ super(String.format("User not found '%s'", login));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserService.java b/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserService.java
new file mode 100644
index 0000000..7def773
--- /dev/null
+++ b/src/main/java/ru/ulstu/is/lab1/DataBase/service/UserService.java
@@ -0,0 +1,91 @@
+package ru.ulstu.is.lab1.DataBase.service;
+
+import ru.ulstu.is.lab1.DataBase.configuration.jwt.JwtException;
+import ru.ulstu.is.lab1.DataBase.configuration.jwt.JwtProvider;
+import ru.ulstu.is.lab1.DataBase.controller.UserDTO;
+import ru.ulstu.is.lab1.DataBase.model.User;
+import ru.ulstu.is.lab1.DataBase.model.UserRole;
+import ru.ulstu.is.lab1.DataBase.service.UserExistsException;
+import ru.ulstu.is.lab1.DataBase.service.UserNotFoundException;
+import ru.ulstu.is.lab1.DataBase.Repository.IUserRepository;
+import ru.ulstu.is.lab1.util.validation.ValidationException;
+import ru.ulstu.is.lab1.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.List;
+import java.util.Objects;
+
+@Service
+public class UserService implements UserDetailsService {
+ private final IUserRepository userRepository;
+ private final PasswordEncoder passwordEncoder;
+ private final ValidatorUtil validatorUtil;
+ private final JwtProvider jwtProvider;
+ public UserService(IUserRepository 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 List findAllUsers() {
+ return userRepository.findAll();
+ }
+ 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 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()));
+ }
+}
diff --git a/src/main/java/ru/ulstu/is/lab1/WebConfiguration.java b/src/main/java/ru/ulstu/is/lab1/WebConfiguration.java
deleted file mode 100644
index f847c1d..0000000
--- a/src/main/java/ru/ulstu/is/lab1/WebConfiguration.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package ru.ulstu.is.lab1;
-
-import org.springframework.context.annotation.Configuration;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-@Configuration
-class WebConfiguration implements WebMvcConfigurer {
- @Override
- public void addCorsMappings(CorsRegistry registry){
- registry.addMapping("/**").allowedMethods("*");
- }
-}
diff --git a/src/main/java/ru/ulstu/is/lab1/util/validation/ValidationException.java b/src/main/java/ru/ulstu/is/lab1/util/validation/ValidationException.java
index 293032e..225a9e7 100644
--- a/src/main/java/ru/ulstu/is/lab1/util/validation/ValidationException.java
+++ b/src/main/java/ru/ulstu/is/lab1/util/validation/ValidationException.java
@@ -3,6 +3,9 @@ package ru.ulstu.is.lab1.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 ccc05e8..cd217cd 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,5 +1,5 @@
spring.main.banner-mode=off
-#server.port=8080
+server.port=8080
spring.datasource.url=jdbc:h2:file:./data
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
@@ -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