Сделана basic авторизация без проверки ролей

This commit is contained in:
Daniil Makeev 2025-02-19 15:17:56 +03:00
parent 5423b45470
commit 3f90380fa6
15 changed files with 223 additions and 17 deletions

View File

@ -7,6 +7,7 @@ import com.example.onomatopoeiaback.domain.visit.Visit;
import com.example.onomatopoeiaback.domain.visit.VisitDTO;
import com.example.onomatopoeiaback.service.EmployeeService;
import com.example.onomatopoeiaback.service.VisitService;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@ -26,22 +27,26 @@ public class EmployeeController {
}
@PostMapping("/create")
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<Employee> createEmployee(@RequestBody EmployeeDTO employeeDTO) {
return ResponseEntity.ok(employeeService.createEmployee(employeeDTO));
}
@GetMapping("/{username}/info")
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<Employee> info(@PathVariable String username) {
return ResponseEntity.ok(employeeService.info(username));
}
@GetMapping("/{username}/auth")
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<Visit> auth(@PathVariable String username) {
employeeService.auth(username);
return new ResponseEntity<>(HttpStatus.OK);
}
@PatchMapping("/{username}/open")
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<Visit> open(@PathVariable String username, @RequestBody VisitDTO visitDTO) {
visitService.register(username, visitDTO);
return new ResponseEntity<>(HttpStatus.OK);

View File

@ -1,8 +1,11 @@
package com.example.onomatopoeiaback.controller;
import com.example.onomatopoeiaback.domain.qrcode.QrCode;
import com.example.onomatopoeiaback.security.Auth;
import com.example.onomatopoeiaback.service.QrCodeService;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@ -19,7 +22,9 @@ public class QrCodeController {
}
@PostMapping("/create")
public ResponseEntity<QrCode> createQrCode(@RequestParam String name) {
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<QrCode> createQrCode(Authentication authentication, @RequestParam String name) {
Auth.getEmployee(authentication);
return ResponseEntity.ok(qrCodeService.createQrCode(name));
}
}

View File

@ -3,6 +3,7 @@ package com.example.onomatopoeiaback.controller;
import com.example.onomatopoeiaback.domain.visit.Visit;
import com.example.onomatopoeiaback.domain.visit.VisitDTO;
import com.example.onomatopoeiaback.service.VisitService;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@ -19,6 +20,7 @@ public class VisitController {
}
@GetMapping("/{login}/visits")
@SecurityRequirement(name = "basicAuth")
public ResponseEntity<Page<Visit>> getVisits(
@PathVariable String login,
@RequestParam(defaultValue = "0") int page,

View File

@ -0,0 +1,8 @@
package com.example.onomatopoeiaback.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "Доступ запрещен")
public class ForbiddenException extends RuntimeException {
}

View File

@ -21,4 +21,11 @@ public class GeneralExceptionsHandler {
public ExceptionDTO handleUnauthorizedException(UnauthorizedException e) {
return new ExceptionDTO("UNAUTHORIZED", e.getMessage());
}
@ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(ForbiddenException.class)
@ResponseBody
public ExceptionDTO handleForbiddenException(ForbiddenException e) {
return new ExceptionDTO("FORBIDDEN", e.getMessage());
}
}

View File

@ -1,12 +1,15 @@
package com.example.onomatopoeiaback.repository;
import com.example.onomatopoeiaback.domain.employee.Employee;
import lombok.NonNull;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Employee getEmployeeById(Long id);
Employee getEmployeesByLogin(String login);
Optional<Employee> findByLogin(@NonNull String login);
}

View File

@ -0,0 +1,14 @@
package com.example.onomatopoeiaback.security;
import com.example.onomatopoeiaback.domain.employee.Employee;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
public class Auth {
public static Employee getEmployee(Authentication authentication) {
if (authentication instanceof UsernamePasswordAuthenticationToken t) {
return ((CustomEmployeeDetails) t.getPrincipal()).getEmployee();
}
return null;
}
}

View File

@ -1,15 +1,40 @@
package com.example.onomatopoeiaback.security;
import com.example.onomatopoeiaback.exceptions.BadRequestException;
import com.example.onomatopoeiaback.exceptions.ForbiddenException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
final
CustomEmployeeDetailsService customEmployeeDetailsService;
final PasswordEncoder passwordEncoder;
public CustomAuthenticationProvider(CustomEmployeeDetailsService customEmployeeDetailsService, PasswordEncoder bCryptPasswordEncoder, PasswordEncoder passwordEncoder) {
this.customEmployeeDetailsService = customEmployeeDetailsService;
this.passwordEncoder = passwordEncoder;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return null;
String login = authentication.getName();
String password = passwordEncoder.encode((String) authentication.getCredentials());
if (login == null || password == null) {
throw new BadRequestException();
}
CustomEmployeeDetails customEmployeeDetails = customEmployeeDetailsService.loadUserByUsername(login);
if (customEmployeeDetails == null || !customEmployeeDetails.getPassword().equals(password)) {
throw new ForbiddenException();
}
return new UsernamePasswordAuthenticationToken(customEmployeeDetails, null, customEmployeeDetails.getAuthorities());
}
@Override

