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

NO-JIRA---jwt토큰 반환값 hotfix #79

Merged
merged 4 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.prgrms.nabimarketbe.global.security.handler.CustomAccessDeniedHandler;
import org.prgrms.nabimarketbe.global.security.handler.CustomAuthenticationEntryPoint;
import org.prgrms.nabimarketbe.global.security.jwt.filter.JwtAuthenticationFilter;
import org.prgrms.nabimarketbe.global.security.jwt.filter.JwtExceptionFilter;
import org.prgrms.nabimarketbe.global.security.jwt.provider.JwtProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -29,6 +30,10 @@ public class SecurityConfiguration {

private final CustomAccessDeniedHandler customAccessDeniedHandler;

private final JwtAuthenticationFilter jwtAuthenticationFilter;

private final JwtExceptionFilter jwtExceptionFilter;

@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
Expand All @@ -51,14 +56,17 @@ public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws
.authenticationEntryPoint(customAuthenticationEntryPoint)
.accessDeniedHandler(customAccessDeniedHandler)
)
.addFilterBefore(new JwtAuthenticationFilter(jwtProvider), UsernamePasswordAuthenticationFilter.class);
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtExceptionFilter, JwtAuthenticationFilter.class);

return http.build();
}

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

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public enum ErrorCode {
USER_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "U0001", "존재하지 않는 사용자입니다."),
USER_NOT_MATCHED(HttpStatus.INTERNAL_SERVER_ERROR, "UOO02", "적합한 사용자가 아닙니다."),
USER_NICKNAME_NOT_UNIQUE(HttpStatus.BAD_REQUEST, "UOO03", "이미 존재하는 닉네임입니다."),
USER_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED, "U0004", "만료된 토큰입니다."),
USER_TOKEN_NOT_VALID(HttpStatus.UNAUTHORIZED, "U0005", "잘못된 Jwt 서명입니다."),
USER_TOKEN_NOT_SUPPORTED(HttpStatus.UNAUTHORIZED, "U0006", "지원하지 않는 토큰입니다."),
CARD_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "C0001", "존재하지 않는 카드입니다."),
DIB_NOT_FOUND(HttpStatus.BAD_REQUEST, "D0001", "존재하지 않는 찜입니다."),
DIB_MYSELF_ERROR(HttpStatus.BAD_REQUEST, "D0002", "자신의 카드는 찜할 수 없습니다."),
Expand All @@ -28,8 +31,8 @@ public enum ErrorCode {
CATEGORY_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "CA0001", "존재하지 않는 카테고리입니다."),
COMPLETE_REQUEST_NOT_FOUND(HttpStatus.INTERNAL_SERVER_ERROR, "CR0001", "존재하지 않는 거래 성사 요청입니다."),
COMPLETE_REQUEST_MYSELF_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "CR0002", "자신의 카드에는 거래 성사 요청을 할 수 없습니다."),
COMPLETE_REQUEST_FROM_CARD_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR,"CR0002", "거래 성사 요청을 이미 보낸 카드입니다."),
COMPLETE_REQUEST_TO_CARD_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR,"CR0002", "거래 성사 요청을 이미 받은 카드입니다."),
COMPLETE_REQUEST_FROM_CARD_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR, "CR0002", "거래 성사 요청을 이미 보낸 카드입니다."),
COMPLETE_REQUEST_TO_CARD_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR, "CR0002", "거래 성사 요청을 이미 받은 카드입니다."),
COMPLETE_REQUEST_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR, "CR0003", "이미 거래 성사 요청이 존재합니다"),
BATCH_INSERT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "B0001", "이미지 저장 중에 문제가 발생했습니다."),
INVALID_ORDER_CONDITION(HttpStatus.INTERNAL_SERVER_ERROR, "O0001", "유효하지 않은 정렬 조건입니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.prgrms.nabimarketbe.global.security.jwt.dto;

public record TokenErrorResponseDTO(
String message
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,35 @@
import org.prgrms.nabimarketbe.global.security.jwt.provider.JwtProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RequiredArgsConstructor
@Slf4j
@Component
public class JwtAuthenticationFilter extends GenericFilterBean {
private final JwtProvider jwtProvider;

// request 로 들어오는 Jwt 의 유효성을 검증 - JwtProvider.validationToken() 을 필터로서 FilterChain 에 추가
@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain filterChain
ServletRequest request,
ServletResponse response,
FilterChain filterChain
) throws IOException, ServletException {
// request 에서 token 을 취한다.
String token = jwtProvider.resolveToken((HttpServletRequest) request);
String token = jwtProvider.resolveToken((HttpServletRequest)request);

// 검증
log.info("[Verifying token]");
log.info(((HttpServletRequest) request).getRequestURL().toString());
log.info(((HttpServletRequest)request).getRequestURL().toString());

// 매번 필터탈때마다 select쿼리
if (token != null && jwtProvider.validationToken(token)) {
if (token != null) {
jwtProvider.validationToken(token);
Authentication authentication = jwtProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.prgrms.nabimarketbe.global.security.jwt.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.prgrms.nabimarketbe.global.security.jwt.dto.TokenErrorResponseDTO;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.jsonwebtoken.JwtException;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Component
public class JwtExceptionFilter extends OncePerRequestFilter {

private final ObjectMapper objectMapper;

@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain) throws
ServletException, IOException {
try {
filterChain.doFilter(httpServletRequest, httpServletResponse);
} catch (JwtException ex) {
setErrorResponse(HttpStatus.UNAUTHORIZED, httpServletResponse, ex);
}
}

private void setErrorResponse(HttpStatus status, HttpServletResponse res, Throwable ex) throws IOException {
res.setStatus(status.value());
res.setContentType("application/json; charset=UTF-8");

TokenErrorResponseDTO tokenErrorResponseDTO = new TokenErrorResponseDTO(ex.getMessage());
res.getWriter().write(objectMapper.writeValueAsString(tokenErrorResponseDTO));
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package org.prgrms.nabimarketbe.global.security.jwt.provider;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.Base64UrlCodec;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Optional;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;

import org.prgrms.nabimarketbe.global.error.ErrorCode;
import org.prgrms.nabimarketbe.global.security.entity.RefreshToken;
import org.prgrms.nabimarketbe.global.security.jwt.dto.TokenResponseDTO;
import org.prgrms.nabimarketbe.global.security.jwt.repository.RefreshTokenRepository;
Expand All @@ -20,12 +19,17 @@
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.transaction.Transactional;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Optional;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.Base64UrlCodec;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
Expand Down Expand Up @@ -83,7 +87,6 @@ public TokenResponseDTO createTokenDTO(Long userPk, String role) {
() -> refreshTokenRepository.save(refreshTokenEntity)
);


return TokenResponseDTO.builder()
.grantType("Bearer")
.accessToken(accessToken)
Expand Down Expand Up @@ -121,20 +124,21 @@ public String resolveToken(HttpServletRequest request) {
}

// jwt 의 유효성 및 만료일자 확인
public boolean validationToken(String token) {
public void validationToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException e) {
log.error("잘못된 Jwt 서명입니다.");
throw new JwtException(ErrorCode.USER_TOKEN_NOT_VALID.getMessage());
} catch (ExpiredJwtException e) {
log.error("만료된 토큰입니다.");
throw new JwtException(ErrorCode.USER_TOKEN_EXPIRED.getMessage());
} catch (UnsupportedJwtException e) {
log.error("지원하지 않는 토큰입니다.");
} catch (IllegalArgumentException e) {
throw new JwtException(ErrorCode.USER_TOKEN_NOT_SUPPORTED.getMessage());
} catch (Exception e) {
log.error("잘못된 토큰입니다.");
throw new JwtException(ErrorCode.USER_TOKEN_NOT_VALID.getMessage());
}

return false;
}
}