Skip to content

Commit

Permalink
Merge pull request #12 from Kusitms-29th-Kobaco-A/feat/jwt
Browse files Browse the repository at this point in the history
feat : jwt 설정 추가
  • Loading branch information
tlarbals824 authored Feb 27, 2024
2 parents 56e3c4f + 5918eaf commit 7d1c80e
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .deploy/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ services:
environment:
- DB_URL=${DB_URL}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- DB_PASSWORD=${DB_PASSWORD}
- TOKEN_SECRET=${TOKEN_SECRET}
1 change: 1 addition & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ jobs:
echo "DB_URL=${{secrets.DB_URL}}" >> .env
echo "DB_USERNAME=${{secrets.DB_USERNAME}}" >> .env
echo "DB_PASSWORD=${{secrets.DB_PASSWORD}}" >> .env
echo "TOKEN_SECRET=${{secrets.TOKEN_SECRET}}" >> .env
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/github-actions-kobaco
sudo docker stop kobaco
Expand Down
9 changes: 9 additions & 0 deletions kobaco/kobaco/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ dependencies {
testImplementation 'org.springframework.security:spring-security-test'
runtimeOnly 'com.mysql:mysql-connector-j'
runtimeOnly 'com.mysql:mysql-connector-j'


// jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.5'

// configuration properties
implementation 'org.springframework.boot:spring-boot-configuration-processor'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package core.kobaco.global.config;

import core.kobaco.global.jwt.JwtProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class ConfigurationPropertiesConfig {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package core.kobaco.global.exception;

import lombok.Getter;

@Getter
public class BusinessException extends RuntimeException{
private final ErrorCode errorCode;

public BusinessException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package core.kobaco.global.exception;

public interface ErrorCode {
String getCode();
String getMessage();
}
11 changes: 11 additions & 0 deletions kobaco/kobaco/src/main/java/core/kobaco/global/jwt/JwtConsts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package core.kobaco.global.jwt;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class JwtConsts {
public static final String TOKEN_ISSUER = "kobaco";
public static final String USER_CLAIMS = "user_claims";
public static final String TOKEN_TYPE = "token_type";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package core.kobaco.global.jwt;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.GetMapping;

@Getter
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
private final String secret;
private final long accessTokenExpirationTime;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package core.kobaco.global.jwt;


import core.kobaco.global.jwt.exception.AuthErrorCode;
import core.kobaco.global.jwt.exception.ExpiredTokenException;
import core.kobaco.global.jwt.exception.InvalidTokenException;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.jackson.io.JacksonDeserializer;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.security.SignatureException;
import java.util.Date;

@Component
@RequiredArgsConstructor
public class JwtProvider {
private final JwtProperties jwtProperties;

public String generateAccessToken(final PrivateClaims.UserClaims userClaims) {
return generateToken(userClaims.createPrivateClaims(TokenType.ACCESS_TOKEN), jwtProperties.getAccessTokenExpirationTime());
}

public PrivateClaims.UserClaims extractUserClaimsFromToken(String token, TokenType tokenType) {
return initializeJwtParser(tokenType)
.parseSignedClaims(token)
.getPayload()
.get(JwtConsts.USER_CLAIMS, PrivateClaims.UserClaims.class);
}

private String generateToken(final PrivateClaims privateClaims, final Long expirationTime) {
final Date now = new Date();
return Jwts.builder()
.issuer(JwtConsts.TOKEN_ISSUER)
.claims(privateClaims.createClaimsMap())
.issuedAt(now)
.expiration(new Date(now.getTime() + expirationTime))
.signWith(getSigningKey())
.compact();
}

/**
* @return 서명에 사용할 Key 반환
*/
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(jwtProperties.getSecret()));
}

/**
* @throws InvalidTokenException 잘못된 토큰이 요청되었을 때 반환(서명 오류, 잘못된 토큰 형식, 잘못된 토큰 발급자, null이거나 공백인 경우)
* @throws ExpiredTokenException 만료된 토큰이 요청되었을 때 반환
*/
public void validateToken(final String token, final TokenType tokenType) {
final JwtParser jwtParser = initializeJwtParser(tokenType);
try {
jwtParser.parse(token);
} catch (MalformedJwtException | IncorrectClaimException | IllegalArgumentException e) {
throw new InvalidTokenException(AuthErrorCode.INVALID_TOKEN);
} catch (ExpiredJwtException e) {
throw new ExpiredTokenException(AuthErrorCode.EXPIRED_TOKEN);
}
}


private JwtParser initializeJwtParser(final TokenType tokenType) {
return Jwts.parser()
.json(new JacksonDeserializer<>(PrivateClaims.getClaimsTypeDetailMap()))
.verifyWith(getSigningKey())
.requireIssuer(JwtConsts.TOKEN_ISSUER)
.require(JwtConsts.TOKEN_TYPE, tokenType)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package core.kobaco.global.jwt;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.Map;

@Getter
public class PrivateClaims {
private final UserClaims userClaims;
private final TokenType tokenType;

public PrivateClaims(UserClaims userClaims, TokenType tokenType) {
this.userClaims = userClaims;
this.tokenType = tokenType;
}

public Map<String, Object> createClaimsMap() {
return Map.of(
JwtConsts.USER_CLAIMS, userClaims,
JwtConsts.TOKEN_TYPE, tokenType.name()
);
}

public static Map<String, Class<?>> getClaimsTypeDetailMap(){
return Map.of(
JwtConsts.USER_CLAIMS, UserClaims.class,
JwtConsts.TOKEN_TYPE, TokenType.class
);
}

@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public static class UserClaims{
@JsonProperty("user_id")
private Long userId;
private UserClaims(Long userId) {
this.userId = userId;
}

public static UserClaims of(Long userId){
return new UserClaims(userId);
}

public PrivateClaims createPrivateClaims(TokenType tokenType) {
return new PrivateClaims(this, tokenType);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package core.kobaco.global.jwt;


public enum TokenType {
ACCESS_TOKEN,
REFRESH_TOKEN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package core.kobaco.global.jwt.exception;

import core.kobaco.global.exception.ErrorCode;


public enum AuthErrorCode implements ErrorCode {
INVALID_TOKEN("유효하지 않는 토큰입니다.", 1000),
EXPIRED_TOKEN("만료된 토큰입니다.", 1001),
;

private final String code;
private final String message;

AuthErrorCode(String code, String message) {
this.code = code;
this.message = message;
}

@Override
public String getCode() {
return code;
}

@Override
public String getMessage() {
return message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package core.kobaco.global.jwt.exception;

import core.kobaco.global.exception.ErrorCode;

public class ExpiredTokenException extends TokenException {
public ExpiredTokenException(ErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package core.kobaco.global.jwt.exception;

import core.kobaco.global.exception.ErrorCode;

public class InvalidTokenException extends TokenException{

public InvalidTokenException(ErrorCode errorCode) {
super(errorCode);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package core.kobaco.global.jwt.exception;

import core.kobaco.global.exception.BusinessException;
import core.kobaco.global.exception.ErrorCode;

import java.nio.Buffer;

public class TokenException extends BusinessException {
public TokenException(ErrorCode errorCode) {
super(errorCode);
}
}
5 changes: 5 additions & 0 deletions kobaco/kobaco/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ spring:
username: ${DB_USERNAME}
password: ${DB_PASSWORD}


jwt:
secret: ${TOKEN_SECRET}
expiration: 86400000 #

0 comments on commit 7d1c80e

Please sign in to comment.