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

액세스 토큰 만료시 Refresh 하는 기능 구현 #43

Merged
merged 24 commits into from
Jan 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6cd3748
chore(config): enable OpenFeign
jacobhboy Jan 8, 2024
65ae96f
chore(config): OpenFeign 설정 변경
jacobhboy Jan 8, 2024
00eb08a
chore(build): jwt 의존성 추가
jacobhboy Jan 10, 2024
6018394
chore(yml): jwt 설정 추가
jacobhboy Jan 10, 2024
660068e
feat(auth): 구글 auth feign 추가
jacobhboy Jan 10, 2024
5aa78ae
feat(auth): 구글 auth로 로그인 구현
jacobhboy Jan 10, 2024
dfbdb8f
feat(auth): 토큰 발급
jacobhboy Jan 10, 2024
20105d5
feat(user): 유저 추가 혹은 업데이트 구현
jacobhboy Jan 10, 2024
7887bd0
feat(auth): jwt credential 추가
jacobhboy Jan 10, 2024
121913e
feat(user): email 추가
jacobhboy Jan 10, 2024
a9fc11d
feat(auth): exception들 추가
jacobhboy Jan 10, 2024
0b4c6e1
feat(user): Role 제거
jacobhboy Jan 10, 2024
1dcb6b4
feat(auth): dto 추가
jacobhboy Jan 10, 2024
d880e66
chore(merge): 머지 하였음
jacobhboy Jan 10, 2024
aefb008
feat(user): Role 추가
jacobhboy Jan 10, 2024
e768859
feat(profile): getCurrentUser 추가
jacobhboy Jan 10, 2024
d2b50d2
feat(comment): getCurrentUser 추가
jacobhboy Jan 10, 2024
57c1208
feat(like): getCurrentUser 추가
jacobhboy Jan 10, 2024
fb3b48c
feat(auth): 현재 사용자를 저장하는 authRepository 구현
jacobhboy Jan 10, 2024
0b423f5
feat(auth): 인터셉터 구현
jacobhboy Jan 10, 2024
7270f7c
feat(user): 아이디로 유저 조회하는 기능 추가
jacobhboy Jan 10, 2024
a854d44
feat(auth): extract가 token을 받도록 변경
jacobhboy Jan 10, 2024
d972cd5
feat(auth): access token을 refresh하는 기능 구현
jacobhboy Jan 10, 2024
f9dead8
fix(auth): merge conflict 해결
jacobhboy Jan 14, 2024
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.sickgyun.server.auth.interceptor;

