Skip to content

Commit

Permalink
Merge pull request #7 from LikeLion-Hackathon-T1/feat/login-update
Browse files Browse the repository at this point in the history
Feat/login update
  • Loading branch information
Jeongh00 authored Jul 21, 2024
2 parents 0d00983 + ce7a695 commit bf172a7
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Backend CD # actions 이름

on:
push:
branches: [ feat/market-info ]
branches: [ feat/login-update ]

jobs:
deploy:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import com.likelion.commonmodule.exception.jwt.SecurityCustomException;
import com.likelion.commonmodule.redis.util.RedisUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import com.likelion.coremodule.user.dto.LoginResponse;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -19,6 +17,7 @@
import java.util.concurrent.TimeUnit;

import static com.likelion.commonmodule.exception.jwt.SecurityErrorCode.INVALID_TOKEN;
import static com.likelion.commonmodule.exception.jwt.SecurityErrorCode.TOKEN_EXPIRED;

@Component
@Slf4j
Expand All @@ -43,37 +42,37 @@ public JwtUtil(
redisUtil = redis;
}

public String createJwtAccessToken(String nickname, String subId) {
public String createJwtAccessToken(String email, String subId) {
Instant issuedAt = Instant.now();
Instant expiration = issuedAt.plusMillis(accessExpMs);

return Jwts.builder()
.setHeaderParam("alg", "HS256")
.setHeaderParam("typ", "JWT")
.setSubject(subId)
.claim("nickname", nickname)
.claim("email", email)
.setIssuedAt(Date.from(issuedAt))
.setExpiration(Date.from(expiration))
.signWith(secretKey)
.compact();
}

public String createJwtRefreshToken(String nickname, String subId) {
public String createJwtRefreshToken(String email, String subId) {
Instant issuedAt = Instant.now();
Instant expiration = issuedAt.plusMillis(refreshExpMs);

String refreshToken = Jwts.builder()
.setHeaderParam("alg", "HS256")
.setHeaderParam("typ", "JWT")
.setSubject(subId)
.claim("nickname", nickname)
.claim("email", email)
.setIssuedAt(Date.from(issuedAt))
.setExpiration(Date.from(expiration))
.signWith(secretKey)
.compact();

redisUtil.saveAsValue(
subId + "_refresh_token",
email + "_refresh_token",
refreshToken,
refreshExpMs,
TimeUnit.MILLISECONDS
Expand All @@ -94,6 +93,35 @@ public String resolveAccessToken(HttpServletRequest request) {
return authorization.split(" ")[1];
}

public LoginResponse reissueToken(String refreshToken) {
try {
validateRefreshToken(refreshToken);
log.info("[*] Valid RefreshToken");

// 삭제 로직
String email = getEmail(refreshToken);
redisUtil.delete(email + "_refresh_token");

String subId = getSubjectFromToken(refreshToken);

return new LoginResponse(
createJwtAccessToken(email, subId),
createJwtRefreshToken(email, subId)
);
} catch (IllegalArgumentException iae) {
throw new SecurityCustomException(INVALID_TOKEN, iae);
} catch (ExpiredJwtException eje) {
throw new SecurityCustomException(TOKEN_EXPIRED, eje);
}
}

public void deleteToken(String refreshToken) {

// 삭제 로직
String email = getEmail(refreshToken);
redisUtil.delete(email + "_refresh_token");
}

public void validateRefreshToken(String refreshToken) {
// refreshToken 유효성 검증
String email = getEmail(refreshToken);
Expand All @@ -105,6 +133,15 @@ public void validateRefreshToken(String refreshToken) {
}
}

public String getSubjectFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}

public Long getId(String token) {
return Long.parseLong(getClaims(token).getSubject());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,26 @@ public LoginResponse kakaoLogin(final KakaoLoginRequest kakaoLoginRequest) {
final User user = userRepository.findBySubId(oidcDecodePayload.sub())
.orElseGet(() -> createNewKakaoUser(oidcDecodePayload));

return new LoginResponse(jwtUtil.createJwtAccessToken(oidcDecodePayload.nickname(), oidcDecodePayload.sub()),
jwtUtil.createJwtRefreshToken(oidcDecodePayload.nickname(), oidcDecodePayload.sub()));
return new LoginResponse(jwtUtil.createJwtAccessToken(oidcDecodePayload.email(), oidcDecodePayload.sub()),
jwtUtil.createJwtRefreshToken(oidcDecodePayload.email(), oidcDecodePayload.sub()));
}

private User createNewKakaoUser(final OidcDecodePayload oidcDecodePayload) {

final User newUser = User.createSocialUser(oidcDecodePayload.sub(), oidcDecodePayload.nickname(), oidcDecodePayload.picture(), oidcDecodePayload.email());
return userRepository.save(newUser);
}

@Transactional
public LoginResponse reissueToken(String refreshToken) {

return jwtUtil.reissueToken(refreshToken);
}

@Transactional
public void logout(String refreshToken, String name) {

jwtUtil.deleteToken(refreshToken);
if (userRepository.findByName(name).isPresent()) userRepository.deleteByName(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.likelion.apimodule.user.dto;

public record UserInfo(String email,
String subId) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.likelion.apimodule.user.application.LoginUseCase;
import com.likelion.apimodule.user.dto.KakaoLoginRequest;
import com.likelion.commonmodule.exception.common.ApplicationResponse;
import com.likelion.commonmodule.security.util.AuthConsts;
import com.likelion.coremodule.user.dto.LoginResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand All @@ -10,10 +12,7 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
Expand All @@ -35,7 +34,42 @@ public class UserController {
}
)
@Operation(summary = "카카오 로그인 API", description = "카카오 로그인 API입니다.")
public LoginResponse kakaoLogin(@Valid @RequestBody KakaoLoginRequest kakaoLoginRequest) {
return loginUseCase.kakaoLogin(kakaoLoginRequest);
public ApplicationResponse<LoginResponse> kakaoLogin(@Valid @RequestBody KakaoLoginRequest kakaoLoginRequest) {
LoginResponse response = loginUseCase.kakaoLogin(kakaoLoginRequest);
return ApplicationResponse.ok(response);
}

@GetMapping("/reissue")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "토큰 재발급 성공",
useReturnTypeSchema = true
)
}
)
@Operation(summary = "토큰 재발급 API", description = "토큰 재발급 API입니다.")
public ApplicationResponse<LoginResponse> reissue(@RequestHeader(AuthConsts.REFRESH_TOKEN_HEADER) String refreshToken) {
LoginResponse response = loginUseCase.reissueToken(refreshToken);
return ApplicationResponse.ok(response);
}

@DeleteMapping("/logout")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "로그아웃 성공",
useReturnTypeSchema = true
)
}
)
@Operation(summary = "로그아웃 API", description = "로그아웃 API입니다.")
public ApplicationResponse<String> logout(@RequestHeader(AuthConsts.REFRESH_TOKEN_HEADER) String refreshToken,
@RequestParam String name) {

loginUseCase.logout(refreshToken, name);
return ApplicationResponse.ok("로그아웃 되었습니다.");
}
}
2 changes: 1 addition & 1 deletion api-module/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ security:
secret: aGFuZXVtLWZvb2Rnby1qd3Qtc2VjcmV0LWtleQo=
token:
access-expiration-time: 86400
refresh-expiration-time: 86400
refresh-expiration-time: 604800

logging:
level:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,15 @@ public static CorsConfigurationSource apiConfigurationSource() {
ArrayList<String> allowedOriginPatterns = new ArrayList<>();
allowedOriginPatterns.add("http://localhost:8080");
allowedOriginPatterns.add("https://localhost:8080");

allowedOriginPatterns.add("http://localhost:3000");
allowedOriginPatterns.add("https://localhost:3000");

allowedOriginPatterns.add("https://syluv.link");

allowedOriginPatterns.add("https://www.syluv.store");
allowedOriginPatterns.add("https://syluv.store");

configuration.setAllowedOrigins(allowedOriginPatterns);
configuration.setAllowedMethods(List.of("HEAD", "POST", "GET", "DELETE", "PUT", "OPTIONS", "PATCH"));
configuration.setAllowedHeaders(List.of("*"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.likelion.commonmodule.security.util;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AuthConsts {
public static final String AUTHENTICATION_TYPE= "Bearer";
public static final String AUTHORIZATION = "Authorization";
public static final String EMPTY_HEADER = null;
public static final String REFRESH_TOKEN_HEADER = "RefreshToken";
public static final String AUTHENTICATION_TYPE_PREFIX = AUTHENTICATION_TYPE+" ";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
@AllArgsConstructor
public enum UserErrorCode implements BaseErrorCode {

No_USER_INFO(HttpStatus.BAD_REQUEST, "2000", "사용자 정보가 존재하지 않습니다.");
No_USER_INFO(HttpStatus.BAD_REQUEST, "2000", "사용자 정보가 존재하지 않습니다."),
EMPTY_AUTHORIZATION_HEADER(HttpStatus.BAD_REQUEST, "2000", "토큰 정보가 비어 있습니다."),
INVALID_AUTHORIZATION_TYPE(HttpStatus.BAD_REQUEST, "2000", "타입이 유효하지 않습니다.");

private final HttpStatus httpStatus;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findBySubId(String subId);

Optional<User> findByEmail(String email);

Optional<User> findByName(String name);

void deleteByName(String name);
}

0 comments on commit bf172a7

Please sign in to comment.