Skip to content
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
4 changes: 4 additions & 0 deletions SixthSeventhSeminar/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
testImplementation 'junit:junit:4.13.1'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand All @@ -43,6 +44,9 @@ dependencies {

// swagger
implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package sopt.org.FourthSeminar.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Value("${spring.redis.host}")
private String redisHost;

@Value("${spring.redis.port}")
private int redisPort;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(redisHost, redisPort);
}

@Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}

@Bean
public StringRedisTemplate stringRedisTemplate() {
StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
stringRedisTemplate.setKeySerializer(new StringRedisSerializer());
stringRedisTemplate.setValueSerializer(new StringRedisSerializer());
stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
return stringRedisTemplate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,66 @@
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import sopt.org.FourthSeminar.exception.Error;
import sopt.org.FourthSeminar.exception.model.UnauthorizedException;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.time.Duration;
import java.util.Base64;
import java.util.Date;

@Service
@RequiredArgsConstructor
public class JwtService {
@Value("${jwt.secret}")
private String jwtSecret;

private final RedisTemplate<String, String> redisTemplate;

@PostConstruct
protected void init() {
jwtSecret = Base64.getEncoder()
.encodeToString(jwtSecret.getBytes(StandardCharsets.UTF_8));
}

// JWT 토큰 발급
public String issuedToken(String userId) {
private String issuedToken(final String userId, final Long duration, final String type) {
final Date now = new Date();

// 클레임 생성
final Claims claims = Jwts.claims()
.setSubject("access_token")
.setSubject(type)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + 120 * 60 * 1000L));

//private claim 등록
claims.put("userId", userId);
.setExpiration(new Date(now.getTime() + duration));

return Jwts.builder()
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
.setClaims(claims)
.signWith(getSigningKey())
.compact();

}

// JWT 액세스 토큰 발급
public String issuedAccessToken(String userId) {
final long duration = 120 * 60 * 1000L;
return issuedToken(userId, duration, "access_token");
}

// JWT 리프레시 토큰 발급
public String issuedRefreshToken(String userId) {
final Long duration = 120 * 60 * 30 * 1000L;
String refreshToken = issuedToken(userId, duration, "refresh_token");
redisTemplate.opsForValue().set(String.valueOf(userId), refreshToken, Duration.ofMillis(duration));
return refreshToken;
}


private Key getSigningKey() {
final byte[] keyBytes = jwtSecret.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public ApiResponse<UserResponseDto> create(@RequestBody @Valid final UserRequest
@Operation(summary = "유저 로그인", description = "로그인합니다.")
public ApiResponse<UserLoginResponseDto> login(@RequestBody @Valid final UserLoginRequestDto request) {
final Long userId = userService.login(request);
final String token = jwtService.issuedToken(String.valueOf(userId));
return ApiResponse.success(Success.LOGIN_SUCCESS, UserLoginResponseDto.of(userId, token));
final String accessToken = jwtService.issuedAccessToken(String.valueOf(userId));
final String refreshToken = jwtService.issuedRefreshToken(String.valueOf(userId));
return ApiResponse.success(Success.LOGIN_SUCCESS, UserLoginResponseDto.of(userId, accessToken, refreshToken));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ public class UserLoginResponseDto {
private Long userId;
private String accessToken;

public static UserLoginResponseDto of(Long userId, String accessToken) {
return new UserLoginResponseDto(userId, accessToken);
private String refreshToken;

public static UserLoginResponseDto of(Long userId, String accessToken, String refreshToken) {
return new UserLoginResponseDto(userId, accessToken, refreshToken);
}
}