basic authorization and authentication configurations
This commit is contained in:
parent
c387436681
commit
a05e250888
@ -84,6 +84,10 @@
|
||||
<artifactId>postgresql</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -1,19 +1,98 @@
|
||||
package ru.tubryansk.tdms.config;
|
||||
|
||||
|
||||
import jakarta.servlet.http.HttpSessionEvent;
|
||||
import jakarta.servlet.http.HttpSessionListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
|
||||
|
||||
|
||||
@Configuration
|
||||
public class SecurityConfiguration {
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
return httpSecurity
|
||||
.authorizeHttpRequests(req -> req.requestMatchers("/**").permitAll())
|
||||
.csrf(AbstractHttpConfigurer::disable).build();
|
||||
.authorizeHttpRequests(this::configureHttpAuthorization)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.cors(AbstractHttpConfigurer::disable)
|
||||
.authenticationManager(authenticationManager())
|
||||
.sessionManagement(this::configureSessionManagement)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization) {
|
||||
/* API ROUTES */
|
||||
httpAuthorization.requestMatchers("/api/diploma-topic/**").permitAll();
|
||||
httpAuthorization.requestMatchers("/api/**").denyAll();
|
||||
/* STATIC ROUTES */
|
||||
httpAuthorization.requestMatchers("/**").permitAll();
|
||||
/* OTHER */
|
||||
httpAuthorization.anyRequest().denyAll();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authenticationManager() {
|
||||
|
||||
return new ProviderManager(authenticationProvider());
|
||||
}
|
||||
|
||||
private AuthenticationProvider authenticationProvider() {
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder());
|
||||
provider.setUserDetailsService(userDetailsService());
|
||||
return provider;
|
||||
}
|
||||
|
||||
private UserDetailsService userDetailsService() {
|
||||
return new InMemoryUserDetailsManager(User.builder()
|
||||
.username("admin")
|
||||
.password("{noop}admin")
|
||||
.authorities("ROLE_STUDENT", "ROLE_TEACHER", "ROLE_CURATOR")
|
||||
.build());
|
||||
}
|
||||
|
||||
private PasswordEncoder passwordEncoder() {
|
||||
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
// todo: remove when login/logout is implemented
|
||||
public HttpSessionListener autoAuthenticateUnderAdmin() {
|
||||
return new HttpSessionListener() {
|
||||
@Override
|
||||
public void sessionCreated(HttpSessionEvent se) {
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("admin", "admin");
|
||||
Authentication authenticated = authenticationManager().authenticate(authentication);
|
||||
context.setAuthentication(authenticated);
|
||||
SecurityContextHolder.setContext(context);
|
||||
se.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// todo: remove when login/logout is implemented, since we do not need automatically created session with no authentication
|
||||
private void configureSessionManagement(SessionManagementConfigurer<HttpSecurity> sessionManagement) {
|
||||
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
|
||||
import jakarta.validation.constraints.PositiveOrZero;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.tubryansk.tdms.dto.DiplomaTopicDTO;
|
||||
import ru.tubryansk.tdms.service.DiplomaTopicService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/diploma-topic/")
|
||||
@Validated
|
||||
public class DiplomaTopicRestController {
|
||||
@Autowired
|
||||
private DiplomaTopicService diplomaTopicService;
|
||||
|
||||
@GetMapping("/get-all")
|
||||
public List<DiplomaTopicDTO> getAll() {
|
||||
return diplomaTopicService.getAll();
|
||||
}
|
||||
|
||||
@GetMapping("/get-by-id/{id:[\\-+]?\\d+}")
|
||||
public DiplomaTopicDTO getById(@PathVariable @PositiveOrZero Integer id) {
|
||||
return diplomaTopicService.getById(id);
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package ru.tubryansk.tdms.controller;
|
||||
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/")
|
||||
public class TestController {
|
||||
@GetMapping
|
||||
public String root() {
|
||||
return "index.html";
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,16 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class DiplomaTopicDTO {
|
||||
private Integer id;
|
||||
private String name;
|
||||
@Builder
|
||||
public record DiplomaTopicDTO(Integer id, String name) {
|
||||
public static DiplomaTopicDTO fromEntity(DiplomaTopic diplomaTopic) {
|
||||
return DiplomaTopicDTO.builder()
|
||||
.id(diplomaTopic.getId())
|
||||
.name(diplomaTopic.getName())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
package ru.tubryansk.tdms.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public record ErrorResponse(String message, ErrorCode errorCode) {
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public enum ErrorCode {
|
||||
BAD_REQUEST(HttpStatus.BAD_REQUEST),
|
||||
VALIDATION_ERROR(HttpStatus.BAD_REQUEST),
|
||||
INTERNAL_ERROR(HttpStatus.INTERNAL_SERVER_ERROR),
|
||||
NOT_FOUND(HttpStatus.NOT_FOUND),
|
||||
ACCESS_DENIED(HttpStatus.FORBIDDEN)
|
||||
;
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
|
||||
public class AccessDeniedException extends BusinessException {
|
||||
public AccessDeniedException() {
|
||||
super("Access denied");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorResponse.ErrorCode getErrorCode() {
|
||||
return ErrorResponse.ErrorCode.ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
|
||||
public class BusinessException extends RuntimeException {
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ErrorResponse.ErrorCode getErrorCode() {
|
||||
return ErrorResponse.ErrorCode.INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler {
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
// todo: make a better error message
|
||||
return new ErrorResponse(e.getMessage(), ErrorResponse.ErrorCode.VALIDATION_ERROR);
|
||||
}
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ErrorResponse handleBusinessException(BusinessException e, HttpServletResponse response) {
|
||||
response.setStatus(e.getErrorCode().getHttpStatus().value());
|
||||
return new ErrorResponse(e.getMessage(), e.getErrorCode());
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoResourceFoundException.class)
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public ErrorResponse handleNoResourceFoundException(NoResourceFoundException e) {
|
||||
// todo: make error page
|
||||
return new ErrorResponse(e.getMessage(), ErrorResponse.ErrorCode.NOT_FOUND);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public ErrorResponse handleUnexpectedException(Exception e) {
|
||||
// todo: make error page
|
||||
log.error("Unexpected exception.", e);
|
||||
return new ErrorResponse(e.getMessage(), ErrorResponse.ErrorCode.INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.tubryansk.tdms.exception;
|
||||
|
||||
import ru.tubryansk.tdms.dto.ErrorResponse;
|
||||
|
||||
public class NotFoundException extends BusinessException {
|
||||
public NotFoundException(Class<?> entityClass, Integer id) {
|
||||
super(entityClass.getSimpleName() + " with id " + id + " not found");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ErrorResponse.ErrorCode getErrorCode() {
|
||||
return ErrorResponse.ErrorCode.NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package ru.tubryansk.tdms.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
|
||||
@Repository
|
||||
public interface DiplomaTopicRepository extends JpaRepository<DiplomaTopic, Integer> {
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package ru.tubryansk.tdms.service;
|
||||
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.tubryansk.tdms.entity.DiplomaTopic;
|
||||
import ru.tubryansk.tdms.exception.NotFoundException;
|
||||
import ru.tubryansk.tdms.repository.DiplomaTopicRepository;
|
||||
import ru.tubryansk.tdms.dto.DiplomaTopicDTO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class DiplomaTopicService {
|
||||
@Autowired
|
||||
private DiplomaTopicRepository diplomaTopicRepository;
|
||||
|
||||
public List<DiplomaTopicDTO> getAll() {
|
||||
return diplomaTopicRepository.findAll()
|
||||
.stream()
|
||||
.map(DiplomaTopicDTO::fromEntity)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public DiplomaTopicDTO getById(Integer id) {
|
||||
return DiplomaTopicDTO.fromEntity(diplomaTopicRepository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id)));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user