Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] Security Config 로직 수정 #122

Merged
merged 12 commits into from
Jul 19, 2024
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;
paragon0107 marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading