Skip to content

Commit

Permalink
Merge pull request #122 from Team-Tiki/fix/#106-exception-handler
Browse files Browse the repository at this point in the history
[FIX] Security Config 로직 수정
  • Loading branch information
paragon0107 authored Jul 19, 2024
2 parents 48617b2 + 42c5e74 commit 8cf418f
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 101 deletions.
14 changes: 5 additions & 9 deletions src/main/java/com/tiki/server/auth/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tiki.server.auth.config;

import com.tiki.server.auth.exception.handler.CustomAccessDeniedHandler;
import com.tiki.server.auth.exception.handler.CustomAuthenticationEntryPointHandler;
import com.tiki.server.auth.filter.ExceptionHandlerFilter;
import com.tiki.server.auth.filter.JwtAuthenticationFilter;
Expand All @@ -21,7 +20,6 @@
public class SecurityConfig {

private final CustomAuthenticationEntryPointHandler customAuthenticationEntryPointHandler;
private final CustomAccessDeniedHandler customAccessDeniedHandler;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final ExceptionHandlerFilter exceptionHandlerFilter;

Expand All @@ -37,16 +35,14 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.exceptionHandling(exceptionHandlingConfigurer ->
exceptionHandlingConfigurer
.accessDeniedHandler(customAccessDeniedHandler)
.authenticationEntryPoint(customAuthenticationEntryPointHandler))
.authorizeHttpRequests(request ->
request
.requestMatchers("/api/v1/auth/login").permitAll()
.requestMatchers("/api/v1/auth/password").permitAll()
.requestMatchers("/api/v1/auth/sign-in").permitAll()
.requestMatchers("/api/v1/auth/reissue").permitAll()
.requestMatchers("/api/v1/members/password").permitAll()
.requestMatchers("/api/v1/members").permitAll()
.requestMatchers("/api/v1/mail/**").permitAll()
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/actuator/health").permitAll()
.anyRequest()
.authenticated())
Expand All @@ -61,8 +57,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

private void permitSwaggerUri(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.requestMatchers(new AntPathRequestMatcher("/v3/api-docs/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/swagger-ui/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/docs/**")).permitAll());
.requestMatchers(new AntPathRequestMatcher("/v3/api-docs/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/swagger-ui/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/docs/**")).permitAll());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,4 @@ public ResponseEntity<SuccessResponse<ReissueGetResponse>> reissue(HttpServletRe
return ResponseEntity.created(UriGenerator.getUri("/"))
.body(SuccessResponse.success(SUCCESS_REISSUE_ACCESS_TOKEN.getMessage(), response));
}

