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

로그인 처리 공통화를 위한 ArgumentResolver 및 Interceptor 구현 #156

Merged
merged 10 commits into from
Jul 31, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ public class AuthController {
private final AuthService authService;

@PostMapping("/login")
public ResponseEntity<RestResponse<LoginResponse>> login(
@RequestBody LoginRequest loginRequest) {
public ResponseEntity<RestResponse<LoginResponse>> login(@RequestBody LoginRequest loginRequest) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

피드백 반영 감사요

LoginResponse response = authService.login(loginRequest);

return ResponseEntity.ok().body(new RestResponse<>(response));
Expand Down
14 changes: 14 additions & 0 deletions backend/src/main/java/mouda/backend/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import mouda.backend.auth.dto.LoginRequest;
import mouda.backend.auth.dto.LoginResponse;
import mouda.backend.auth.exception.AuthErrorMessage;
import mouda.backend.auth.exception.AuthException;
import mouda.backend.member.domain.Member;
import mouda.backend.member.repository.MemberRepository;
import mouda.backend.security.JwtProvider;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

@Service
Expand Down Expand Up @@ -33,4 +36,15 @@ public LoginResponse login(LoginRequest loginRequest) {
return new LoginResponse(token);
});
}

public Member findMember(String token) {
long memberId = jwtProvider.extractMemberId(token);
return memberRepository.findById(memberId)
.orElseThrow(
() -> new AuthException(HttpStatus.UNAUTHORIZED, AuthErrorMessage.UNAUTHORIZED));
}

public void checkAuthentication(String token) {
jwtProvider.validateExpiration(token);
}
}
31 changes: 31 additions & 0 deletions backend/src/main/java/mouda/backend/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package mouda.backend.config;

import java.util.List;
import lombok.RequiredArgsConstructor;
import mouda.backend.config.argumentresolver.LoginMemberArgumentResolver;
import mouda.backend.config.interceptor.AuthenticationCheckInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {

private final LoginMemberArgumentResolver loginMemberArgumentResolver;

private final AuthenticationCheckInterceptor authenticationCheckInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationCheckInterceptor)
.addPathPatterns("/v1/**")
.excludePathPatterns("/v1/auth/login");
}

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginMemberArgumentResolver);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mouda.backend.config.argumentresolver;

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

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginMember {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package mouda.backend.config.argumentresolver;

import lombok.RequiredArgsConstructor;
import mouda.backend.auth.service.AuthService;
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 LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {

private final AuthService authService;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return false;
}

@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory
) throws Exception {
String authorizationHeader = webRequest.getHeader("Authorization");

String token = extractToken(authorizationHeader);
return authService.findMember(token);
}

private String extractToken(String authorizationHeader) {
return authorizationHeader.substring(7);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package mouda.backend.config.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import mouda.backend.auth.service.AuthService;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
@RequiredArgsConstructor
public class AuthenticationCheckInterceptor implements HandlerInterceptor {

private static final String AUTHORIZATION_PREFIX = "Bearer ";

private final AuthService authService;

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String authorizationHeader = request.getHeader("Authorization");

if (authorizationHeader == null || !authorizationHeader.startsWith(AUTHORIZATION_PREFIX)) {
throw new IllegalArgumentException("Authorization header is missing or invalid");
}

String token = extractToken(authorizationHeader);
authService.checkAuthentication(token);

return true;
}

private String extractToken(String authorizationHeader) {
return authorizationHeader.substring(7);
}
}
8 changes: 4 additions & 4 deletions backend/src/main/java/mouda/backend/security/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,19 @@ public String extractNickname(String token) {

public Claims getPayload(String token) {
try {
Claims claims = Jwts.parser()
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();

validateExpiration(claims);
return claims;
} catch (JwtException | IllegalArgumentException e) {
throw new AuthException(HttpStatus.UNAUTHORIZED, AuthErrorMessage.UNAUTHORIZED);
}
}

public void validateExpiration(Claims claims) {
public void validateExpiration(String token) {
Claims claims = getPayload(token);

if (claims.getExpiration().before(new Date())) {
throw new AuthException(HttpStatus.UNAUTHORIZED, AuthErrorMessage.UNAUTHORIZED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import mouda.backend.auth.dto.LoginRequest;
import mouda.backend.config.DatabaseCleaner;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -15,6 +17,14 @@ public class AuthControllerTest {
@Autowired
AuthController authController;

@Autowired
private DatabaseCleaner databaseCleaner;

@AfterEach
void cleanUp() {
databaseCleaner.cleanUp();
}

@DisplayName("로그인 하기")
@Test
void login() {
Expand Down
Loading