import static org.springframework.http.HttpHeaders.*;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
Expand Down Expand Up @@ -30,7 +32,7 @@ public class AuthInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (handler instanceof HandlerMethod hm) {
if (hm.hasMethodAnnotation(LoginRequired.class)) {
String jwt = BearerTokenExtractor.extract(request);
String jwt = BearerTokenExtractor.extract(request.getHeader(AUTHORIZATION));
Long userId = jwtParser.getIdFromJwt(jwt);

User user = userRepository.findById(userId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.web.bind.annotation.RestController;

import com.sickgyun.server.auth.presentation.dto.AccessTokenRequest;
import com.sickgyun.server.auth.presentation.dto.RefreshTokenRequest;
import com.sickgyun.server.auth.presentation.dto.TokenResponse;
import com.sickgyun.server.auth.service.CommandAuthService;

Expand All @@ -23,4 +24,11 @@ public TokenResponse login(@RequestBody AccessTokenRequest accessToken) {
authService.login(accessToken.accessToken())
);
}

@PostMapping("/refresh")
public TokenResponse refreshAccessToken(@RequestBody RefreshTokenRequest refreshToken) {
return TokenResponse.from(
authService.refresh(refreshToken.refreshToken())
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.sickgyun.server.auth.presentation.dto;

public record RefreshTokenRequest(
String refreshToken
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
import com.sickgyun.server.auth.service.implementation.AuthReader;
import com.sickgyun.server.auth.service.implementation.AuthValidator;
import com.sickgyun.server.auth.service.implementation.TokenProvider;
import com.sickgyun.server.auth.util.BearerTokenExtractor;
import com.sickgyun.server.user.domain.User;
import com.sickgyun.server.user.service.implementation.UserCreator;
import com.sickgyun.server.user.service.implementation.UserReader;
import com.sickgyun.server.user.service.implementation.UserUpdater;
import com.sickgyun.server.user.service.implementation.UserValidator;

Expand All @@ -20,14 +22,15 @@
public class CommandAuthService {
private final TokenProvider tokenProvider;
private final AuthReader authReader;
private final AuthValidator validator;
private final AuthValidator authValidator;
private final UserValidator userValidator;
private final UserUpdater userUpdater;
private final UserCreator userCreator;
private final UserReader userReader;

public Token login(String accessToken) {
User user = authReader.getGoogleUser(accessToken);
validator.shouldBeBssmEmail(user);
authValidator.shouldBeBssmEmail(user);

User updatedUser;
if (userValidator.checkUserExist(user)) {
Expand All @@ -38,4 +41,14 @@ public Token login(String accessToken) {

return tokenProvider.createNewTokens(updatedUser);
}

public Token refresh(String bearer) {
String refreshToken = BearerTokenExtractor.extract(bearer);
authValidator.shouldRefreshTokenValid(refreshToken);

Long userId = authReader.getIdFromJwt(refreshToken);
String accessToken = tokenProvider.createAccessToken(userReader.readUser(userId));

Comment on lines +46 to +51
Copy link
Contributor

Choose a reason for hiding this comment

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

깔쌈하네요

return new Token(accessToken, refreshToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.springframework.stereotype.Service;

import com.sickgyun.server.auth.infra.feign.GoogleOauthFeign;
import com.sickgyun.server.auth.util.JwtParser;
import com.sickgyun.server.user.domain.User;

import lombok.RequiredArgsConstructor;
Expand All @@ -11,8 +12,13 @@
@RequiredArgsConstructor
public class AuthReader {
private final GoogleOauthFeign googleFeign;
private final JwtParser jwtParser;

public User getGoogleUser(String accessToken) {
return googleFeign.getInfo(accessToken).toUser();
}

public Long getIdFromJwt(String token) {
return jwtParser.getIdFromJwt(token);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
import org.springframework.stereotype.Service;

import com.sickgyun.server.auth.exception.NotBssmEmailException;
import com.sickgyun.server.auth.exception.TokenExpiredException;
import com.sickgyun.server.auth.exception.TokenInvalidException;
import com.sickgyun.server.common.config.JwtCredentials;
import com.sickgyun.server.user.domain.User;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class AuthValidator {
private final JwtCredentials jwtCredentials;

public void shouldBeBssmEmail(User user) {
String email = user.getEmail();
Expand All @@ -18,4 +25,17 @@ public void shouldBeBssmEmail(User user) {
throw new NotBssmEmailException(email);
}
}

public void shouldRefreshTokenValid(String refreshToken) {
try {
Jwts.parserBuilder()
.setSigningKey(jwtCredentials.secretKey())
.build()
.parseClaimsJws(refreshToken);
} catch (ExpiredJwtException e) {
throw new TokenExpiredException();
} catch (JwtException e) {
throw new TokenInvalidException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public Token createNewTokens(User user) {
);
}

private String createAccessToken(User user) {
public String createAccessToken(User user) {
return createToken(user, jwtCredentials.accessTokenExpirationTime());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package com.sickgyun.server.auth.util;

import static org.springframework.http.HttpHeaders.*;

import com.sickgyun.server.auth.exception.TokenInvalidException;
import com.sickgyun.server.auth.exception.TokenMissingException;

import jakarta.servlet.http.HttpServletRequest;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Expand All @@ -15,10 +12,9 @@ public class BearerTokenExtractor {
private static final String BEARER_TYPE = "Bearer ";
private static final String BEARER_JWT_REGEX = "^Bearer [A-Za-z0-9-_=]+\\.[A-Za-z0-9-_=]+\\.?[A-Za-z0-9-_.+/=]*$";

public static String extract(HttpServletRequest request) {
String authorization = request.getHeader(AUTHORIZATION);
validate(authorization);
return authorization.replace(BEARER_TYPE, "").trim();
public static String extract(String bearer) {
validate(bearer);
return bearer.replace(BEARER_TYPE, "").trim();
}

private static void validate(String authorization) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.sickgyun.server.user.service.implementation;

import org.springframework.stereotype.Service;

import com.sickgyun.server.user.domain.User;
import com.sickgyun.server.user.domain.repository.UserRepository;
import com.sickgyun.server.user.exception.UserNotFoundException;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class UserReader {
private final UserRepository userRepository;

public User readUser(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
}

}