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

[202102567 최정민] Level6 미션 제출합니다 #33

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
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
34 changes: 19 additions & 15 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
}

group = 'backend.likelion'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '21'
sourceCompatibility = '21'
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
compileOnly {
extendsFrom annotationProcessor
}
}

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'

// JWT
implementation 'com.auth0:java-jwt:4.4.0'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured'
}

tasks.named('test') {
useJUnitPlatform()
useJUnitPlatform()
}
52 changes: 52 additions & 0 deletions src/main/java/backend/likelion/todos/LogInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//package backend.likelion.todos;
//
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.web.method.HandlerMethod;
//import org.springframework.web.servlet.HandlerInterceptor;
//import org.springframework.web.servlet.ModelAndView;
//
//import java.util.UUID;
//
////import static jdk.nio.zipfs.ZipFileAttributeView.AttrID.method;
//@Slf4j
//public class LogInterceptor implements HandlerInterceptor {
// public static String LOG_ID = "logId";
//
// @Override
// public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
// throws Exception{
// String requestURI = request.getRequestURI();
// String uuid = UUID.randomUUID().toString();
//
// request.setAttribute(LOG_ID, uuid);
//
// if(handler instanceof HandlerMethod){
// HandlerMethod handlerMethod = (HandlerMethod) method;
// }
// }
//
//
//
// @Override
// public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
// ModelAndView modelAndView) throws Exception{
// log.info("postHandler [{}]",modelAndView);
// }
//
//
// @Override
// public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
// throws Exception {
// String requestURI = request.getRequestURI();
// String logId = (String)request.getAttribute(LOG_ID);
//
// log.info("RESPONSE [{}][{}][{}]",logId,requestURI,handler);
// if (ex!= null){
// log.error("afterCompletion error!!",ex);
// }
// }
//
//
//}
8 changes: 5 additions & 3 deletions src/main/java/backend/likelion/todos/TodosApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;

