This commit is contained in:
Konstantin 2025-02-19 15:32:18 +03:00
parent cac66e7e2c
commit 9cccc1fe60
13 changed files with 197 additions and 55 deletions

13
pom.xml
View File

@ -52,6 +52,19 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -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();
}
}

View File

@ -1,32 +1,38 @@
package com.example.nto.controller; package com.example.nto.controller;
import com.example.nto.entity.Code; import com.example.nto.model.entity.Code;
import com.example.nto.entity.Employee; import com.example.nto.model.entity.Employee;
import com.example.nto.service.EmployeeService; import com.example.nto.service.EmployeeService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController @RestController
@RequestMapping("/api/{login}") @RequestMapping("/api")
@RequiredArgsConstructor @RequiredArgsConstructor
public class EmployeeController { public class EmployeeController {
private final EmployeeService employeeService; private final EmployeeService employeeService;
@GetMapping("/auth") @GetMapping("/auth")
public void auth(@PathVariable final String login) { public void auth(@RequestParam final String login, @RequestParam final String password) {
employeeService.employeeExists(login); employeeService.auth(login, password);
} }
@PreAuthorize("hasAuthority('User', 'Admin')")
@GetMapping("/info") @GetMapping("/info")
public Employee info(@PathVariable final String login) { public Employee info(@RequestParam final String login) {
return employeeService.getEmployee(login); return employeeService.getEmployee(login);
} }
@PreAuthorize("hasAuthority('User', 'Admin')")
@PatchMapping("/open") @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()); employeeService.updateVisit(login, code.getValue());
} }
@PostMapping("/add")
public void add(@RequestBody final Employee employee) {
employeeService.addEmployee(employee);
}
} }

View File

@ -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;
}

View File

@ -0,0 +1,6 @@
package com.example.nto.model;
public enum EmployeeRoleType {
USER,
ADMIN
}

View File

@ -1,4 +1,4 @@
package com.example.nto.entity; package com.example.nto.model.entity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;

View File

@ -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<? extends GrantedAuthority> 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;
}
}

View File

@ -1,6 +1,6 @@
package com.example.nto.repository; 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.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;

View File

@ -1,6 +1,6 @@
package com.example.nto.repository; 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.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;

View File

@ -1,12 +1,17 @@
package com.example.nto.service; 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 { public interface EmployeeService {
void employeeExists(String login); void auth(String login, String password);
Employee getEmployee(String login); Employee getEmployee(String login);
void updateVisit(String login, long value); void updateVisit(String login, long value);
void addEmployee(Employee employee);
UserDetails loadUserByUsername(String username);
} }

View File

@ -1,30 +1,38 @@
package com.example.nto.service.impl; package com.example.nto.service.impl;
import com.example.nto.entity.Code; import com.example.nto.model.entity.Employee;
import com.example.nto.entity.Employee;
import com.example.nto.repository.CodeRepository; import com.example.nto.repository.CodeRepository;
import com.example.nto.repository.EmployeeRepository; import com.example.nto.repository.EmployeeRepository;
import com.example.nto.service.EmployeeService; import com.example.nto.service.EmployeeService;
import com.example.nto.service.exception.CodeNotFoundException; import com.example.nto.service.exception.CodeNotFoundException;
import com.example.nto.service.exception.EmployeeNotFoundException; import com.example.nto.service.exception.EmployeeNotFoundException;
import lombok.RequiredArgsConstructor; 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 org.springframework.stereotype.Service;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class EmployeeServiceImpl implements EmployeeService { public class EmployeeServiceImpl implements EmployeeService, UserDetailsService {
private final EmployeeRepository employeeRepository; private final EmployeeRepository employeeRepository;
private final PasswordEncoder passwordEncoder;
private final CodeRepository codeRepository; private final CodeRepository codeRepository;
private final AuthenticationManager authenticationManager;
@Override @Override
public void employeeExists(final String login) { public void auth(final String login, final String password) {
if (!employeeRepository.existsByLogin(login)) { final Employee employee = getEmployee(login);
throw new EmployeeNotFoundException(); authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(employee.getUsername(), employee.getPassword()));
}
} }
@Override @Override
@ -47,4 +55,15 @@ public class EmployeeServiceImpl implements EmployeeService {
employee.setLastVisit(LocalDateTime.now()); employee.setLastVisit(LocalDateTime.now());
employeeRepository.save(employee); 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);
}
} }

View File

@ -29,3 +29,7 @@ spring:
server: server:
port: 8090 port: 8090
token:
signing:
key: 53A73E5F1C4E0A2D3B5F2D784E6A1B423D6F247D1F6E5C3A596D635A75327855

View File

@ -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 VALUES
(1, 'pivanov', 'Иванов Петр Федорович', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Разработчик', '2024-02-12T08:30'), (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'), (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'), (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'); (4, 'afedorov', 'Федоров Александр Сергеевич', 'https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg', 'Тестировщик', '2024-02-12T08:36', 'USER');
INSERT INTO code (value) INSERT INTO code (value)
VALUES VALUES