-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Global Configuration & Spring Security Set Up (#1)
* chore: Init Configuration Setting By Submodule * feat: update submodule * feat: Global Configuration & Spring Security Set Up
- Loading branch information
Showing
48 changed files
with
1,477 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "A11-Config"] | ||
path = A11-Config | ||
url = [email protected]:h-beeen/A11-Config.git |
Submodule A11-Config
added at
339325
41 changes: 41 additions & 0 deletions
41
src/main/java/com/partybbangbbang/global/auditing/BaseEntity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package com.partybbangbbang.global.auditing; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.EntityListeners; | ||
import jakarta.persistence.MappedSuperclass; | ||
import lombok.Getter; | ||
import org.springframework.data.annotation.CreatedDate; | ||
import org.springframework.data.annotation.LastModifiedDate; | ||
import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
import static com.fasterxml.jackson.annotation.JsonFormat.Shape.STRING; | ||
|
||
@Getter | ||
@MappedSuperclass | ||
@EntityListeners(value = AuditingEntityListener.class) | ||
public abstract class BaseEntity { | ||
|
||
@Column( | ||
nullable = false, | ||
insertable = false, | ||
updatable = false, | ||
columnDefinition = "datetime default CURRENT_TIMESTAMP") | ||
@CreatedDate | ||
@JsonFormat( | ||
shape = STRING, | ||
pattern = "yyyy-MM-dd a HH:mm") | ||
private LocalDateTime createdDate; | ||
|
||
@Column( | ||
nullable = false, | ||
insertable = false, | ||
columnDefinition = "datetime default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP") | ||
@LastModifiedDate | ||
@JsonFormat( | ||
shape = STRING, | ||
pattern = "yyyy-MM-dd a HH:mm") | ||
private LocalDateTime updatedDate; | ||
} |
9 changes: 9 additions & 0 deletions
9
src/main/java/com/partybbangbbang/global/config/AuditingConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.partybbangbbang.global.config; | ||
|
||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing; | ||
|
||
@Configuration | ||
@EnableJpaAuditing | ||
public class AuditingConfig { | ||
} |
22 changes: 22 additions & 0 deletions
22
src/main/java/com/partybbangbbang/global/config/QueryDslConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.partybbangbbang.global.config; | ||
|
||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import jakarta.persistence.EntityManager; | ||
import jakarta.persistence.PersistenceContext; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
@Configuration | ||
@RequiredArgsConstructor | ||
public class QueryDslConfig { | ||
|
||
@PersistenceContext | ||
private EntityManager entityManager; | ||
|
||
@Bean | ||
public JPAQueryFactory jpaQueryFactory() { | ||
return new JPAQueryFactory(this.entityManager); | ||
} | ||
} | ||
|
15 changes: 15 additions & 0 deletions
15
src/main/java/com/partybbangbbang/global/config/RestTemplateConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.partybbangbbang.global.config; | ||
|
||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
@Configuration | ||
public class RestTemplateConfig { | ||
|
||
@Bean | ||
public RestTemplate restTemplate() { | ||
return new RestTemplate(); | ||
} | ||
} | ||
|
49 changes: 49 additions & 0 deletions
49
src/main/java/com/partybbangbbang/global/exception/ApiExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.partybbangbbang.global.exception; | ||
|
||
import com.partybbangbbang.global.exception.error.ErrorCode; | ||
import com.partybbangbbang.global.exception.error.ErrorResponse; | ||
import com.partybbangbbang.global.exception.error.GlobalError; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.MissingServletRequestParameterException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.ResponseStatus; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
|
||
@Slf4j | ||
@RestControllerAdvice | ||
public class ApiExceptionHandler { | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
@ExceptionHandler(MethodArgumentNotValidException.class) | ||
public ErrorResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { | ||
return ErrorResponse.of(ex.getFieldErrors()); | ||
} | ||
|
||
@ResponseStatus(HttpStatus.BAD_REQUEST) | ||
@ExceptionHandler(MissingServletRequestParameterException.class) | ||
public ErrorResponse missingServletRequestParameterException() { | ||
return ErrorResponse.of(GlobalError.INVALID_REQUEST_PARAM); | ||
} | ||
|
||
@ExceptionHandler(BusinessException.class) | ||
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException exception) { | ||
logBusinessException(exception); | ||
return convert(exception.getErrorCode()); | ||
} | ||
|
||
private ResponseEntity<ErrorResponse> convert(ErrorCode errorCode) { | ||
return ResponseEntity.status(errorCode.getStatus()) | ||
.body(ErrorResponse.of(errorCode)); | ||
} | ||
|
||
private void logBusinessException(BusinessException exception) { | ||
if (exception.getErrorCode().getStatus().is5xxServerError()) { | ||
log.error("", exception); | ||
} else { | ||
log.error("error message = {}", exception.getMessage()); | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/main/java/com/partybbangbbang/global/exception/ApiExceptionHandlingFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package com.partybbangbbang.global.exception; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.partybbangbbang.global.exception.error.ErrorResponse; | ||
import jakarta.servlet.FilterChain; | ||
import jakarta.servlet.ServletException; | ||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.filter.OncePerRequestFilter; | ||
|
||
import java.io.IOException; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class ApiExceptionHandlingFilter extends OncePerRequestFilter { | ||
|
||
private final ObjectMapper om; | ||
|
||
@Override | ||
protected void doFilterInternal( | ||
HttpServletRequest request, | ||
HttpServletResponse response, | ||
FilterChain chain | ||
) throws ServletException, IOException { | ||
try { | ||
chain.doFilter(request, response); | ||
} catch (BusinessException e) { | ||
setErrorResponse(response, e); | ||
} | ||
} | ||
|
||
private void setErrorResponse( | ||
HttpServletResponse response, | ||
BusinessException e | ||
) { | ||
try { | ||
response.setContentType(MediaType.APPLICATION_JSON_VALUE); | ||
response.setStatus(HttpStatus.UNAUTHORIZED.value()); | ||
om.writeValue(response.getOutputStream(), ErrorResponse.of(e.getErrorCode())); | ||
} catch (IOException ex) { | ||
throw new RuntimeException(ex); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/partybbangbbang/global/exception/BusinessException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.partybbangbbang.global.exception; | ||
|
||
import com.partybbangbbang.global.exception.error.ErrorCode; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class BusinessException extends RuntimeException { | ||
private final ErrorCode errorCode; | ||
|
||
public BusinessException(ErrorCode errorCode) { | ||
super(errorCode.getMessage()); | ||
this.errorCode = errorCode; | ||
} | ||
|
||
public static BusinessException of(ErrorCode errorCode) { | ||
return new BusinessException(errorCode); | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
src/main/java/com/partybbangbbang/global/exception/error/ErrorCode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.partybbangbbang.global.exception.error; | ||
|
||
import org.springframework.http.HttpStatus; | ||
|
||
public interface ErrorCode { | ||
|
||
String getMessage(); | ||
|
||
HttpStatus getStatus(); | ||
|
||
String getCode(); | ||
} |
54 changes: 54 additions & 0 deletions
54
src/main/java/com/partybbangbbang/global/exception/error/ErrorResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.partybbangbbang.global.exception.error; | ||
|
||
import lombok.Getter; | ||
import org.springframework.validation.FieldError; | ||
|
||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.stream.Collectors; | ||
|
||
import static com.partybbangbbang.global.exception.error.GlobalError.INVALID_REQUEST_PARAM; | ||
|
||
@Getter | ||
public class ErrorResponse { | ||
|
||
private final String timeStamp; | ||
|
||
private final String errorCode; | ||
|
||
private final String errorMessage; | ||
|
||
private final Object details; | ||
|
||
private ErrorResponse(String errorCode, String errorMessage, Object details) { | ||
this.timeStamp = LocalDateTime.now().toString(); | ||
this.errorCode = errorCode; | ||
this.errorMessage = errorMessage; | ||
this.details = details; | ||
} | ||
|
||
public static ErrorResponse of(ErrorCode errorCode) { | ||
return new ErrorResponse(errorCode.getCode(), errorCode.getMessage(), null); | ||
} | ||
|
||
public static ErrorResponse of( | ||
ErrorCode errorCode, | ||
Object details | ||
) { | ||
return new ErrorResponse(errorCode.getCode(), errorCode.getMessage(), details); | ||
} | ||
|
||
public static ErrorResponse of(Optional<FieldError> fieldError) { | ||
return fieldError.map(error -> new ErrorResponse(error.getCode(), error.getDefaultMessage(), null)) | ||
.orElseGet(() -> ErrorResponse.of(INVALID_REQUEST_PARAM)); | ||
} | ||
|
||
public static ErrorResponse of(List<FieldError> fieldErrors) { | ||
Map<String, String> errors = fieldErrors.stream() | ||
.collect(Collectors.toMap(FieldError::getField, err -> err.getDefaultMessage() == null ? "null" : err.getDefaultMessage())); | ||
|
||
return ErrorResponse.of(INVALID_REQUEST_PARAM, errors); | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/com/partybbangbbang/global/exception/error/GlobalError.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.partybbangbbang.global.exception.error; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
|
||
import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||
import static org.springframework.http.HttpStatus.NOT_FOUND; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public enum GlobalError implements ErrorCode { | ||
|
||
GLOBAL_NOT_FOUND("리소스가 존재하지 않습니다.", NOT_FOUND, "G_001"), | ||
INVALID_REQUEST_PARAM("요청 파라미터가 유효하지 않습니다.", BAD_REQUEST, "G_002"); | ||
|
||
private final String message; | ||
private final HttpStatus status; | ||
private final String code; | ||
} |
94 changes: 94 additions & 0 deletions
94
src/main/java/com/partybbangbbang/global/security/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package com.partybbangbbang.global.security; | ||
|
||
import com.partybbangbbang.global.exception.ApiExceptionHandlingFilter; | ||
import com.partybbangbbang.global.security.matcher.CustomRequestMatcher; | ||
import com.partybbangbbang.global.security.filter.CustomAuthorizationFilter; | ||
import com.partybbangbbang.global.security.provider.CustomAuthenticationProvider; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.annotation.Order; | ||
import org.springframework.security.authentication.AuthenticationManager; | ||
import org.springframework.security.authentication.ProviderManager; | ||
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.crypto.factory.PasswordEncoderFactories; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.SecurityFilterChain; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | ||
import org.springframework.web.cors.CorsConfiguration; | ||
import org.springframework.web.cors.CorsConfigurationSource; | ||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; | ||
|
||
import java.util.List; | ||
|
||
@Configuration | ||
@EnableWebSecurity | ||
@RequiredArgsConstructor | ||
public class SecurityConfig { | ||
|
||
private final ApiExceptionHandlingFilter apiExceptionHandlingFilter; | ||
private final CustomAuthorizationFilter customAuthorizationFilter; | ||
private final CustomRequestMatcher customRequestMatcher; | ||
|
||
@Bean | ||
@Order(0) | ||
public SecurityFilterChain authFilterChain(HttpSecurity http) throws Exception { | ||
http.securityMatchers(matcher -> matcher.requestMatchers( | ||
customRequestMatcher.authEndpoints(), | ||
customRequestMatcher.errorEndpoints(), | ||
customRequestMatcher.userEndpoints() | ||
)) | ||
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll()) | ||
.addFilterBefore(apiExceptionHandlingFilter, UsernamePasswordAuthenticationFilter.class); | ||
|
||
return commonHttpSecurity(http).build(); | ||
} | ||
|
||
@Bean | ||
@Order(1) | ||
public SecurityFilterChain anyRequestFilterChain(HttpSecurity http) throws Exception { | ||
http.authorizeHttpRequests(auth -> auth | ||
.requestMatchers(new AntPathRequestMatcher("/api/v1/tteokguk/find/**")).hasAnyRole("ANONYMOUS", "USER") | ||
.anyRequest().hasRole("USER")) | ||
.addFilterAfter(customAuthorizationFilter, UsernamePasswordAuthenticationFilter.class) | ||
.addFilterBefore(apiExceptionHandlingFilter, UsernamePasswordAuthenticationFilter.class); | ||
|
||
return commonHttpSecurity(http).build(); | ||
} | ||
|
||
private HttpSecurity commonHttpSecurity(HttpSecurity http) throws Exception { | ||
return http | ||
.csrf(AbstractHttpConfigurer::disable) | ||
.cors(configurer -> corsConfigurationSource()) | ||
.formLogin(AbstractHttpConfigurer::disable) | ||
.httpBasic(AbstractHttpConfigurer::disable); | ||
} | ||
|
||
@Bean | ||
public PasswordEncoder passwordEncoder() { | ||
return PasswordEncoderFactories.createDelegatingPasswordEncoder(); | ||
} | ||
|
||
@Bean | ||
public AuthenticationManager authenticationManager(CustomAuthenticationProvider authenticationProvider) { | ||
ProviderManager providerManager = new ProviderManager(authenticationProvider); | ||
providerManager.setEraseCredentialsAfterAuthentication(false); | ||
return providerManager; | ||
} | ||
|
||
@Bean | ||
public CorsConfigurationSource corsConfigurationSource() { | ||
CorsConfiguration configuration = new CorsConfiguration(); | ||
configuration.setAllowedOriginPatterns(List.of("*")); | ||
configuration.setAllowedMethods(List.of("HEAD", "POST", "GET", "DELETE", "PUT")); | ||
configuration.setAllowedHeaders(List.of("*")); | ||
configuration.setAllowCredentials(true); | ||
|
||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); | ||
source.registerCorsConfiguration("/**", configuration); | ||
return source; | ||
} | ||
} |
Oops, something went wrong.