Skip to content

Commit

Permalink
Merge pull request #85 from Map-Pin/issue/81
Browse files Browse the repository at this point in the history
Issue/81
  • Loading branch information
seungheon123 authored Nov 28, 2023
2 parents 61f6742 + 3e6bce7 commit 0e45838
Show file tree
Hide file tree
Showing 68 changed files with 1,712 additions and 1,185 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.server.mappin.auth.config;

import com.server.mappin.auth.filter.JwtExceptionFilter;
import com.server.mappin.auth.filter.JwtRequestFilter;
import com.server.mappin.auth.token.TokenProvider;
import lombok.RequiredArgsConstructor;
Expand All @@ -12,9 +13,12 @@
public class JwtSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenProvider tokenProvider;


@Override
public void configure(HttpSecurity http){
JwtRequestFilter filter = new JwtRequestFilter(tokenProvider);
JwtExceptionFilter exceptionFilter = new JwtExceptionFilter();
http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(exceptionFilter, JwtRequestFilter.class);
}
}
23 changes: 19 additions & 4 deletions src/main/java/com/server/mappin/auth/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;

Expand All @@ -18,23 +19,37 @@ public class SecurityConfig {
private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

// @Bean
// public WebSecurityCustomizer webSecurityCustomizer(){
// return (web) -> web.ignoring()
// .antMatchers(
// "/favicon.ico",
// "/swagger-ui.html",
// "/v3/api-docs",
// "/v3/api-docs/**",
// "/swagger-ui/**"
// );
// }

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
.cors()
.and()
.csrf().disable()

.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler)
.and()

.formLogin().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login/**","/swagger-ui/**","/v3/api-docs/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler)
.and()
.apply(new JwtSecurityConfig(tokenProvider));
return http.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.server.mappin.auth.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.server.mappin.common.ErrorReasonDto;
import com.server.mappin.common.status.ErrorStatus;
import com.server.mappin.exception.handler.JwtHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@RequiredArgsConstructor
public class JwtExceptionFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException{
try{
filterChain.doFilter(request,response);
}catch (JwtHandler handler){

response.setContentType("application/json; charset=UTF-8");

String errorName = handler.getMessage();
ErrorStatus errorStatus = ErrorStatus.valueOf(errorName);

ErrorReasonDto errorReasonDto = ErrorReasonDto.builder()
.httpStatus(errorStatus.getHttpStatus())
.code(errorStatus.getCode())
.message(errorStatus.getMessage())
.success(false)
.build();

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(errorReasonDto);
response.getWriter().write(json);
response.getWriter().flush();
response.getWriter().close();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.server.mappin.auth.config.SecurityConfig;
import com.server.mappin.auth.token.TokenProvider;
import com.server.mappin.common.status.ErrorStatus;
import com.server.mappin.exception.handler.JwtHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
Expand All @@ -27,9 +29,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
throws ServletException, IOException{
String token = tokenProvider.resolveToken(request);

if(request.getRequestURI().startsWith("/swagger-ui/") || request.getRequestURI().startsWith("/v3/") || request.getRequestURI().startsWith("/login")){
filterChain.doFilter(request,response);
return;
}
if(StringUtils.hasText(token) && tokenProvider.validateToken(token)){
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}else{
throw new JwtHandler(ErrorStatus.JWT_TOKEN_NOT_FOUND);
}
filterChain.doFilter(request,response);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.server.mappin.auth.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.server.mappin.common.ErrorReasonDto;
import com.server.mappin.common.status.ErrorStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
Expand All @@ -16,8 +20,23 @@ public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException)
throws IOException, ServletException {

response.setContentType("application/json; charset=UTF-8");
response.setStatus(HttpStatus.FORBIDDEN.value());
ErrorReasonDto errorReasonDto = ErrorReasonDto.builder()
.httpStatus(ErrorStatus._FORBIDDEN.getHttpStatus())
.code(ErrorStatus._FORBIDDEN.getCode())
.message(ErrorStatus._FORBIDDEN.getMessage())
.success(false)
.build();

log.error("Access Denied error");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
//response.sendError(HttpServletResponse.SC_FORBIDDEN);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(errorReasonDto);
response.getWriter().write(json);
response.getWriter().flush();
response.getWriter().close();
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.server.mappin.auth.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.server.mappin.common.ErrorReasonDto;
import com.server.mappin.common.status.ErrorStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
Expand All @@ -16,8 +20,23 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authExeption)
throws IOException, ServletException{
response.setContentType("application/json; charset=UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
ErrorReasonDto errorReasonDto = ErrorReasonDto.builder()
.httpStatus(ErrorStatus._UNAUTHORIZED.getHttpStatus())
.code(ErrorStatus._UNAUTHORIZED.getCode())
.message(ErrorStatus._UNAUTHORIZED.getMessage())
.success(false)
.build();

log.error("Unathroized error");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
//response.sendError(HttpServletResponse.SC_UNAUTHORIZED);

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(errorReasonDto);
response.getWriter().write(json);
response.getWriter().flush();
response.getWriter().close();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.server.mappin.auth.token;

import com.server.mappin.common.status.ErrorStatus;
import com.server.mappin.exception.handler.JwtHandler;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
Expand Down Expand Up @@ -96,14 +98,17 @@ public boolean validateToken(String token){
return true;
}catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e){
log.info("잘못된 JWT 서명입니다");
throw new JwtHandler(ErrorStatus.JWT_BAD_REQUEST);
}catch (ExpiredJwtException e){
log.info("만료된 JWT 토큰입니다");
throw new JwtHandler(ErrorStatus.JWT_ACCESS_TOKEN_EXPIRED);
}catch (UnsupportedJwtException e){
log.info("지원되지 않는 JWT 토큰입니다");
throw new JwtHandler(ErrorStatus.JWT_TOKEN_UNSUPPORTED);
}catch (IllegalArgumentException e){
log.info("JWT 토큰이 잘못되었습니다");
throw new JwtHandler(ErrorStatus.JWT_BAD_REQUEST);
}
return false;
}

//Request 헤더에서 토큰 꺼내오기
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/com/server/mappin/common/BaseErrorCode.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.server.mappin.common;


public interface BaseErrorCode {
ErrorReasonDto getReason();

ErrorReasonDto getReasonHttpStatus();
}
33 changes: 33 additions & 0 deletions src/main/java/com/server/mappin/common/BaseResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.server.mappin.common;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.server.mappin.common.status.SuccessStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
@JsonPropertyOrder({"success", "code", "message", "result"})
@Schema(description = "기본 응답")
public class BaseResponseDto<T> {
private final boolean success;
private final String code;
private final String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T result;

// 성공한 경우 응답 생성
public static <T> BaseResponseDto<T> onSuccess(T data){
return new BaseResponseDto<>(true, SuccessStatus._OK.getCode(), SuccessStatus._OK.getMessage(), data);
}
public static <T> BaseResponseDto<T> of(String message, String code, T data){
return new BaseResponseDto<>(true, code, message, data);
}

// 실패한 경우 응답 생성
public static <T> BaseResponseDto<T> onFailure(String message, String code, T data) {
return new BaseResponseDto<>(false, code, message, data);
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/server/mappin/common/ErrorReasonDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.server.mappin.common;

import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@Builder
public class ErrorReasonDto {
private final HttpStatus httpStatus;
private final boolean success;
private final String code;
private final String message;

}
67 changes: 67 additions & 0 deletions src/main/java/com/server/mappin/common/status/ErrorStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.server.mappin.common.status;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.server.mappin.common.BaseErrorCode;
import com.server.mappin.common.ErrorReasonDto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;



@Getter
@AllArgsConstructor
public enum ErrorStatus implements BaseErrorCode {
//일반적인 응답
_INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "서버 에러, 관리자에게 문의 바랍니다."),
_BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."),
_UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."),
_FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."),
// 멤버 관려 에러
MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."),
NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."),

// 예시,,,
ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."),

// MapService Error
MAP_DONG_NOT_FOUND(HttpStatus.NOT_FOUND, "DONG4001", "동이 존재하지 않습니다"),
MAP_NO_DOCUMENT(HttpStatus.NOT_FOUND, "MAP4001", "문서가 존재하지 않습니다"),

// S3Service ERRor
S3_NOT_CONVERTABLE(HttpStatus.NOT_MODIFIED, "S33004", "변환이 안됩니다"),
S3_UPLOAD_FAILED(HttpStatus.FORBIDDEN, "S34003", "S3 업로드에 실패하셨습니다"),
S3_WRONG_PATH(HttpStatus.BAD_REQUEST, "S34000", "S3 요청 Path가 잘못되었습니다"),
S3_URL_NOT_FOUND(HttpStatus.NOT_FOUND, "S34001", "S3 URL을 가져올 수 없습니다"),
S3_DELETE_FAILED(HttpStatus.FORBIDDEN, "S34003", "S3 삭제에 실패하셨습니다"),
TEMP_EXCEPTION(HttpStatus.BAD_REQUEST, "TEMP4001", "테스트"),
//JWT 토큰 관련 에러
JWT_BAD_REQUEST(HttpStatus.BAD_REQUEST,"TOKEN400","잘못된 JWT 서명입니다."),
JWT_TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "TOKEN401","유효한 JWT 토큰이 없습니다"),
JWT_ACCESS_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED,"TOKEN402","액세스 토큰이 만료되었습니다"),
JWT_TOKEN_UNSUPPORTED(HttpStatus.UNAUTHORIZED,"TOKEN403","지원하지 않는 JWT 토큰입니다");

private final HttpStatus httpStatus;
private final String code;
private final String message;

@Override
public ErrorReasonDto getReason(){
return ErrorReasonDto.builder()
.message(message)
.code(code)
.success(false)
.build();
}

@Override
public ErrorReasonDto getReasonHttpStatus(){
return ErrorReasonDto.builder()
.httpStatus(httpStatus)
.message(message)
.code(code)
.success(false)
.build();
}
}
16 changes: 16 additions & 0 deletions src/main/java/com/server/mappin/common/status/SuccessStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.server.mappin.common.status;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum SuccessStatus {
_OK(HttpStatus.OK,"COMMON200","성공입니다");

private final HttpStatus httpStatus;
private final String code;
private final String message;

}
Loading

0 comments on commit 0e45838

Please sign in to comment.