@GetMapping("/{memberId}")
public String getAccessTokenForClient(@PathVariable long memberId) {
return authService.getAccessTokenForClient(memberId);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,33 +1,43 @@
package com.tiki.server.auth.exception.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tiki.server.auth.info.AuthenticationResponse;
import com.tiki.server.auth.message.ErrorCode;
import com.tiki.server.common.dto.ErrorResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import java.io.IOException;

import static com.tiki.server.auth.message.ErrorCode.UNAUTHENTICATED_USER;

@Slf4j
@Component
@RequiredArgsConstructor
public class CustomAuthenticationEntryPointHandler implements AuthenticationEntryPoint {

private final AuthenticationResponse authenticationResponse;
private final ObjectMapper objectMapper;

@Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException
) throws IOException {
authenticationResponse.makeFailureResponse(response, UNAUTHENTICATED_USER);
log.info("-EntryPoint-");
setResponse(response, ErrorCode.UNAUTHENTICATED_USER.getMessage());
}

private void setResponse(HttpServletResponse response, String errorMessage) throws IOException {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
val writer = response.getWriter();
writer.write(objectMapper.writeValueAsString(ErrorResponse.of(errorMessage)));
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package com.tiki.server.auth.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.tiki.server.auth.exception.AuthException;
import com.tiki.server.auth.message.ErrorCode;
import com.tiki.server.common.dto.ErrorResponse;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

Expand All @@ -17,12 +24,49 @@
@RequiredArgsConstructor
public class ExceptionHandlerFilter extends OncePerRequestFilter {

private final ObjectMapper objectMapper;

@Override
protected void doFilterInternal(
@NonNull HttpServletRequest request,
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws ServletException, IOException {
filterChain.doFilter(request, response);
) throws IOException {
try {
filterChain.doFilter(request, response);
} catch (AuthException e) {
log.info("ExceptionHandlerFilter: AuthException - " + e);
handleAuthException(response, e);
} catch (JwtException e) {
log.info("ExceptionHandlerFilter: JWTException - " + e);
handleJwtException(response);
} catch (Exception e) {
log.info("ExceptionHandlerFilter: Exception - " + e);
handleUncaughtException(response, e);
}
}

private void handleAuthException(HttpServletResponse response, AuthException e) throws IOException {
val errorMessage = e.getErrorCode().getMessage();
val httpStatus = e.getErrorCode().getHttpStatus();
setResponse(response, httpStatus, errorMessage);
}

private void handleJwtException(HttpServletResponse response) throws IOException {
val jwtException = ErrorCode.INVALID_JWT_TOKEN;
setResponse(response, jwtException.getHttpStatus(), jwtException.getMessage());
}

private void handleUncaughtException(HttpServletResponse response, Exception e) throws IOException {
val uncaughtException = ErrorCode.UNCAUGHT_EXCEPTION;
setResponse(response, uncaughtException.getHttpStatus(), uncaughtException.getMessage());
}

private void setResponse(HttpServletResponse response, HttpStatus httpStatus, String errorMessage) throws IOException {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.setStatus(httpStatus.value());
val writer = response.getWriter();
writer.write(objectMapper.writeValueAsString(ErrorResponse.of(errorMessage)));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tiki.server.auth.filter;


import com.tiki.server.auth.jwt.JwtProvider;
import com.tiki.server.auth.jwt.JwtValidator;
import com.tiki.server.auth.jwt.UserAuthentication;
Expand All @@ -15,13 +14,11 @@
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

import static com.tiki.server.auth.jwt.JwtValidationType.VALID_JWT;
import static io.jsonwebtoken.lang.Strings.hasText;

@Slf4j
@Component
@RequiredArgsConstructor
Expand All @@ -36,17 +33,17 @@ protected void doFilterInternal(
@NonNull HttpServletResponse response,
@NonNull FilterChain filterChain
) throws IOException, ServletException {
try {
val token = jwtProvider.getTokenFromRequest(request);
if (hasText(token) && jwtValidator.validateToken(token) == VALID_JWT) {
val authentication = new UserAuthentication(jwtProvider.getUserFromJwt(token), null, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
log.info(e.getMessage());
val token = jwtProvider.getTokenFromRequest(request);
if (StringUtils.hasText(token)) {
jwtValidator.validateToken(token);
setAuthenticationContextHolder(jwtProvider.getUserFromJwt((token)), request);
}

filterChain.doFilter(request, response);
}

private void setAuthenticationContextHolder(long memberId, HttpServletRequest request) {
val authentication = new UserAuthentication(memberId, null, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
4 changes: 1 addition & 3 deletions src/main/java/com/tiki/server/auth/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tiki.server.auth.jwt;

import com.tiki.server.auth.exception.AuthException;
import com.tiki.server.common.Constants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
Expand All @@ -13,7 +12,6 @@

import javax.crypto.SecretKey;

import static com.tiki.server.auth.message.ErrorCode.INVALID_KEY;
import static io.jsonwebtoken.security.Keys.hmacShaKeyFor;
import static java.util.Base64.getEncoder;

Expand All @@ -27,7 +25,7 @@ public class JwtProvider {
public String getTokenFromRequest(HttpServletRequest request) {
val accessToken = request.getHeader(Constants.AUTHORIZATION);
if (!StringUtils.hasText(accessToken) || !accessToken.startsWith(Constants.BEARER)) {
throw new AuthException(INVALID_KEY);
return null;
}
return accessToken.substring(Constants.BEARER.length());
}
Expand Down
9 changes: 0 additions & 9 deletions src/main/java/com/tiki/server/auth/jwt/JwtValidationType.java

This file was deleted.

22 changes: 11 additions & 11 deletions src/main/java/com/tiki/server/auth/jwt/JwtValidator.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.tiki.server.auth.jwt;

import com.tiki.server.auth.exception.AuthException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import static com.tiki.server.auth.jwt.JwtValidationType.*;
import static com.tiki.server.auth.message.ErrorCode.*;

@Slf4j
@RequiredArgsConstructor
Expand All @@ -16,22 +17,21 @@ public class JwtValidator {

private final JwtProvider jwtProvider;

public JwtValidationType validateToken(String token) {
public void validateToken(String token) {
try {
jwtProvider.getBodyFromJwt(token);
return VALID_JWT;
} catch (MalformedJwtException exception) {
log.error(exception.getMessage());
return INVALID_JWT_TOKEN;
log.info(exception.getMessage());
throw new AuthException(INVALID_JWT_TOKEN);
} catch (ExpiredJwtException exception) {
log.error(exception.getMessage());
return EXPIRED_JWT_TOKEN;
log.info(exception.getMessage());
throw new AuthException(EXPIRED_JWT_TOKEN);
} catch (UnsupportedJwtException exception) {
log.error(exception.getMessage());
return UNSUPPORTED_JWT_TOKEN;
log.info(exception.getMessage());
throw new AuthException(UNSUPPORTED_JWT_TOKEN);
} catch (IllegalArgumentException exception) {
log.error(exception.getMessage());
return EMPTY_JWT;
log.info(exception.getMessage());
throw new AuthException(EMPTY_JWT);
}
}
}
16 changes: 11 additions & 5 deletions src/main/java/com/tiki/server/auth/message/ErrorCode.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,30 @@
package com.tiki.server.auth.message;

import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.http.HttpStatus.FORBIDDEN;

import org.springframework.http.HttpStatus;

import lombok.AllArgsConstructor;
import lombok.Getter;

import static org.springframework.http.HttpStatus.*;

@Getter
@AllArgsConstructor
public enum ErrorCode {

/* 401 UNAUTHORIZED : 인증 없음 */
UNAUTHENTICATED_USER(UNAUTHORIZED, "인증되지 않은 사용자입니다."),
UNAUTHENTICATED_USER(UNAUTHORIZED, "잘못된 토큰 형식입니다."),
INVALID_KEY(UNAUTHORIZED, "유효하지 않은 키입니다."),
UNMATCHED_TOKEN(UNAUTHORIZED, "토큰이 일치하지 않습니다."),
INVALID_JWT_TOKEN(UNAUTHORIZED, "잘못된 토큰 형식입니다."),
EXPIRED_JWT_TOKEN(UNAUTHORIZED, "만료된 토큰입니다."),
UNSUPPORTED_JWT_TOKEN(UNAUTHORIZED, "지원하지 않은 토큰입니다."),
EMPTY_JWT(UNAUTHORIZED, "빈 토큰입니다."),

/* 403 FORBIDDEN : 인가 없음 */
UNAUTHORIZED_USER(FORBIDDEN, "권한이 없는 사용자입니다.");
UNAUTHORIZED_USER(FORBIDDEN, "권한이 없는 사용자입니다."),

/* 500 INTERNAL_SERVER_ERROR : 서버 내부 오류입니다. */
UNCAUGHT_EXCEPTION(INTERNAL_SERVER_ERROR, "서버 내부 오류입니다.");

private final HttpStatus httpStatus;
private final String message;
Expand Down
6 changes: 0 additions & 6 deletions src/main/java/com/tiki/server/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import com.tiki.server.member.adapter.MemberFinder;
import com.tiki.server.member.entity.Member;
import com.tiki.server.member.exception.MemberException;
import com.tiki.server.auth.utils.CookieUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
Expand Down Expand Up @@ -61,11 +60,6 @@ public ReissueGetResponse reissueToken(HttpServletRequest request) {
return ReissueGetResponse.from(accessToken);
}

public String getAccessTokenForClient(long memberId) {
val authentication = createAuthentication(memberId);
return jwtGenerator.generateToken(authentication, 12096000L);
}

private Member checkMemberEmpty(LoginRequest request) {
return memberFinder.findByEmail(request.email()).orElseThrow(() -> new MemberException(INVALID_MEMBER));
}
Expand Down

0 comments on commit 8cf418f

Please sign in to comment.