Skip to content
Open
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
15 changes: 14 additions & 1 deletion src/main/java/com/overcomingroom/bellbell/config/CorsConfig.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.overcomingroom.bellbell.config;

import com.overcomingroom.bellbell.interceptor.AuthorizationInterceptor;
import com.overcomingroom.bellbell.resolver.LoginUserResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
* CORS(Cross-Origin Resource Sharing) 설정을 구성하는 클래스입니다.
*/
Expand All @@ -15,6 +19,7 @@
public class CorsConfig implements WebMvcConfigurer {

private final AuthorizationInterceptor authorizationInterceptor;
private final LoginUserResolver loginUserResolver;

/**
* CORS(Cross-Origin Resource Sharing) 설정을 추가합니다.
Expand All @@ -33,6 +38,14 @@ public void addCorsMappings(CorsRegistry registry) {

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this.authorizationInterceptor);
registry.addInterceptor(this.authorizationInterceptor)
.addPathPatterns("/**") // 모든 요청은 인터셉터를 거치도록 함.
.excludePathPatterns("/v1/login") // 로그인 시 AccessToken이 없기 때문에 해당 요청만 제외
.excludePathPatterns("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html"); // 스웨거 예외 처리
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginUserResolver);
}
}
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
package com.overcomingroom.bellbell.interceptor;

import com.overcomingroom.bellbell.exception.CustomException;
import com.overcomingroom.bellbell.exception.ErrorCode;
import com.overcomingroom.bellbell.resolver.LoginUserContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
@Slf4j
@RequiredArgsConstructor
public class AuthorizationInterceptor implements HandlerInterceptor {

private static final ThreadLocal<String> tokenHolder = new ThreadLocal<>();
private final LoginUserContext loginUserContext;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 헤더에서 토큰 추출
String token = extractToken(request);
tokenHolder.set(token);
return true;
}
String accessToken = extractToken(request);

log.info("interceptor accessToken = {}", accessToken);
// 토큰 검증
if (accessToken == null) {
throw new CustomException(ErrorCode.JWT_VALUE_IS_EMPTY);
}

// 로그인 된 유저 토큰 저장
registerLoginUser(accessToken);

public static String getAccessToken() {
return tokenHolder.get();
return HandlerInterceptor.super.preHandle(request, response, handler);
}

private String extractToken(HttpServletRequest request) {
return request.getHeader("Authorization");
}

public void registerLoginUser(String accessToken) {
loginUserContext.save(accessToken);
}

public void releaseLoginUser() {
loginUserContext.remove();
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
releaseLoginUser();
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,45 @@
package com.overcomingroom.bellbell.member.domain.controller;

import com.overcomingroom.bellbell.member.domain.service.MemberService;
import com.overcomingroom.bellbell.resolver.Login;
import com.overcomingroom.bellbell.resolver.LoginUser;
import com.overcomingroom.bellbell.response.ResResult;
import com.overcomingroom.bellbell.response.ResponseCode;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

/**
* 멤버 관련 API 를 처리하는 컨트롤러입니다.
*/
@RestController
@RequiredArgsConstructor
@Slf4j
public class MemberController {

private final MemberService memberService;
private final MemberService memberService;

/**
* 멤버 정보를 반환하는 메서드입니다.
*
* @param accessToken 클라이언트를 통해 전달받은 액세스 토큰
* @return 멤버 정보
*/
@GetMapping("/v1/member")
public ResponseEntity<ResResult> getUserInfo(
@RequestHeader("Authorization") String accessToken
) {
ResponseCode responseCode = ResponseCode.MEMBER_INFO_GET_SUCCESSFUL;
return ResponseEntity.ok(
ResResult.builder()
.responseCode(responseCode)
.code(responseCode.getCode())
.message(responseCode.getMessage())
.data(memberService.getMemberInfo(accessToken.substring(7)))
.build()
);
}
/**
* 멤버 정보를 반환하는 메서드입니다.
*
* @param accessToken 클라이언트를 통해 전달받은 액세스 토큰
* @return 멤버 정보
*/
@GetMapping("/v1/member")
public ResponseEntity<ResResult> getUserInfo(@Parameter(hidden = true) @Login LoginUser loginUser) {

ResponseCode responseCode = ResponseCode.MEMBER_INFO_GET_SUCCESSFUL;

return ResponseEntity.ok(
ResResult.builder()
.responseCode(responseCode)
.code(responseCode.getCode())
.message(responseCode.getMessage())
.data(memberService.getMemberInfo(loginUser.getAccessToken()))
.build()
);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/overcomingroom/bellbell/resolver/Login.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.overcomingroom.bellbell.resolver;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Login {
}
15 changes: 15 additions & 0 deletions src/main/java/com/overcomingroom/bellbell/resolver/LoginUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.overcomingroom.bellbell.resolver;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class LoginUser {

private String accessToken;

public static LoginUser of(String accessToken) {
return new LoginUser(accessToken.substring(7));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.overcomingroom.bellbell.resolver;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class LoginUserContext {
private static final ThreadLocal<LoginUser> loginUserContext = new ThreadLocal<>();

public LoginUser getLoginUser() {
return loginUserContext.get();
}

public void save(String accessToken){
loginUserContext.set(LoginUser.of(accessToken));
}

public void remove(){
loginUserContext.remove();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.overcomingroom.bellbell.resolver;

import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
@RequiredArgsConstructor
public class LoginUserResolver implements HandlerMethodArgumentResolver {

private final LoginUserContext loginUserContext;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Login.class) && parameter.getParameterType().equals(LoginUser.class);
}

@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
return loginUserContext.getLoginUser();
}
}
Loading