Skip to content

Commit

Permalink
Merge pull request #35 from sh1220/dev
Browse files Browse the repository at this point in the history
Refactor: merge & conflict resolve
  • Loading branch information
sh1220 authored Aug 1, 2024
2 parents 60763cd + 9512a85 commit 66b46c7
Show file tree
Hide file tree
Showing 14 changed files with 266 additions and 26 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ dependencies {

implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.mysql:mysql-connector-java'
runtimeOnly 'com.h2database:h2'

//mail
Expand All @@ -61,6 +60,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}


tasks.named('test') {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import store.itpick.backend.service.UserService;

import static store.itpick.backend.common.response.status.BaseExceptionResponseStatus.*;

Expand All @@ -22,8 +23,8 @@
public class JwtAuthInterceptor implements HandlerInterceptor {

private static final String JWT_TOKEN_PREFIX = "Bearer ";

private final JwtProvider jwtProvider;
private final UserService userService;

// 컨트롤러 호출전에 JWT 검증
@Override
Expand All @@ -35,8 +36,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
String email = jwtProvider.getPrincipal(accessToken);
validatePayload(email);

// long userId = authService.getUserIdByEmail(email);
// request.setAttribute("userId", userId);
long userId = userService.getUserIdByEmail(email);
request.setAttribute("userId", userId);
return true;

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package store.itpick.backend.common.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import store.itpick.backend.common.exception.jwt.bad_request.JwtNoTokenException;
import store.itpick.backend.common.exception.jwt.bad_request.JwtUnsupportedTokenException;
import store.itpick.backend.common.exception.jwt.unauthorized.JwtExpiredTokenException;
import store.itpick.backend.common.exception.jwt.unauthorized.JwtInvalidTokenException;
import store.itpick.backend.jwt.JwtProvider;
import store.itpick.backend.service.UserService;

import static store.itpick.backend.common.response.status.BaseExceptionResponseStatus.*;

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

private static final String JWT_TOKEN_PREFIX = "Bearer ";
private final JwtProvider jwtProvider;
private final UserService userService;

// 컨트롤러 호출전에 JWT 검증
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {

String refreshToken = resolveRefreshToken(request);
validateRefreshToken(refreshToken);

String email = jwtProvider.getPrincipal(refreshToken);
validatePayload(email);

long userId = userService.getUserIdByEmail(email);
request.setAttribute("userId", userId);
return true;

}

private String resolveRefreshToken(HttpServletRequest request) {
String token = request.getHeader(HttpHeaders.AUTHORIZATION);
validateToken(token);
return token.substring(JWT_TOKEN_PREFIX.length());
}

private void validateToken(String token) {
if (token == null) {
throw new JwtNoTokenException(TOKEN_NOT_FOUND);
}
if (!token.startsWith(JWT_TOKEN_PREFIX)) {
throw new JwtUnsupportedTokenException(UNSUPPORTED_TOKEN_TYPE);
}
}

private void validateRefreshToken(String accessToken) {
if (jwtProvider.isExpiredToken(accessToken)) {
throw new JwtExpiredTokenException(EXPIRED_TOKEN);
}
}

private void validatePayload(String email) {
if (email == null) {
throw new JwtInvalidTokenException(INVALID_TOKEN);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,23 @@ public enum BaseExceptionResponseStatus implements ResponseStatus {
INVALID_TOKEN(4003, HttpStatus.UNAUTHORIZED.value(), "유효하지 않은 토큰입니다."),
MALFORMED_TOKEN(4004, HttpStatus.UNAUTHORIZED.value(), "토큰이 올바르게 구성되지 않았습니다."),
EXPIRED_TOKEN(4005, HttpStatus.UNAUTHORIZED.value(), "만료된 토큰입니다."),
TOKEN_MISMATCH(4006, HttpStatus.UNAUTHORIZED.value(), "로그인 정보가 토큰 정보와 일치하지 않습니다."),
TOKEN_MISMATCH(4006, HttpStatus.UNAUTHORIZED.value(), "회원 정보가 토큰 정보와 일치하지 않습니다."),
EXPIRED_REFRESH_TOKEN(4007, HttpStatus.UNAUTHORIZED.value(), "다시 로그인 해주세요."),


/**
* 5000: User 오류
*/
INVALID_USER_VALUE(5000, HttpStatus.BAD_REQUEST.value(), "회원가입 요청에서 잘못된 값이 존재합니다."),
INVALID_USER_VALUE(5000, HttpStatus.BAD_REQUEST.value(), "회원가입/로그인 요청에서 잘못된 값이 존재합니다."),
DUPLICATE_EMAIL(5001, HttpStatus.BAD_REQUEST.value(), "이미 존재하는 이메일입니다."),
DUPLICATE_NICKNAME(5002, HttpStatus.BAD_REQUEST.value(), "이미 존재하는 닉네임입니다."),
USER_NOT_FOUND(5003, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 회원입니다."),
PASSWORD_NO_MATCH(5004, HttpStatus.BAD_REQUEST.value(), "비밀번호가 일치하지 않습니다."),
INVALID_USER_STATUS(5005, HttpStatus.BAD_REQUEST.value(), "잘못된 회원 status 값입니다."),
EMAIL_NOT_FOUND(5006, HttpStatus.BAD_REQUEST.value(), "존재하지 않는 이메일입니다."),
INVALID_PASSWORD(5007, HttpStatus.BAD_REQUEST.value(), "유효하지 않는 password입니다."),
UNABLE_TO_SEND_EMAIL(5008,HttpStatus.BAD_REQUEST.value(),"메일을 전송할 수 없습니다."),
INVALID_REFRESHTOKEN(5008, HttpStatus.BAD_REQUEST.value(), "유효하지 않는 토큰입니다."),
UNABLE_TO_SEND_EMAIL(5012,HttpStatus.BAD_REQUEST.value(),"메일을 전송할 수 없습니다."),
NO_SUCH_ALGORITHM(5009, HttpStatus.BAD_REQUEST.value(), "인증 번호 생성을 위한 알고리즘을 찾을 수 없습니다."),
AUTH_CODE_IS_NOT_SAME(5010, HttpStatus.BAD_REQUEST.value(), "인증 번호가 일치하지 않습니다."),
MEMBER_EXISTS(5011,HttpStatus.BAD_REQUEST.value(), "이미 존재하는 회원입니다.");
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/store/itpick/backend/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtAuthenticationInterceptor)
.order(1)
.addPathPatterns("/auth/test","/users/**")
.excludePathPatterns("/users");
.addPathPatterns("/**")
.excludePathPatterns("/auth/login", "/auth/singup", "/auth/refresh");
//인터셉터 적용 범위 수정
}

Expand Down
25 changes: 23 additions & 2 deletions src/main/java/store/itpick/backend/controller/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import store.itpick.backend.common.argument_resolver.PreAuthorize;
import store.itpick.backend.common.exception.jwt.unauthorized.JwtExpiredTokenException;
import store.itpick.backend.common.exception.jwt.unauthorized.JwtInvalidTokenException;
import store.itpick.backend.common.response.BaseResponse;
import store.itpick.backend.dto.auth.LoginRequest;
import store.itpick.backend.dto.auth.LoginResponse;
import store.itpick.backend.dto.auth.RefreshRequest;
import store.itpick.backend.dto.auth.RefreshResponse;
import store.itpick.backend.dto.user.user.PostUserRequest;
import store.itpick.backend.dto.user.user.PostUserResponse;
import store.itpick.backend.service.UserService;
Expand All @@ -22,11 +32,18 @@
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class UserController {

@Autowired
private final UserService userService;

@PostMapping("/refresh")
public BaseResponse<RefreshResponse> refresh(@Validated @RequestBody RefreshRequest refreshRequest) {
return new BaseResponse<>(userService.refresh(refreshRequest.getRefreshToken()));
}


/**
* 로그인
*/
Expand All @@ -39,8 +56,12 @@ public BaseResponse<LoginResponse> login(@Validated @RequestBody LoginRequest au
return new BaseResponse<>(userService.login(authRequest));
}

@PostMapping("/logout")
public BaseResponse<?> logoutUser() {
/**
* 로그아웃 : db의 refresh 토큰을 null로 설정
*/
@PatchMapping("/logout")
public BaseResponse<Object> logout(@PreAuthorize long userId) {
userService.logout(userId);
return new BaseResponse<>(null);
}

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/store/itpick/backend/dto/auth/JwtDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package store.itpick.backend.dto.auth;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class JwtDTO {
private String accessToken;
private String refreshToken;

public JwtDTO(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
public class LoginResponse {

private long userId;
private String jwt;
private JwtDTO jwt;

}
15 changes: 15 additions & 0 deletions src/main/java/store/itpick/backend/dto/auth/LogoutRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package store.itpick.backend.dto.auth;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class LogoutRequest {

@NotBlank(message = "refreshToken: {NotBlank}")
private String refreshToken;
}
15 changes: 15 additions & 0 deletions src/main/java/store/itpick/backend/dto/auth/RefreshRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package store.itpick.backend.dto.auth;

import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class RefreshRequest {

@NotBlank(message = "refreshToken: {NotBlank}")
private String refreshToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package store.itpick.backend.dto.auth;

import lombok.AllArgsConstructor;

@AllArgsConstructor
public class RefreshResponse {
private String accessToken;
}
22 changes: 20 additions & 2 deletions src/main/java/store/itpick/backend/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public class JwtProvider {
@Value("${secret.jwt-expired-in}")
private long JWT_EXPIRED_IN;

@Value("${secret.jwt-refresh-expired-in}")
private long JWT_REFRESH_EXPIRED_IN;

public String createToken(String principal, long userId) {
log.info("JWT key={}", JWT_SECRET_KEY);

Expand All @@ -38,16 +41,31 @@ public String createToken(String principal, long userId) {
.compact();
}

public String createRefreshToken(String principal, long userId) {
log.info("JWT key={}", JWT_SECRET_KEY);

Claims claims = Jwts.claims().setSubject(principal);
Date now = new Date();
Date validity = new Date(now.getTime() + JWT_REFRESH_EXPIRED_IN);

return Jwts.builder()
.setClaims(claims)
.setIssuedAt(now)
.setExpiration(validity)
.claim("userId", userId)
.signWith(SignatureAlgorithm.HS256, JWT_SECRET_KEY)
.compact();
}

public boolean isExpiredToken(String token) throws JwtInvalidTokenException {
try {
Jws<Claims> claims = Jwts.parserBuilder()
.setSigningKey(JWT_SECRET_KEY).build()
.parseClaimsJws(token);
.parseClaimsJws(token); // 유효성 확인
return claims.getBody().getExpiration().before(new Date());

} catch (ExpiredJwtException e) {
return true;

} catch (UnsupportedJwtException e) {
throw new JwtUnsupportedTokenException(UNSUPPORTED_TOKEN_TYPE);
} catch (MalformedJwtException e) {
Expand Down
Loading

0 comments on commit 66b46c7

Please sign in to comment.