@ConfigurationPropertiesScan
@SpringBootApplication
public class TodosApplication {

public static void main(String[] args) {
SpringApplication.run(TodosApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(TodosApplication.class, args);
}

}
16 changes: 16 additions & 0 deletions src/main/java/backend/likelion/todos/User.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package backend.likelion.todos;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter

public class User {
private String id;
private String name;

public User(String id, String name){
this.id = id;
this.name = name;
}
}
20 changes: 20 additions & 0 deletions src/main/java/backend/likelion/todos/UserArgumentResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package backend.likelion.todos;
import org.springframework.core.MethodParameter;
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;

public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter){
return parameter.getParameterType().equals(User.class);
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String userId = webRequest.getHeader("userId");
String userName = webRequest.getHeader("userName");
return new User(userId,userName);
}
}
12 changes: 12 additions & 0 deletions src/main/java/backend/likelion/todos/UserController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package backend.likelion.todos;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
@GetMapping("/user")
public String getUserInfo(User user){
return "사용자 ID: " + user.getId() + ", 이름:" + user.getName();
}
}
17 changes: 17 additions & 0 deletions src/main/java/backend/likelion/todos/WebConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//package backend.likelion.todos;
//
//import org.springframework.context.annotation.Configuration;
//import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//
//@Configuration
//public class WebConfig implements WebMvcConfigurer {
//
// @Override
// public void addInterceptors(InterceptorRegistry registry){
// registry.addInterceptor(new LogInterceptor())
// .order(1)
// .addPathPatterns("/**")
// .excludePathPatterns("/css/**","/*.ico","/error","/login","/members");
// }
//}
11 changes: 11 additions & 0 deletions src/main/java/backend/likelion/todos/auth/Auth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package backend.likelion.todos.auth;

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 Auth {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package backend.likelion.todos.auth;

import backend.likelion.todos.auth.jwt.JwtService;
import backend.likelion.todos.common.UnAuthorizedException;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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;

@RequiredArgsConstructor
@Component
public class AuthArgumentResolver implements HandlerMethodArgumentResolver {

private final JwtService jwtService;

@Override
public boolean supportsParameter(MethodParameter parameter) {
// TODO [6단계] parameter가 @Auth 어노테이션을 갖고 있고, 파라미터 타입이 Long.class인 경우 true를 반환하는 조건을 구현하세요.
return parameter.getParameterAnnotation(Auth.class) != null && Long.class.isAssignableFrom(parameter.getParameterType());
}

@Override
public Object resolveArgument(
MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory
) {
// TODO [6단계] webRequest로부터 accessToken을 추출하고, jwtService를 사용하여 memberId를 추출하여 반환하는 로직을 구현하세요.
String accessToken = extractAccessToken(webRequest);
return jwtService.extractMemberId(accessToken);
}

private static String extractAccessToken(NativeWebRequest request) {
// TODO [6단계] request 헤더에서 "Authorization" 헤더 값을 추출하여 "Bearer "로 시작하는 accessToken을 반환하세요. 유효하지 않을 경우 "로그인 후 접근할 수 있습니다." 메시지와 함께 UnAuthorizedException을 발생시키는 로직을 구현하세요.
String authorization = request.getHeader("Authorization");
if (StringUtils.hasText(authorization) && authorization.startsWith("Bearer ")) {
return authorization.substring(7);
} else {
throw new UnAuthorizedException("로그인 후 접근할 수 있습니다.");
}
}

}
21 changes: 21 additions & 0 deletions src/main/java/backend/likelion/todos/auth/AuthConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package backend.likelion.todos.auth;

import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@RequiredArgsConstructor
@Configuration
public class AuthConfig implements WebMvcConfigurer {

private final AuthArgumentResolver authArgumentResolver;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
// TODO [6단계] resolvers 리스트에 authArgumentResolver를 추가하세요.
resolvers.add(authArgumentResolver);
}

}
17 changes: 17 additions & 0 deletions src/main/java/backend/likelion/todos/auth/AuthTestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package backend.likelion.todos.auth;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class AuthTestController {

@GetMapping("/auth")
public String test(
@Auth Long memberId
) {
return "아이디가 " + memberId + "인 회원 인증 성공!";
}
}
10 changes: 10 additions & 0 deletions src/main/java/backend/likelion/todos/auth/jwt/JwtProperty.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package backend.likelion.todos.auth.jwt;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("jwt")
public record JwtProperty(
String secretKey,
Long accessTokenExpirationDay
) {
}
46 changes: 46 additions & 0 deletions src/main/java/backend/likelion/todos/auth/jwt/JwtService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package backend.likelion.todos.auth.jwt;

import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

import backend.likelion.todos.common.UnAuthorizedException;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import java.util.Date;
import org.springframework.stereotype.Service;

@Service
public class JwtService {

private final long accessTokenExpirationDayToMills;
private final Algorithm algorithm;

public JwtService(JwtProperty jwtProperty) {
this.accessTokenExpirationDayToMills =
MILLISECONDS.convert(jwtProperty.accessTokenExpirationDay(), DAYS);
this.algorithm = Algorithm.HMAC512(jwtProperty.secretKey());
}

public String createToken(Long memberId) {
return JWT.create()
.withExpiresAt(new Date(
accessTokenExpirationDayToMills + System.currentTimeMillis()
))
.withIssuedAt(new Date())
.withClaim("memberId", memberId)
.sign(algorithm);
}

public Long extractMemberId(String token) {
try {
return JWT.require(algorithm)
.build()
.verify(token)
.getClaim("memberId")
.asLong();
} catch (JWTVerificationException e) {
throw new UnAuthorizedException("유효하지 않은 토큰입니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package backend.likelion.todos.common;

public class BadRequestException extends RuntimeException {

public BadRequestException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package backend.likelion.todos.common;

public class ConflictException extends RuntimeException {

public ConflictException(String message) {
super(message);
}
}
Loading