diff --git a/pom.xml b/pom.xml index 88282ee..7773001 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,19 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + com.github.javafaker + javafaker + 1.0.2 + \ No newline at end of file diff --git a/src/main/java/com/example/nto/config/SecurityConfig.java b/src/main/java/com/example/nto/config/SecurityConfig.java new file mode 100644 index 0000000..6356ffa --- /dev/null +++ b/src/main/java/com/example/nto/config/SecurityConfig.java @@ -0,0 +1,46 @@ +package com.example.nto.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + + private final UserDetailsService userDetailsService; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + return http.csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> auth.requestMatchers(new AntPathRequestMatcher("/api/login")).permitAll() + .requestMatchers(new AntPathRequestMatcher("/api/**")).authenticated()) + .formLogin(AbstractAuthenticationFilterConfigurer::permitAll) + .getOrBuild(); + } + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(userDetailsService); + provider.setPasswordEncoder(passwordEncoder()); + return provider; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index d776678..fa39f01 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,32 +1,38 @@ package com.example.nto.controller; -import com.example.nto.entity.Code; -import com.example.nto.entity.Employee; +import com.example.nto.model.entity.Code; +import com.example.nto.model.entity.Employee; import com.example.nto.service.EmployeeService; import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import java.util.Map; - @RestController -@RequestMapping("/api/{login}") +@RequestMapping("/api") @RequiredArgsConstructor public class EmployeeController { private final EmployeeService employeeService; @GetMapping("/auth") - public void auth(@PathVariable final String login) { - employeeService.employeeExists(login); + public void auth(@RequestParam final String login, @RequestParam final String password) { + employeeService.auth(login, password); } + @PreAuthorize("hasAuthority('User', 'Admin')") @GetMapping("/info") - public Employee info(@PathVariable final String login) { + public Employee info(@RequestParam final String login) { return employeeService.getEmployee(login); } + @PreAuthorize("hasAuthority('User', 'Admin')") @PatchMapping("/open") - public void open(@PathVariable final String login, @RequestBody final Code code) { + public void open(@RequestParam final String login, @RequestBody final Code code) { employeeService.updateVisit(login, code.getValue()); } + + @PostMapping("/add") + public void add(@RequestBody final Employee employee) { + employeeService.addEmployee(employee); + } } diff --git a/src/main/java/com/example/nto/entity/Employee.java b/src/main/java/com/example/nto/entity/Employee.java deleted file mode 100644 index 04f5c91..0000000 --- a/src/main/java/com/example/nto/entity/Employee.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.example.nto.entity; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import java.time.LocalDateTime; - -@Entity(name = "employee") -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Employee { - - @Id - private long id; - private String login; - private String name; - private String photo; - private String position; - private LocalDateTime lastVisit; -} diff --git a/src/main/java/com/example/nto/model/EmployeeRoleType.java b/src/main/java/com/example/nto/model/EmployeeRoleType.java new file mode 100644 index 0000000..55c5bf2 --- /dev/null +++ b/src/main/java/com/example/nto/model/EmployeeRoleType.java @@ -0,0 +1,6 @@ +package com.example.nto.model; + +public enum EmployeeRoleType { + USER, + ADMIN +} diff --git a/src/main/java/com/example/nto/entity/Code.java b/src/main/java/com/example/nto/model/entity/Code.java similarity index 92% rename from src/main/java/com/example/nto/entity/Code.java rename to src/main/java/com/example/nto/model/entity/Code.java index 5e3e92a..bb6fbac 100644 --- a/src/main/java/com/example/nto/entity/Code.java +++ b/src/main/java/com/example/nto/model/entity/Code.java @@ -1,4 +1,4 @@ -package com.example.nto.entity; +package com.example.nto.model.entity; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/src/main/java/com/example/nto/model/entity/Employee.java b/src/main/java/com/example/nto/model/entity/Employee.java new file mode 100644 index 0000000..0812a7b --- /dev/null +++ b/src/main/java/com/example/nto/model/entity/Employee.java @@ -0,0 +1,71 @@ +package com.example.nto.model.entity; + +import com.example.nto.model.EmployeeRoleType; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import javax.persistence.*; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +@Entity(name = "employee") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Employee implements UserDetails { + + @Id + private long id; + + private String login; + + private String password; + + private String name; + + private String photo; + + private String position; + + private LocalDateTime lastVisit; + + @Enumerated(EnumType.STRING) + private EmployeeRoleType role; + + @Override + public Collection getAuthorities() { + return List.of(new SimpleGrantedAuthority("ROLE_" + role.name())); + } + + @Override + public String getUsername() { + return login; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } +} diff --git a/src/main/java/com/example/nto/repository/CodeRepository.java b/src/main/java/com/example/nto/repository/CodeRepository.java index d1ed7da..be1ef7b 100644 --- a/src/main/java/com/example/nto/repository/CodeRepository.java +++ b/src/main/java/com/example/nto/repository/CodeRepository.java @@ -1,6 +1,6 @@ package com.example.nto.repository; -import com.example.nto.entity.Code; +import com.example.nto.model.entity.Code; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index 16440d3..404dacf 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,6 +1,6 @@ package com.example.nto.repository; -import com.example.nto.entity.Employee; +import com.example.nto.model.entity.Employee; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index ae75d14..d18caaa 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,12 +1,17 @@ package com.example.nto.service; -import com.example.nto.entity.Employee; +import com.example.nto.model.entity.Employee; +import org.springframework.security.core.userdetails.UserDetails; public interface EmployeeService { - void employeeExists(String login); + void auth(String login, String password); Employee getEmployee(String login); void updateVisit(String login, long value); + + void addEmployee(Employee employee); + + UserDetails loadUserByUsername(String username); } diff --git a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java index 8a39171..3bb8fd3 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,30 +1,38 @@ package com.example.nto.service.impl; -import com.example.nto.entity.Code; -import com.example.nto.entity.Employee; +import com.example.nto.model.entity.Employee; import com.example.nto.repository.CodeRepository; import com.example.nto.repository.EmployeeRepository; import com.example.nto.service.EmployeeService; import com.example.nto.service.exception.CodeNotFoundException; import com.example.nto.service.exception.EmployeeNotFoundException; import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +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.time.LocalDateTime; @Service @RequiredArgsConstructor -public class EmployeeServiceImpl implements EmployeeService { +public class EmployeeServiceImpl implements EmployeeService, UserDetailsService { private final EmployeeRepository employeeRepository; + private final PasswordEncoder passwordEncoder; + private final CodeRepository codeRepository; + private final AuthenticationManager authenticationManager; + @Override - public void employeeExists(final String login) { - if (!employeeRepository.existsByLogin(login)) { - throw new EmployeeNotFoundException(); - } + public void auth(final String login, final String password) { + final Employee employee = getEmployee(login); + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(employee.getUsername(), employee.getPassword())); } @Override @@ -47,4 +55,15 @@ public class EmployeeServiceImpl implements EmployeeService { employee.setLastVisit(LocalDateTime.now()); employeeRepository.save(employee); } + + @Override + public void addEmployee(Employee employee) { + employee.setPassword(passwordEncoder.encode(employee.getPassword())); + employeeRepository.save(employee); + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return employeeRepository.findEmployeeByLogin(username); + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1742b2b..b5dbfe5 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -28,4 +28,8 @@ spring: operationsSorter: method server: - port: 8090 \ No newline at end of file + port: 8090 + +token: + signing: + key: 53A73E5F1C4E0A2D3B5F2D784E6A1B423D6F247D1F6E5C3A596D635A75327855 \ No newline at end of file diff --git a/src/main/resources/data.sql b/src/main/resources/data.sql index 03720a9..8a6f29d 100644 --- a/src/main/resources/data.sql +++ b/src/main/resources/data.sql @@ -1,9 +1,9 @@ -INSERT INTO employee (id, login, name, photo, position, last_visit) +INSERT INTO employee (id, login, name, photo, position, last_visit, role) VALUES -(1, 'pivanov', 'Иванов Петр Федорович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-12T08:30'), -(2, 'ipetrov', 'Петров Иван Константинович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Аналитик', '2024-02-13T08:35'), -(3, 'asemenov', 'Семенов Анатолий Анатольевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-13T08:31'), -(4, 'afedorov', 'Федоров Александр Сергеевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Тестировщик', '2024-02-12T08:36'); +(1, 'pivanov', 'Иванов Петр Федорович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-12T08:30', 'USER'), +(2, 'ipetrov', 'Петров Иван Константинович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Аналитик', '2024-02-13T08:35', 'ADMIN'), +(3, 'asemenov', 'Семенов Анатолий Анатольевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-13T08:31', 'USER'), +(4, 'afedorov', 'Федоров Александр Сергеевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Тестировщик', '2024-02-12T08:36', 'USER'); INSERT INTO code (value) VALUES