Feat: 로그인 기능
This commit is contained in:
commit
63834ccbb9
31
.gitignore
vendored
31
.gitignore
vendored
@ -1,2 +1,33 @@
|
||||
.DS_Store
|
||||
application.yml
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
@ -55,6 +55,9 @@ dependencies {
|
||||
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
|
||||
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
|
||||
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
|
||||
|
||||
//Swagger
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2'
|
||||
}
|
||||
|
||||
tasks.named('test') {
|
||||
|
@ -0,0 +1,58 @@
|
||||
package com.worlabel.global.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||
import io.swagger.v3.oas.annotations.info.Info;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||
import org.springdoc.core.customizers.OperationCustomizer;
|
||||
import org.springdoc.core.models.GroupedOpenApi;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@OpenAPIDefinition(
|
||||
info = @Info(
|
||||
title = "auto labeling API",
|
||||
description = "auto labeling API 목록입니다.",
|
||||
version = "v1.0"
|
||||
)
|
||||
)
|
||||
@SecurityScheme(
|
||||
name = "Authorization",
|
||||
type = SecuritySchemeType.HTTP,
|
||||
bearerFormat = "JWT",
|
||||
in = SecuritySchemeIn.HEADER,
|
||||
scheme = "Bearer",
|
||||
description = "access token"
|
||||
)
|
||||
public class SwaggerConfig {
|
||||
|
||||
private final String[] noRequiredTokenApi = {"/register", "/login", "/reissue","/login/oauth", "/oauth2/**",
|
||||
"/login/oauth2/**", "/error", "login/oauth2/code/kakao", "/register/duplicate", "/test"};
|
||||
|
||||
private final OperationCustomizer operationCustomizer;
|
||||
|
||||
public SwaggerConfig(OperationCustomizer operationCustomizer) {
|
||||
this.operationCustomizer = operationCustomizer;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi nonSecurityGroup(){ //jwt 토큰 불필요한 api
|
||||
return GroupedOpenApi.builder()
|
||||
.group("token 불필요 API")
|
||||
.pathsToMatch(noRequiredTokenApi)
|
||||
.addOperationCustomizer(operationCustomizer)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GroupedOpenApi securityGroup(){ //jwt 토큰 필요한 api
|
||||
return GroupedOpenApi.builder()
|
||||
.group("token 필요 API")
|
||||
.pathsToExclude(noRequiredTokenApi)
|
||||
.addOperationCustomizer(operationCustomizer)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
package com.worlabel.global.config.swagger;
|
||||
|
||||
import com.worlabel.global.exception.CustomException;
|
||||
import com.worlabel.global.response.BaseResponse;
|
||||
import com.worlabel.global.response.CustomError;
|
||||
import com.worlabel.global.response.ErrorResponse;
|
||||
import io.swagger.v3.oas.models.Operation;
|
||||
import io.swagger.v3.oas.models.examples.Example;
|
||||
import io.swagger.v3.oas.models.media.Content;
|
||||
import io.swagger.v3.oas.models.media.MediaType;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.models.responses.ApiResponses;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import org.springdoc.core.customizers.OperationCustomizer;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import com.worlabel.global.exception.ErrorCode;
|
||||
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
public class CustomOperationCustomizer implements OperationCustomizer {
|
||||
|
||||
@Override
|
||||
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
|
||||
SwaggerApiError swaggerApiError = handlerMethod.getMethodAnnotation(SwaggerApiError.class);
|
||||
operation.getResponses().remove("500");
|
||||
if (swaggerApiError != null) {
|
||||
generateErrorCodeResponse(operation, swaggerApiError.value());
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
|
||||
//에러 코드로 error response 만들어 ApiResponse 에 넣음
|
||||
private void generateErrorCodeResponse(Operation operation, ErrorCode[] errorCodes) {
|
||||
ApiResponses responses = operation.getResponses();
|
||||
|
||||
Map<Integer, List<BaseResponse<CustomError>>> statusWithErrorResponse = Arrays.stream(errorCodes)
|
||||
.map(errorCode -> ErrorResponse.of(new CustomException(errorCode)))
|
||||
.collect(Collectors.groupingBy(BaseResponse::getStatus));
|
||||
|
||||
addErrorCodesToResponse(responses, statusWithErrorResponse);
|
||||
}
|
||||
|
||||
//ApiResponses에 error response 추가
|
||||
private void addErrorCodesToResponse(ApiResponses apiResponses, Map<Integer, List<BaseResponse<CustomError>>> responses) {
|
||||
responses.forEach((status, value) -> {
|
||||
Content content = new Content();
|
||||
MediaType mediaType = new MediaType();
|
||||
ApiResponse apiResponse = new ApiResponse();
|
||||
|
||||
value.forEach(
|
||||
errorInfoResponse -> {
|
||||
Example example = new Example();
|
||||
example.setValue(errorInfoResponse);
|
||||
mediaType.addExamples(String.valueOf(errorInfoResponse.getCode()), example);
|
||||
});
|
||||
|
||||
content.addMediaType("application/json", mediaType);
|
||||
apiResponse.setContent(content);
|
||||
apiResponses.addApiResponse(String.valueOf(status), apiResponse);
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.worlabel.global.config.swagger;
|
||||
|
||||
import com.worlabel.global.exception.ErrorCode;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface SwaggerApiError {
|
||||
|
||||
ErrorCode[] value();
|
||||
}
|
||||
|
@ -0,0 +1,20 @@
|
||||
package com.worlabel.global.config.swagger;
|
||||
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponses;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200")
|
||||
})
|
||||
public @interface SwaggerApiSuccess {
|
||||
|
||||
String description() default "";
|
||||
}
|
||||
|
@ -22,6 +22,11 @@ public abstract class BaseResponse<T> {
|
||||
@Getter(onMethod_ = @JsonProperty("isSuccess"))
|
||||
private boolean isSuccess;
|
||||
|
||||
/**
|
||||
* 상태 코드
|
||||
*/
|
||||
private int status;
|
||||
|
||||
/**
|
||||
* 응답 코드
|
||||
*/
|
||||
@ -42,8 +47,9 @@ public abstract class BaseResponse<T> {
|
||||
*/
|
||||
protected List<CustomError> errors;
|
||||
|
||||
public BaseResponse(boolean isSuccess, int code, String message) {
|
||||
public BaseResponse(boolean isSuccess, int status, int code, String message) {
|
||||
this.isSuccess = isSuccess;
|
||||
this.status = status;
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
this.data = null;
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.worlabel.global.response;
|
||||
|
||||
import com.worlabel.global.exception.CustomException;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.validation.Errors;
|
||||
@ -16,19 +17,22 @@ import java.util.stream.Collectors;
|
||||
* 에러 발생시 리턴 할 에러 응답 객체
|
||||
*/
|
||||
@Slf4j
|
||||
public class ErrorResponse extends BaseResponse<Void> {
|
||||
@Getter
|
||||
public class ErrorResponse extends BaseResponse<CustomError> {
|
||||
|
||||
public ErrorResponse(boolean isSuccess, int code, String message, Errors errors) {
|
||||
super(isSuccess, code, message);
|
||||
|
||||
|
||||
public ErrorResponse(boolean isSuccess, int status, int code, String message, Errors errors) {
|
||||
super(isSuccess, status, code, message);
|
||||
super.errors = parseErrors(errors);
|
||||
}
|
||||
|
||||
public ErrorResponse(CustomException exception) {
|
||||
this(false, exception.getErrorCode().getCode(), exception.getMessage(), exception.getErrors());
|
||||
this(false, exception.getErrorCode().getStatus().value(), exception.getErrorCode().getCode(), exception.getMessage(), exception.getErrors());
|
||||
}
|
||||
|
||||
public ErrorResponse(CustomException exception, String message) {
|
||||
this(false, 1, message, exception.getErrors());
|
||||
this(false, exception.getErrorCode().getStatus().value(), 1, message, exception.getErrors());
|
||||
}
|
||||
|
||||
public static ErrorResponse of(CustomException exception) {
|
||||
@ -40,7 +44,7 @@ public class ErrorResponse extends BaseResponse<Void> {
|
||||
}
|
||||
|
||||
public static ErrorResponse of(Exception exception) {
|
||||
return new ErrorResponse(false, HttpStatus.INTERNAL_SERVER_ERROR.value(), exception.getMessage(), null);
|
||||
return new ErrorResponse(false, HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.value(), exception.getMessage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,8 +63,8 @@ public class ErrorResponse extends BaseResponse<Void> {
|
||||
// 글로벌 에러 리스트 생성
|
||||
List<CustomError> globalErrors = errors.getGlobalErrors().stream()
|
||||
.map(e -> new CustomError(null, // 필드 이름이 없으므로 null
|
||||
e.getCode(),
|
||||
e.getDefaultMessage(),
|
||||
e.getCode(),
|
||||
e.getDefaultMessage(),
|
||||
e.getObjectName()
|
||||
))
|
||||
.toList();
|
||||
|
@ -17,7 +17,7 @@ public class SuccessResponse<T> extends BaseResponse<T> {
|
||||
* 성공 응답 객체 생성자
|
||||
*/
|
||||
public SuccessResponse() {
|
||||
super(true, HttpStatus.OK.value(), "success");
|
||||
super(true, HttpStatus.OK.value(), 200, "success");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -26,7 +26,7 @@ public class SuccessResponse<T> extends BaseResponse<T> {
|
||||
* @param data 성공시 반환하는 데이터
|
||||
*/
|
||||
public SuccessResponse(T data) {
|
||||
super(true, 200, "success");
|
||||
super(true, 200, 200, "success");
|
||||
super.data = data;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user