View File

@ -0,0 +1,33 @@
package com.example.onomatopoeiaback.security;
import com.example.onomatopoeiaback.domain.employee.Employee;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
@Getter
public class CustomEmployeeDetails implements UserDetails {
Employee employee;
public CustomEmployeeDetails(Employee employee) {
this.employee = employee;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return Collections.emptyList();
}
@Override
public String getPassword() {
return employee.getPassword();
}
@Override
public String getUsername() {
return employee.getLogin();
}
}

View File

@ -0,0 +1,25 @@
package com.example.onomatopoeiaback.security;
import com.example.onomatopoeiaback.domain.employee.Employee;
import com.example.onomatopoeiaback.exceptions.UnauthorizedException;
import com.example.onomatopoeiaback.repository.EmployeeRepository;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service("employeeDetailsService")
public class CustomEmployeeDetailsService implements UserDetailsService {
final
EmployeeRepository employeeRepository;
public CustomEmployeeDetailsService(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
@Override
public CustomEmployeeDetails loadUserByUsername(String login) throws UnauthorizedException {
Optional<Employee> employeeOptional = employeeRepository.findByLogin(login);
return employeeOptional.map(CustomEmployeeDetails::new).orElse(null);
}
}

View File

@ -0,0 +1,20 @@
package com.example.onomatopoeiaback.security;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
public class NoPopupBasicAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}

View File

@ -0,0 +1,14 @@
package com.example.onomatopoeiaback.security;
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();
}
}

View File

@ -1,16 +1,53 @@
package com.example.onomatopoeiaback.security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configurable
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final CustomAuthenticationProvider customAuthenticationProvider;
public SecurityConfig(CustomAuthenticationProvider customAuthenticationProvider, UserDetailsService userDetailsService) {
this.customAuthenticationProvider = customAuthenticationProvider;
this.userDetailsService = userDetailsService;
}
@Autowired
private UserDetailsService userDetailsService;
public void configureGlobal(AuthenticationManagerBuilder auth, PasswordEncoder passwordEncoder) throws Exception {
auth
.authenticationProvider(customAuthenticationProvider)
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(management -> management
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.httpBasic(basic -> basic
.authenticationEntryPoint(new NoPopupBasicAuthenticationEntryPoint()))
.authorizeHttpRequests(requests -> requests
.requestMatchers(
"/docs",
"/docs/**",
"/v3/api-docs/**",
"/swagger-ui/**"
)
.permitAll()
.anyRequest().authenticated());
return httpSecurity.build();
}
}

View File

@ -8,6 +8,8 @@ import com.example.onomatopoeiaback.repository.VisitRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class EmployeeService {
@ -34,20 +36,20 @@ public class EmployeeService {
}
public void auth(String login) {
Employee employee = employeeRepository.getEmployeesByLogin(login);
Optional<Employee> employeeOptional = employeeRepository.findByLogin(login);
if (employee == null) {
if (employeeOptional.isEmpty()) {
throw new UnauthorizedException();
}
}
public Employee info(String login) {
Employee employee = employeeRepository.getEmployeesByLogin(login);
Optional<Employee> employeeOptional = employeeRepository.findByLogin(login);
if (employee == null) {
if (employeeOptional.isEmpty()) {
throw new UnauthorizedException();
}
return employee;
return employeeOptional.get();
}
}

View File

@ -35,7 +35,7 @@ public class VisitService {
public void register(String login, VisitDTO visitDTO) {
Employee employee = employeeRepository.getEmployeesByLogin(login);
Optional<Employee> employeeOptional = employeeRepository.findByLogin(login);
LocalDateTime localDateTime = LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
Optional<QrCode> qrCodeOptional = qrCodeRepository.findById(visitDTO.getQrCodeId());
@ -43,11 +43,12 @@ public class VisitService {
throw new BadRequestException();
}
if (employee == null) {
if (employeeOptional.isEmpty()) {
throw new UnauthorizedException();
}
QrCode qrCode = qrCodeOptional.get();
Employee employee = employeeOptional.get();
Visit visit = new Visit();
visit.setQrCode(qrCode);
visit.setVisitType(visitDTO.getVisitType());
@ -59,8 +60,13 @@ public class VisitService {
}
public Page<Visit> getVisits(String login, Integer page, Integer size) {
Employee employee = employeeRepository.getEmployeesByLogin(login);
Optional<Employee> employeeOptional = employeeRepository.findByLogin(login);
if (employeeOptional.isEmpty()) {
throw new UnauthorizedException();
}
PageRequest pageable = PageRequest.of(page, size);
return visitRepository.findByEmployeeId(employee.getId(), pageable);
return visitRepository.findByEmployeeId(employeeOptional.get().getId(), pageable);
}
}