Добавление Spring Security и реализация авторизации

This commit is contained in:
Shilyaev_Dmitry 2025-02-19 14:28:28 +03:00
parent ef69abccec
commit 7dc4d1e77e
24 changed files with 319 additions and 59 deletions

View File

@ -1,5 +1,52 @@
package com.example.nto.config; package com.example.nto.config;
public class WebSecurityConfig { import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/h2-console/**").permitAll()
// .antMatchers("/api/auth").permitAll()
// .antMatchers("/api/users/username/{username}").permitAll()
// .antMatchers("/api/volunteer/one/1").hasAnyAuthority("ROLE_ADMIN")
// .antMatchers("/api/authority/**").hasAnyAuthority("ROLE_ADMIN")
.antMatchers("/api/**").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.headers().frameOptions().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
} }

View File

@ -5,25 +5,28 @@ import com.example.nto.entity.Code;
import com.example.nto.entity.Employee; import com.example.nto.entity.Employee;
import com.example.nto.service.impl.EmployeeServiceImpl; import com.example.nto.service.impl.EmployeeServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping("/api")
public class EmployeeController { public class EmployeeController {
private final EmployeeServiceImpl employeeService; private final EmployeeServiceImpl employeeService;
@GetMapping("/api/{login}/auth") @GetMapping("/login")
public String auth(@PathVariable("login") String login){ public ResponseEntity<Employee> login(Authentication authentication){
return employeeService.getAuth(login); return ResponseEntity.ok(employeeService.getEmployeeByUsername(authentication.getName()));
} }
@GetMapping("/api/{login}/info") @GetMapping("/info/{login}")
public Employee info(@PathVariable("login") String login){ public Employee info(@PathVariable("login") String username){
return employeeService.getInfo(login); return employeeService.getInfo(username);
} }
@PatchMapping("/api/{login}/open") @PatchMapping("/open/{login}")
public String open(@PathVariable("login") String login, @RequestBody Code value){ public String open(@PathVariable("login") String login, @RequestBody Code value){
return employeeService.patchOpen(login, value.getValue()); return employeeService.patchOpen(login, value.getValue());
} }

View File

@ -3,8 +3,9 @@ package com.example.nto.dto;
import lombok.Data; import lombok.Data;
@Data @Data
public class EmployeeRegisterDTO { public class EmployeeDTO {
private String login; private String username;
private String name; private String name;
private String password; private String password;
private String lastVisit;
} }

View File

@ -0,0 +1,17 @@
package com.example.nto.entity;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import javax.persistence.*;
@Data
@Entity
@Table(name="authority")
public class Authority implements GrantedAuthority {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name="authorities")
private String authority;
}

View File

@ -2,28 +2,65 @@ package com.example.nto.entity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*; import javax.persistence.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@Data @Data
@Entity @Entity
@Table(name="employee") @Table(name="employee")
public class Employee { public class Employee implements UserDetails {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id; private long id;
@Column(name = "login")
private String login; @Column(name = "username")
private String username;
@Column(name = "name") @Column(name = "name")
private String name; private String name;
@Column(name="password") @Column(name="password")
private String password; private String password;
@Column(name = "photo_url") @Column(name = "photo_url")
private String photoUrl; private String photoUrl;
@Column(name = "position") @Column(name = "position")
private String position; private String position;
@Column(name = "last_visit") @Column(name = "last_visit")
private LocalDateTime lastVisit; private LocalDateTime lastVisit;
@ManyToOne
@JoinColumn(name="enter_type_id")
private EnterType typeId;
@ManyToMany(fetch = FetchType.EAGER)
private Set<Authority> authorities;
@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

@ -0,0 +1,16 @@
package com.example.nto.entity;
import lombok.Data;
import javax.persistence.*;
@Data
@Entity
public class EnterType {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private long id;
@Column(name = "enter_type")
private String type;
}

View File

@ -0,0 +1,7 @@
package com.example.nto.exception;
public class EmployeeNotFoundException extends RuntimeException {
public EmployeeNotFoundException(String message) {
super(message);
}
}

View File

@ -0,0 +1,7 @@
package com.example.nto.exception;
public class IncorrectCodeException extends RuntimeException {
public IncorrectCodeException(String message) {
super(message);
}
}

View File

@ -0,0 +1,7 @@
package com.example.nto.exception;
public class IncorrectPasswordException extends RuntimeException {
public IncorrectPasswordException(String message) {
super(message);
}
}

View File

@ -0,0 +1,27 @@
package com.example.nto.exception.handler;
import com.example.nto.exception.EmployeeNotFoundException;
import com.example.nto.exception.IncorrectCodeException;
import com.example.nto.exception.IncorrectPasswordException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public ResponseEntity<String> handleEmployeeNotFoundException(EmployeeNotFoundException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
@ExceptionHandler
public ResponseEntity<String> handleIncorrectCodeException(IncorrectCodeException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(430));
}
@ExceptionHandler(IncorrectPasswordException.class)
public ResponseEntity<String> handlerIncorrectPasswordException(IncorrectPasswordException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT);
}
}

View File

@ -4,7 +4,9 @@ import com.example.nto.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;
import java.util.Optional;
@Repository @Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> { public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Employee findByLogin(String login); Optional<Employee> findByUsername(String username);
} }

View File

@ -0,0 +1,7 @@
package com.example.nto.repository;
import com.example.nto.entity.EnterType;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EnterTypeRepository extends JpaRepository<EnterType, Long> {
}

View File

@ -1,8 +1,9 @@
package com.example.nto.service; package com.example.nto.service;
import com.example.nto.entity.Code;
import com.example.nto.entity.Employee; import com.example.nto.entity.Employee;
public interface EmployeeService { public interface EmployeeService {
Employee getEmployeeByUsername(String username);
Employee getInfo(String username);
String patchOpen(String username, long value);
} }

View File

@ -2,6 +2,7 @@ package com.example.nto.service.impl;
import com.example.nto.entity.Code; import com.example.nto.entity.Code;
import com.example.nto.entity.Employee; import com.example.nto.entity.Employee;
import com.example.nto.exception.EmployeeNotFoundException;
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;
@ -22,35 +23,28 @@ public class EmployeeServiceImpl implements EmployeeService {
private final CodeRepository codeRepository; private final CodeRepository codeRepository;
@Override @Override
public String getAuth(String login) { public Employee getEmployeeByUsername(String username) {
Employee employeeOptional = employeeRepository.findByLogin(login); Optional<Employee> optionalEmployee = employeeRepository.findByUsername(username);
if(employeeOptional==null){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
return ("Данный логин существует - можно пользоваться приложением");
}
@Override
public Employee getInfo(String login) {
Employee employeeLogin=employeeRepository.findByLogin(login);
if(employeeLogin==null) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
Optional<Employee> employeeOptional=employeeRepository.findById(employeeLogin.getId());
return employeeOptional.get(); if(optionalEmployee.isEmpty())
throw new EmployeeNotFoundException("Employee with username: " + username + " not found");
return optionalEmployee.get();
} }
@Override @Override
public String patchOpen(String login, long value) { // Сделать возвращение статуса, а не строчки public Employee getInfo(String username) {
Employee employeeLogin=employeeRepository.findByLogin(login); Optional<Employee> optionalEmployee = employeeRepository.findByUsername(username);
Code codeValue=codeRepository.findByValue(value); if(optionalEmployee.isEmpty()) {
if(employeeLogin==null) { throw new EmployeeNotFoundException("Employee with username: " + username + " not found");
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
if(codeValue==null){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
} }
return optionalEmployee.get();
}
@Override
public String patchOpen(String username, long value) {
return ("Дверь открыта"); return ("Дверь открыта");
} }
} }

View File

@ -0,0 +1,30 @@
package com.example.nto.service.impl;
import com.example.nto.entity.Employee;
import com.example.nto.repository.EmployeeRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final EmployeeRepository employeeRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Optional<Employee> optionalEmployee = employeeRepository.findByUsername(s);
if(optionalEmployee.isEmpty()) {
throw new UsernameNotFoundException("User not found");
}
return optionalEmployee.get();
}
}

View File

@ -1,17 +1,17 @@
package com.example.nto.util; package com.example.nto.util;
import com.example.nto.dto.EmployeeRegisterDTO; import com.example.nto.dto.EmployeeDTO;
import com.example.nto.entity.Employee; import com.example.nto.entity.Employee;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@UtilityClass @UtilityClass
public class EmployeeMapper { public class EmployeeMapper {
public EmployeeRegisterDTO convertToRegisterDTO(Employee employee){ public EmployeeDTO convertToDTO(Employee employee){
EmployeeRegisterDTO employeeRegisterDTO = new EmployeeRegisterDTO(); EmployeeDTO employeeDTO = new EmployeeDTO();
employeeRegisterDTO.setLogin(employee.getLogin()); employeeDTO.setUsername(employee.getUsername());
employeeRegisterDTO.setPassword(employee.getPassword()); employeeDTO.setPassword(employee.getPassword());
employeeRegisterDTO.setName(employee.getName()); employeeDTO.setName(employee.getName());
return employeeRegisterDTO; return employeeDTO;
} }
} }

View File

@ -15,7 +15,7 @@
<column name="id" type="BIGINT" autoIncrement="true" > <column name="id" type="BIGINT" autoIncrement="true" >
<constraints primaryKey="true" nullable="false"/> <constraints primaryKey="true" nullable="false"/>
</column> </column>
<column name="login" type="VARCHAR(50)"> <column name="username" type="VARCHAR(50)">
<constraints nullable="false" unique="true"/> <constraints nullable="false" unique="true"/>
</column> </column>
<column name="name" type="VARCHAR(50)"> <column name="name" type="VARCHAR(50)">
@ -24,6 +24,9 @@
<column name="password" type="VARCHAR(100)"> <column name="password" type="VARCHAR(100)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="enter_type_id" type="BIGINT">
<constraints foreignKeyName="fk_employee_enter" referencedTableName="enter_type" referencedColumnNames="id"/>
</column>
<column name="position" type="VARCHAR(50)"/> <column name="position" type="VARCHAR(50)"/>
<column name="photo_url" type="VARCHAR(300)"/> <column name="photo_url" type="VARCHAR(300)"/>
<column name="last_visit" type="DATETIME()"/> <column name="last_visit" type="DATETIME()"/>

View File

@ -18,7 +18,7 @@
<column name="employee_id" type="BIGINT"> <column name="employee_id" type="BIGINT">
<constraints foreignKeyName="fk_authorities_employee" referencedTableName="employee" referencedColumnNames="id"/> <constraints foreignKeyName="fk_authorities_employee" referencedTableName="employee" referencedColumnNames="id"/>
</column> </column>
<column name="authority_id" type="BIGINT"> <column name="authorities_id" type="BIGINT">
<constraints foreignKeyName="fk_authorities_authority" referencedTableName="authority" referencedColumnNames="id"/> <constraints foreignKeyName="fk_authorities_authority" referencedTableName="authority" referencedColumnNames="id"/>
</column> </column>
</createTable> </createTable>

View File

@ -0,0 +1,27 @@
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog-ext.xsd">
<changeSet id="2025-02-19-0005-enter-type" author="dshilyaev">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="enter_type"/>
</not>
</preConditions>
<createTable tableName="enter_type">
<column name="id" type="BIGINT" autoIncrement="true" >
<constraints primaryKey="true" nullable="false"/>
</column>
<column name="types" type="VARCHAR(10)">
<constraints unique="true" nullable="false"/>
</column>
</createTable>
</changeSet>
</databaseChangeLog>

View File

@ -0,0 +1,14 @@
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog-ext.xsd">
<changeSet id="2025-02-19-0005-enter-type.xml" author="dshilyaev">
<loadData tableName="enter_type" file="db.changelog/1.0/data/csv/2025-02-19-0005-enter-type-data.csv"
separator=";"
quotchar='*'
encoding="UTF-8"/>
</changeSet>
</databaseChangeLog>

View File

@ -1,8 +1,8 @@
login;name;password;position;photo_url;last_visit username;name;password;position;photo_url;last_visit
pivanov;Иванов Петр Федорович;abcd;Разработчик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-12T08:30:21 pivanov;Иванов Петр Федорович;$2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6;Разработчик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-12T08:30:21
ipetrov;Петров Иван Константинович;abcd;Аналитик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-13T08:35:44 ipetrov;Петров Иван Константинович;$2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6;Аналитик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-13T08:35:44
asemenov;Семенов Анатолий Анатольевич;abcd;Разработчик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-13T08:31:33 asemenov;Семенов Анатолий Анатольевич;$2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6;Разработчик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-13T08:31:33
afedorov;Федоров Александр Сергеевич;abcd;Тестировщик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-12T08:36:09 afedorov;Федоров Александр Сергеевич;$2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6;Тестировщик;https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg;2024-02-12T08:36:09

1 login username name password position photo_url last_visit
2 pivanov pivanov Иванов Петр Федорович abcd $2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6 Разработчик https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg 2024-02-12T08:30:21
3 ipetrov ipetrov Петров Иван Константинович abcd $2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6 Аналитик https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg 2024-02-13T08:35:44
4 asemenov asemenov Семенов Анатолий Анатольевич abcd $2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6 Разработчик https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg 2024-02-13T08:31:33
5 afedorov afedorov Федоров Александр Сергеевич abcd $2a$12$sgNQzK6r02WoGZb0rJolB.AkVWYc.ZF0RtxZ6PXjhw8EhakqvoFU6 Тестировщик https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg 2024-02-12T08:36:09
6
7
8

View File

@ -1,4 +1,4 @@
employee_id;authority_id employee_id;authorities_id
1;1 1;1
2;1 2;1
3;1 3;1

1 employee_id authority_id authorities_id
2 1 1 1
3 2 1 1
4 3 1 1

View File

@ -0,0 +1,3 @@
types
card
qr
1 types
2 card
3 qr

View File

@ -4,14 +4,24 @@
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog-ext.xsd"> http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog-ext.xsd">
<include file="db.changelog/1.0/1.0/2025-02-18-0001-employee.xml"/> <include file="db.changelog/1.0/1.0/2025-02-19-0005-enter-type.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0002-code.xml"/> <include file="db.changelog/1.0/data/2025-02-19-0005-enter-type-data.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0003-authority.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0004-employee-authority.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0001-employee.xml"/>
<include file="db.changelog/1.0/data/2025-02-18-0001-employee-data.xml"/> <include file="db.changelog/1.0/data/2025-02-18-0001-employee-data.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0002-code.xml"/>
<include file="db.changelog/1.0/data/2025-02-18-0002-code-data.xml"/> <include file="db.changelog/1.0/data/2025-02-18-0002-code-data.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0003-authority.xml"/>
<include file="db.changelog/1.0/data/2025-02-18-0003-authority-data.xml"/> <include file="db.changelog/1.0/data/2025-02-18-0003-authority-data.xml"/>
<include file="db.changelog/1.0/1.0/2025-02-18-0004-employee-authority.xml"/>
<include file="db.changelog/1.0/data/2025-02-18-0004-employee-authority-data.xml"/> <include file="db.changelog/1.0/data/2025-02-18-0004-employee-authority-data.xml"/>
</databaseChangeLog> </databaseChangeLog>