Skip to content

Commit

Permalink
Merge pull request #30 from inhooo00/feature/filter
Browse files Browse the repository at this point in the history
Feat(#29) 필터 기능 구현
  • Loading branch information
inhooo00 authored Jul 28, 2024
2 parents 8c18664 + 5bda735 commit 86e935d
Show file tree
Hide file tree
Showing 18 changed files with 586 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ public class AuthController {
private final AuthServiceFactory authServiceFactory;
private final AuthMemberService memberService;
private final TokenService tokenService;
private final GoogleAuthService getGoogleAccessToken;
private final KakaoAuthService kakaoAuthService;

@GetMapping("oauth2/callback/google")
public JsonNode googleCallback(@RequestParam(name = "code") String code) {
return getGoogleAccessToken.getGoogleIdToken(code);
AuthService googleAuthService = authServiceFactory.getAuthService("google");
return googleAuthService.getIdToken(code);
}

@GetMapping("oauth2/callback/kakao")
public JsonNode kakaoCallback(@RequestParam(name = "code") String code) {
return kakaoAuthService.getKakaoAccessToken(code);
AuthService kakaoAuthService = authServiceFactory.getAuthService("kakao");
return kakaoAuthService.getIdToken(code);
}

// @Operation(summary = "로그인 후 토큰 발급", description = "액세스, 리프레쉬 토큰을 발급합니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import org.springframework.transaction.annotation.Transactional;
import shop.kkeujeok.kkeujeokbackend.auth.api.dto.response.MemberLoginResDto;
import shop.kkeujeok.kkeujeokbackend.auth.api.dto.response.UserInfo;
import shop.kkeujeok.kkeujeokbackend.auth.exception.EmailNotFoundException;
import shop.kkeujeok.kkeujeokbackend.auth.exception.ExistsMemberEmailException;
import shop.kkeujeok.kkeujeokbackend.global.entity.Status;
import shop.kkeujeok.kkeujeokbackend.member.domain.Member;
import shop.kkeujeok.kkeujeokbackend.member.domain.Role;
Expand Down Expand Up @@ -34,7 +36,7 @@ public MemberLoginResDto saveUserInfo(UserInfo userInfo, SocialType provider) {

private void validateNotFoundEmail(String email) {
if (email == null) {
throw new RuntimeException();
throw new EmailNotFoundException();
}
}

Expand All @@ -44,14 +46,8 @@ private Member getExistingMemberOrCreateNew(UserInfo userInfo, SocialType provid

private Member createMember(UserInfo userInfo, SocialType provider) {
String userPicture = getUserPicture(userInfo.picture());
String name = userInfo.name();
String nickname = userInfo.nickname();

if (name == null && nickname != null) {
name = nickname;
} else if (nickname == null && name != null) {
nickname = name;
}
String name = unionName(userInfo.name(), userInfo.nickname());
String nickname = unionNickname(userInfo.name(), userInfo.nickname());

return memberRepository.save(
Member.builder()
Expand All @@ -67,9 +63,25 @@ private Member createMember(UserInfo userInfo, SocialType provider) {
);
}

private String unionName(String name, String nickname) {
if (name == null && nickname != null) {
return nickname;
} else if (nickname == null && name != null) {
return name;
}
return name;
}

private String unionNickname(String name, String nickname) {
if (nickname == null) {
return name;
}
return nickname;
}
private String getUserPicture(String picture) {
return Optional.ofNullable(picture)
.map(this::convertToHighRes).orElseThrow();
.map(this::convertToHighRes)
.orElseThrow();
}

private String convertToHighRes(String url){
Expand All @@ -78,7 +90,7 @@ private String convertToHighRes(String url){

private void validateSocialType(Member member, SocialType provider) {
if (!provider.equals(member.getSocialType())) {
throw new RuntimeException();
throw new ExistsMemberEmailException();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package shop.kkeujeok.kkeujeokbackend.auth.application;

import com.fasterxml.jackson.databind.JsonNode;
import shop.kkeujeok.kkeujeokbackend.auth.api.dto.response.UserInfo;

public interface AuthService {
UserInfo getUserInfo(String authCode);

String getProvider();

JsonNode getIdToken(String code);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.transaction.annotation.Transactional;
import shop.kkeujeok.kkeujeokbackend.auth.api.dto.request.RefreshTokenReqDto;
import shop.kkeujeok.kkeujeokbackend.auth.api.dto.response.MemberLoginResDto;
import shop.kkeujeok.kkeujeokbackend.auth.exception.InvalidTokenException;
import shop.kkeujeok.kkeujeokbackend.global.jwt.TokenProvider;
import shop.kkeujeok.kkeujeokbackend.global.jwt.api.dto.TokenDto;
import shop.kkeujeok.kkeujeokbackend.global.jwt.domain.Token;
Expand Down Expand Up @@ -52,8 +53,8 @@ private void refreshTokenUpdate(MemberLoginResDto memberLoginResDto, TokenDto to

@Transactional
public TokenDto generateAccessToken(RefreshTokenReqDto refreshTokenReqDto) {
if (!tokenRepository.existsByRefreshToken(refreshTokenReqDto.refreshToken()) || !tokenProvider.validateToken(refreshTokenReqDto.refreshToken())) {
throw new RuntimeException();
if (isInvalidRefreshToken(refreshTokenReqDto.refreshToken())) {
throw new InvalidTokenException();
}

Token token = tokenRepository.findByRefreshToken(refreshTokenReqDto.refreshToken()).orElseThrow();
Expand All @@ -62,4 +63,7 @@ public TokenDto generateAccessToken(RefreshTokenReqDto refreshTokenReqDto) {
return tokenProvider.generateAccessTokenByRefreshToken(member.getEmail(), token.getRefreshToken());
}

private boolean isInvalidRefreshToken(String refreshToken) {
return !tokenRepository.existsByRefreshToken(refreshToken) || !tokenProvider.validateToken(refreshToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package shop.kkeujeok.kkeujeokbackend.auth.exception;

import shop.kkeujeok.kkeujeokbackend.global.error.exception.NotFoundGroupException;

public class EmailNotFoundException extends NotFoundGroupException {
public EmailNotFoundException(String message) {
super(message);
}

public EmailNotFoundException() {
this("존재하지 않는 이메일 입니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package shop.kkeujeok.kkeujeokbackend.auth.exception;

import shop.kkeujeok.kkeujeokbackend.global.error.exception.InvalidGroupException;

public class ExistsMemberEmailException extends InvalidGroupException {
public ExistsMemberEmailException(String message) {
super(message);
}

public ExistsMemberEmailException() {
this("이미 가입한 계정이 있는 이메일 입니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package shop.kkeujeok.kkeujeokbackend.auth.exception;

import shop.kkeujeok.kkeujeokbackend.global.error.exception.AuthGroupException;

public class InvalidTokenException extends AuthGroupException {
public InvalidTokenException(String message) {
super(message);
}

public InvalidTokenException() {
this("토큰이 유효하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package shop.kkeujeok.kkeujeokbackend.global.config;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import shop.kkeujeok.kkeujeokbackend.global.filter.LogFilter;
import shop.kkeujeok.kkeujeokbackend.global.filter.LoginCheckFilter;
import shop.kkeujeok.kkeujeokbackend.global.jwt.TokenProvider;

@Configuration
public class WebConfig {
private final TokenProvider tokenProvider;

public WebConfig(TokenProvider tokenProvider) {
this.tokenProvider = tokenProvider;
}

@Bean
public FilterRegistrationBean<LogFilter> logFilter() {
FilterRegistrationBean<LogFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LogFilter()); // 여기서 만든 필터 클래스 등록
filterRegistrationBean.setOrder(1);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}

@Bean
public FilterRegistrationBean<LoginCheckFilter> loginCheckFilter() {
FilterRegistrationBean<LoginCheckFilter> filterRegistrationBean = new FilterRegistrationBean<>();
filterRegistrationBean.setFilter(new LoginCheckFilter(tokenProvider)); // JWT 토큰 유효성 검사를 위한 필터 클래스 등록
filterRegistrationBean.setOrder(2); // 1번인 로그필터 다음으로 수행
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package shop.kkeujeok.kkeujeokbackend.global.filter;

import java.io.IOException;
import java.util.UUID;


import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.GenericFilterBean;

@Slf4j
public class LogFilter extends GenericFilterBean {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
log.info("log filter doFilter");

HttpServletRequest httpRequest = (HttpServletRequest) request;
String requestURI = httpRequest.getRequestURI();

String uuid = UUID.randomUUID().toString();

try {
log.info("REQUEST [{}][{}]", uuid, requestURI);
chain.doFilter(request, response);
// chain이 없으면 여기서 끝난다. 즉, 로그만 띄우고 컨트롤러까지 가지 않아서 백지만 나온다.
// chain doFilter로 다시 호출해주면 controller로 넘어가서 정상적으로 페이지를 띄운다.
} catch (Exception e) {
throw e;
} finally {
log.info("REQUEST [{}][{}]", uuid, requestURI);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package shop.kkeujeok.kkeujeokbackend.global.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.PatternMatchUtils;
import org.springframework.web.filter.GenericFilterBean;
import shop.kkeujeok.kkeujeokbackend.global.filter.exceptiton.AuthenticationException;
import shop.kkeujeok.kkeujeokbackend.global.jwt.TokenProvider;

import java.io.IOException;

@Slf4j
@RequiredArgsConstructor
public class LoginCheckFilter extends GenericFilterBean {

private static final String[] whiteList = {
"*", // 일단 다 열어둠
// "/",
// "/api/oauth2/callback/**",
// "/api/*/token",
// "/api/token/access",
};

private final TokenProvider tokenProvider;

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String requestURI = httpRequest.getRequestURI();
try {
log.info("인증 체크 필터 시작{}", requestURI);
if (!isLoginCheckPath(requestURI)) {
log.info("인증 체크 로직 실행{}", requestURI);
String token = resolveToken(httpRequest);
if (token == null || !tokenProvider.validateToken(token)) {
log.info("미인증 사용자 요청 {}", requestURI);
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
// 토큰이 유효한 경우 사용자 정보를 로그로 출력
}
chain.doFilter(request, response);
} catch (AuthenticationException e) {
throw e;
} finally {
log.info("인증 체크 필터 종료{}", requestURI);
}
}

private boolean isLoginCheckPath(String requestURI) {
return PatternMatchUtils.simpleMatch(whiteList, requestURI); // 화이트리스트에 있는 경로는 true 반환
}

private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package shop.kkeujeok.kkeujeokbackend.global.filter.exceptiton;

import shop.kkeujeok.kkeujeokbackend.global.error.exception.AuthGroupException;

public class AuthenticationException extends AuthGroupException {

public AuthenticationException(String message) {
super(message);
}

public AuthenticationException() {
this("인증에 실패했습니다. 자격 증명을 확인하고 다시 시도하십시오.");
}
}
Loading

0 comments on commit 86e935d

Please sign in to comment.