This repository has been archived by the owner on Jul 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: jwt 의존성 추가 Co-authored-by: HubCreator <[email protected]> * feat: Jwt 토큰 발급 기능 구현 Co-authored-by: HubCreator <[email protected]> * feat: `Token` Entity 생성 Co-authored-by: HubCreator <[email protected]> * refactor: `member` 정적 팩토리 메서드 입력값 수정 Co-authored-by: HubCreator <[email protected]> * feat: JWT 정책 추가 Co-authored-by: HubCreator <[email protected]> * feat: 인터셉터 초안 Co-authored-by: HubCreator <[email protected]> * refactor: 사용하지 않는 클래스 삭제 Co-authored-by: HubCreator <[email protected]> * feat: token을 반환하는 Extractor기능 구현 Co-authored-by: HubCreator <[email protected]> * feat: token의 대한 CustomException 정의 Co-authored-by: HubCreator <[email protected]> * feat: 인증에 사용되는 커스텀 어노테이션 정의 Co-authored-by: HubCreator <[email protected]> * feat: access와 refresh 토큰 Interceptor 기능 구현 Co-authored-by: HubCreator <[email protected]> * feat: refreshToken의 유효성 검사 로직 기능 구현 Co-authored-by: HubCreator <[email protected]> * feat: 토큰 검증 후 AccessToken 발급 기능 구현 Co-authored-by: HubCreator <[email protected]> * refactor: token의 payload key값 변경 Co-authored-by: HubCreator <[email protected]> * feat: accessToken Resolver기능 구현 Co-authored-by: HubCreator <[email protected]> * refactor: 패키지 변경 및 DTO생성 Co-authored-by: HubCreator <[email protected]> * refactor: test 코드 수정 Co-authored-by: HubCreator <[email protected]> * feat: 인가 어노테이션 적용 Co-authored-by: HubCreator <[email protected]> * feat: token의 대한 환경변수 추가 Co-authored-by: HubCreator <[email protected]> * refactor :환경변수 추가 및 개행 제거 Co-authored-by: HubCreator <[email protected]> * refactor :Response타입 변경 Co-authored-by: HubCreator <[email protected]> * refactor :docker 설정 및 환경변수 수정 * refactor :재발급 기준 변경 * refactor :config 결합 및 Interceptor범위 수정 * refactor :네이밍 수정 * refactor :interceptor범위 수정 * refactor :token을 return방식 수정 * refactor :사용하지 않는 어노테이션 삭제 * refactor :사용하지 않는 파라미터 삭제 * refactor :Cookie 유효시간 생성자 주입 적용 * refactor :Path범위 수정 * refactor :중복 되는 로직 메서드 분리 * refactor :접근제어자 변경 * refactor :long -> int 변경 * refactor :에러 메시지 오타 수정 * refactor :개행 제거 * refactor :불필요한 if문 제거 --------- Co-authored-by: HubCreator <[email protected]> Co-authored-by: echo724 <[email protected]>
- Loading branch information
1 parent
0916f23
commit abd353b
Showing
31 changed files
with
563 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
backend/src/main/java/org/donggle/backend/application/repository/TokenRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package org.donggle.backend.application.repository; | ||
|
||
import org.donggle.backend.auth.JwtToken; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import java.util.Optional; | ||
|
||
public interface TokenRepository extends JpaRepository<JwtToken, Long> { | ||
Optional<JwtToken> findByMemberId(final Long memberId); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
backend/src/main/java/org/donggle/backend/auth/JwtToken.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package org.donggle.backend.auth; | ||
|
||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.FetchType; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.OneToOne; | ||
import lombok.AccessLevel; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import org.donggle.backend.domain.member.Member; | ||
|
||
import java.util.Objects; | ||
|
||
@Entity | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
public class JwtToken { | ||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
private String refreshToken; | ||
@OneToOne(fetch = FetchType.LAZY) | ||
private Member member; | ||
|
||
public JwtToken(final String token, final Member member) { | ||
this.refreshToken = token; | ||
this.member = member; | ||
} | ||
|
||
public boolean isDifferentRefreshToken(final String refreshToken) { | ||
return !this.refreshToken.equals(refreshToken); | ||
} | ||
|
||
public void updateRefreshToken(final String refreshToken) { | ||
this.refreshToken = refreshToken; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "JwtToken{" + | ||
"id=" + id + | ||
", token='" + refreshToken + '\'' + | ||
", member=" + member + | ||
'}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
final JwtToken jwtToken = (JwtToken) o; | ||
return Objects.equals(getId(), jwtToken.getId()); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(getId()); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
backend/src/main/java/org/donggle/backend/auth/JwtTokenProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package org.donggle.backend.auth; | ||
|
||
import io.jsonwebtoken.Claims; | ||
import io.jsonwebtoken.ExpiredJwtException; | ||
import io.jsonwebtoken.Jws; | ||
import io.jsonwebtoken.JwtException; | ||
import io.jsonwebtoken.Jwts; | ||
import io.jsonwebtoken.SignatureAlgorithm; | ||
import io.jsonwebtoken.security.Keys; | ||
import org.donggle.backend.auth.exception.NoSuchTokenException; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.stereotype.Component; | ||
|
||
import javax.crypto.SecretKey; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Date; | ||
|
||
@Component | ||
public class JwtTokenProvider { | ||
private static final String MEMBER_ID_KEY = "memberId"; | ||
|
||
private final SecretKey key; | ||
private final long accessTokenValidityInMilliseconds; | ||
private final long refreshTokenValidityInMilliseconds; | ||
|
||
public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") final String secretKey, | ||
@Value("${security.jwt.token.access-token-expire-length}") final int accessTokenValidityInMilliseconds, | ||
@Value("${security.jwt.token.refresh-token-expire-length}") final int refreshTokenValidityInMilliseconds) { | ||
this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); | ||
this.accessTokenValidityInMilliseconds = accessTokenValidityInMilliseconds; | ||
this.refreshTokenValidityInMilliseconds = refreshTokenValidityInMilliseconds; | ||
} | ||
|
||
public String createAccessToken(final Long payload) { | ||
return createToken(payload, accessTokenValidityInMilliseconds); | ||
} | ||
|
||
public String createRefreshToken(final Long payload) { | ||
return createToken(payload, refreshTokenValidityInMilliseconds); | ||
} | ||
|
||
private String createToken(final Long payload, final long validityInMilliseconds) { | ||
final Date now = new Date(); | ||
final Date validity = new Date(now.getTime() + validityInMilliseconds); | ||
|
||
return Jwts.builder() | ||
.claim(MEMBER_ID_KEY, payload) | ||
.setIssuedAt(now) | ||
.setExpiration(validity) | ||
.signWith(key, SignatureAlgorithm.HS256) | ||
.compact(); | ||
} | ||
|
||
public Long getPayload(final String token) { | ||
return getClaims(token).getBody().get(MEMBER_ID_KEY, Long.class); | ||
} | ||
|
||
public boolean inValidTokenUsage(final String token) { | ||
try { | ||
final Jws<Claims> claims = getClaims(token); | ||
return claims.getBody().getExpiration().before(new Date()); | ||
} catch (final ExpiredJwtException e) { | ||
throw new NoSuchTokenException(); | ||
} catch (final JwtException | IllegalArgumentException e) { | ||
return true; | ||
} | ||
} | ||
|
||
private Jws<Claims> getClaims(final String token) { | ||
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token); | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
backend/src/main/java/org/donggle/backend/auth/JwtTokenService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package org.donggle.backend.auth; | ||
|
||
import jakarta.transaction.Transactional; | ||
import lombok.RequiredArgsConstructor; | ||
import org.donggle.backend.application.repository.TokenRepository; | ||
import org.donggle.backend.domain.member.Member; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class JwtTokenService { | ||
private final TokenRepository tokenRepository; | ||
|
||
public void synchronizeRefreshToken(final Member member, final String refreshToken) { | ||
tokenRepository.findByMemberId(member.getId()) | ||
.ifPresentOrElse( | ||
token -> token.updateRefreshToken(refreshToken), | ||
() -> tokenRepository.save(new JwtToken(refreshToken, member)) | ||
); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...d/src/main/java/org/donggle/backend/auth/exception/EmptyAuthorizationHeaderException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.donggle.backend.auth.exception; | ||
|
||
import org.donggle.backend.exception.business.BusinessException; | ||
|
||
public class EmptyAuthorizationHeaderException extends BusinessException { | ||
private static final String MESSAGE = "header에 Authorization이 존재하지 않습니다."; | ||
|
||
public EmptyAuthorizationHeaderException() { | ||
super(MESSAGE); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
backend/src/main/java/org/donggle/backend/auth/exception/InvalidAccessTokenException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.donggle.backend.auth.exception; | ||
|
||
import org.donggle.backend.exception.business.BusinessException; | ||
|
||
public class InvalidAccessTokenException extends BusinessException { | ||
private static final String MESSAGE = "유효하지 않은 토큰입니다."; | ||
|
||
public InvalidAccessTokenException() { | ||
super(MESSAGE); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
backend/src/main/java/org/donggle/backend/auth/exception/NoSuchTokenException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.donggle.backend.auth.exception; | ||
|
||
import org.donggle.backend.exception.business.BusinessException; | ||
|
||
public class NoSuchTokenException extends BusinessException { | ||
private static final String MESSAGE = "존재하지 않는 토큰입니다."; | ||
|
||
public NoSuchTokenException() { | ||
super(MESSAGE); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
backend/src/main/java/org/donggle/backend/auth/presentation/AuthInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package org.donggle.backend.auth.presentation; | ||
|
||
import jakarta.servlet.http.HttpServletRequest; | ||
import jakarta.servlet.http.HttpServletResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.donggle.backend.auth.JwtTokenProvider; | ||
import org.donggle.backend.auth.exception.InvalidAccessTokenException; | ||
import org.donggle.backend.auth.support.AuthorizationExtractor; | ||
import org.springframework.web.cors.CorsUtils; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
|
||
@RequiredArgsConstructor | ||
public class AuthInterceptor implements HandlerInterceptor { | ||
private final JwtTokenProvider jwtTokenProvider; | ||
|
||
@Override | ||
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) { | ||
if (CorsUtils.isPreFlightRequest(request)) { | ||
return true; | ||
} | ||
|
||
validateToken(request); | ||
return true; | ||
} | ||
|
||
private void validateToken(final HttpServletRequest request) { | ||
final String token = AuthorizationExtractor.extract(request); | ||
if (jwtTokenProvider.inValidTokenUsage(token)) { | ||
throw new InvalidAccessTokenException(); | ||
} | ||
} | ||
} |
Oops, something went wrong.