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

인증인가 기능 리팩토링 #19

Merged
merged 15 commits into from
May 21, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
Expand All @@ -23,7 +24,8 @@ public class CategoryController {
@ApiResponse(content = @Content(schema = @Schema(implementation = SubCategoryListResponse.class)))
@ResponseStatus(HttpStatus.OK)
@GetMapping("/categories")
public Response<SubCategoryListResponse> getSubCategoryList(@RequestParam MainCategory mainCategory) {
return Response.createSuccess(categoryService.getSubCategoryList(mainCategory));
public Response<SubCategoryListResponse> getSubCategoryList(@RequestParam MainCategory mainCategory,
@AuthenticationPrincipal Long userId) {
return Response.createSuccess(categoryService.getSubCategoryList(mainCategory, userId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import java.nio.file.attribute.UserPrincipal;
import java.security.Principal;

@Tag(name = "영상", description = "영상 또는 요약과 관련된 api")
@RestController
@RequiredArgsConstructor
Expand All @@ -22,13 +28,20 @@
public class VideoSummaryController {
private final VideoSummaryService videoSummaryService;

@GetMapping("/test")
public Response<String> test(@AuthenticationPrincipal Long principal) {
System.out.println("principal = "+ principal);
return Response.createSuccess("TEST");
}

@Operation(summary = "영상 요약 요청", description = "영상 요약 요청을 위한 메소드")
@ApiResponse(content = @Content(schema = @Schema(implementation = VideoSummaryInitiateResponse.class)))
@PostMapping("/summaries/initiate")
@ResponseStatus(HttpStatus.ACCEPTED)
public Response<VideoSummaryInitiateResponse> initiateSummarizing(@RequestBody VideoSummaryInitiateRequest videoSummaryInitiateRequest) {
public Response<VideoSummaryInitiateResponse> initiateSummarizing(@RequestBody VideoSummaryInitiateRequest videoSummaryInitiateRequest,
@AuthenticationPrincipal Long userId) {
log.info("summarize initiate video url={}", videoSummaryInitiateRequest.getUrl());
return Response.createSuccess(videoSummaryService.initiateSummarizing(videoSummaryInitiateRequest));
return Response.createSuccess(videoSummaryService.initiateSummarizing(videoSummaryInitiateRequest, userId));
}

@Operation(summary = "영상 요약 상태", description = "영상 요약 상태 확인을 위한 메소드")
Expand All @@ -37,9 +50,10 @@ public Response<VideoSummaryInitiateResponse> initiateSummarizing(@RequestBody V
@ResponseStatus(HttpStatus.OK)
public Response<VideoSummaryStatusResponse> getSummarizeStatus(@PathVariable(name = "videoCode")
@Parameter(name = "videoCode", description = "영상 요약 요청에서 응답받은 비디오 코드", example = "INSTAGRAM_C4kWXhEuQpD")
String videoCode) {
String videoCode,
@AuthenticationPrincipal Long userId) {
log.info("summarize status request videoCode = {}", videoCode);
return Response.createSuccess(videoSummaryService.getStatus(videoCode));
return Response.createSuccess(videoSummaryService.getStatus(videoCode, userId));
}

@Operation(summary = "영상 요약 조회", description = "영상 요약 조회를 위한 메소드")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,4 @@ public class KaKaoResponse extends AuthResponse{

String accessToken;
String refreshToken;
int exprTime;
User user;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ public class ReissueResponse {

String accessToken;
String refreshToken;
int exprTime;
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,38 @@
package com.hongik.graduationproject.jwt;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final TokenProvider tokenProvider;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = parseBearerToken(request);
if (tokenProvider.validateToken(token)) {
setAuthentication(token);
}

try {
AbstractAuthenticationToken authentication;
if (token != null && !token.equalsIgnoreCase("null")) {
String email = tokenProvider.validate(token);

System.out.println("=====vvvvvvv=======");
authentication = new UsernamePasswordAuthenticationToken(email, null, AuthorityUtils.NO_AUTHORITIES);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authentication);
SecurityContextHolder.setContext(securityContext);
filterChain.doFilter(request, response);
filterChain.doFilter(request, response);
}

}
} catch (Exception exception) {
exception.printStackTrace();
}
System.out.println("=====mmmmmm=======");
private void setAuthentication(String accessToken) {
Long userId = tokenProvider.parseUserId(accessToken);

UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userId, "");
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}

@Override
Expand All @@ -52,10 +41,10 @@ protected boolean shouldNotFilter(HttpServletRequest request) {
return path.startsWith("/");
}

private String parseBearerToken(HttpServletRequest request){
private String parseBearerToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");

if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer")) {
if (bearerToken != null && bearerToken.startsWith("Bearer")) {
return bearerToken.substring(7);
}
return null;
Expand Down
100 changes: 36 additions & 64 deletions src/main/java/com/hongik/graduationproject/jwt/TokenProvider.java
Original file line number Diff line number Diff line change
@@ -1,82 +1,54 @@
package com.hongik.graduationproject.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import org.springframework.beans.factory.InitializingBean;
import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
public class TokenProvider implements InitializingBean {

private static final long REFRESH_TOKEN_EXPIRATION = 604800000;
import java.util.Date;

private static final String SECURITY_KEY = "jwtseckey!@";
@Component
public class TokenProvider {
@Value("${jwt.secret}")
private String jwtSecretKey;
@Value("${jwt.access-token-time}")
private long accessTokenTime;
@Value("${jwt.refresh-token-time}")
private long refreshTokenTime;

public String createAccessToken(Long userId) {
return createToken(userId, accessTokenTime);
}

public String createAccessToken(Long id){
Date exprTime = Date.from(Instant.now().plus(1, ChronoUnit.HOURS));
public String createRefreshToken(Long userId) {
return createToken(userId, refreshTokenTime);
}

private String createToken(Long userId, long validTime) {
Date now = new Date();
return Jwts.builder()
.signWith(SignatureAlgorithm.HS512, SECURITY_KEY)
.setIssuedAt(new Date())
.setExpiration(exprTime)
.claim("id", id)
.signWith(SignatureAlgorithm.HS512, jwtSecretKey)
.setIssuedAt(now)
.setExpiration(new Date(now.getTime() + validTime))
.claim("userId", userId)
.compact();
}

public String createRefreshToken(String accessToken){
try {
Claims claims = Jwts.parser().setSigningKey(SECURITY_KEY).parseClaimsJws(accessToken).getBody();

validateExpiration(claims);

Date newExpirationTime = Date.from(Instant.now().plus(REFRESH_TOKEN_EXPIRATION, ChronoUnit.MILLIS));

String refreshToken = Jwts.builder()
.setExpiration(newExpirationTime)
.signWith(SignatureAlgorithm.HS512, SECURITY_KEY)
.compact();

claims.put("refreshToken", refreshToken);

return Jwts.builder()
.setClaims(claims)
.setExpiration(newExpirationTime)
.signWith(SignatureAlgorithm.HS512, SECURITY_KEY)
.compact();
} catch (Exception e) {
throw new BadCredentialsException("Token refresh failed", e);
}
}

public String validate(String token) {
try{
Claims claims = Jwts.parser().setSigningKey(SECURITY_KEY).parseClaimsJws(token).getBody();
validateExpiration(claims);
return claims.getSubject();
} catch (Exception e) {
throw new BadCredentialsException("JWT validation failed", e);
public boolean validateToken(String token) {
if (!StringUtils.hasText(token)) {
throw new RuntimeException();
}
}

private void validateExpiration(Claims claims) {
Date expiration = claims.getExpiration();
if (expiration != null && expiration.before(new Date())) {
throw new BadCredentialsException("JWT has expired");
try {
Jwts.parser().setSigningKey(jwtSecretKey).parseClaimsJws(token).getBody();
} catch (SignatureException | ExpiredJwtException e) {
throw new RuntimeException();
}
return true;
}

@Override
public void afterPropertiesSet() throws Exception {

}

public Long getUserId(String token){
Claims claims = Jwts.parser().setSigningKey(SECURITY_KEY).parseClaimsJws(token).getBody();
return claims.get("id", Long.class);
public Long parseUserId(String token) {
Claims claims = Jwts.parser().setSigningKey(jwtSecretKey).parseClaimsJws(token).getBody();
return claims.get("userId", Long.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ public class CategoryService {
private final CategoryRepository categoryRepository;
private final UserRepository userRepository;

public SubCategoryListResponse getSubCategoryList(MainCategory mainCategory) {
public SubCategoryListResponse getSubCategoryList(MainCategory mainCategory, Long userId) {
// User user = userRepository.getReferenceById(userId);
User user = userRepository.getReferenceById(1L);
// MainCategory mainCategory = subCategoryListRequest.mainCategory();/

List<SubCategoryResponse> subCategoryList = categoryRepository.findAllByMainCategoryAndUser(mainCategory, user).stream()
.map(category -> new SubCategoryResponse(category.getSubCategory(), category.getId()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ public class VideoSummaryService {
private final CategoryRepository categoryRepository;
private final VideoSummaryCategoryRepository videoSummaryCategoryRepository;

public VideoSummaryInitiateResponse initiateSummarizing(VideoSummaryInitiateRequest summaryInitiateRequest) {
public VideoSummaryInitiateResponse initiateSummarizing(VideoSummaryInitiateRequest summaryInitiateRequest, Long userId) {
Platform platform = UrlUtils.getVideoPlatform(summaryInitiateRequest.getUrl());
String videoId = UrlUtils.getVideoId(summaryInitiateRequest.getUrl(), platform);

String videoCode = platform.name() + '_' + videoId;

Long userId = summaryInitiateRequest.getUserId();
userId = summaryInitiateRequest.getUserId();

if (summaryStatusCacheRepository.existsByVideoCodeAndUserId(videoCode, userId)) {
throw new AppException(ErrorCode.ALREADY_REQUESTED_SUMMARIZING);
Expand Down Expand Up @@ -67,9 +67,10 @@ public VideoSummaryDto getVideoSummaryById(Long videoSummaryId) {
}

@Transactional
public VideoSummaryStatusResponse getStatus(String videoCode) {
public VideoSummaryStatusResponse getStatus(String videoCode, Long userId) {
VideoSummaryStatusCache statusCache = summaryStatusCacheRepository.findByVideoCode(videoCode).get();
if (statusCache.getStatus().equals("COMPLETE")) {
// Category category = categoryRepository.findDefaultCategoryByUserIdAndMainCategory(userId, statusCache.getGeneratedMainCategory()).get();
Category category = categoryRepository.findDefaultCategoryByUserIdAndMainCategory(1L, statusCache.getGeneratedMainCategory()).get();
VideoSummary videoSummary = videoSummaryRepository.getReferenceById(statusCache.getVideoSummaryId());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,25 +36,32 @@ public AuthResponse loginUser(AuthRequest authRequest) {
KaKaoRequest kakaoRequest = (KaKaoRequest) authRequest;
KaKaoProfile kakaoProfile = getKaKaoProfile(kakaoRequest.getAccessToken());

checkInvalidProfile(kakaoProfile);

checkDuplicateUser(kakaoProfile);

User savedUser = userRepository.save(User.of(kakaoProfile));

String accessToken = tokenProvider.createAccessToken(savedUser.getId());
String refreshToken = tokenProvider.createRefreshToken(savedUser.getId());

return new KaKaoResponse(accessToken, refreshToken);
}

private void checkInvalidProfile(KaKaoProfile kakaoProfile) {
if (kakaoProfile == null || kakaoProfile.getKakao_account() == null) {
log.error("Failed to retrieve Kakao profile or account information");
throw new RuntimeException(); //TODO: 예외 처리 요망
}
}

private void checkDuplicateUser(KaKaoProfile kakaoProfile) {
String email = kakaoProfile.getKakao_account().getEmail();
Optional<User> optionalUser = userRepository.findByEmail(email);

if (optionalUser.isPresent()) {
throw new RuntimeException(); //TODO: 예외 처리 요망
}

User savedUser = userRepository.save(User.of(kakaoProfile));

String newAccessToken = tokenProvider.createAccessToken(savedUser.getId());
String refreshToken = tokenProvider.createRefreshToken(newAccessToken);
int exprTime = 3600000;

return new KaKaoResponse(newAccessToken, refreshToken, exprTime, savedUser);
}

private KaKaoProfile getKaKaoProfile(String token) {
Expand Down Expand Up @@ -83,27 +90,13 @@ private KaKaoProfile getKaKaoProfile(String token) {

@Override
public ReissueResponse reissueToken(ReissueRequest reissueRequest) {
Long userId = tokenProvider.parseUserId(reissueRequest.getAccessToken());

Long userId = tokenProvider.getUserId(reissueRequest.getAccessToken());

if (userId == null) {
log.error("Failed to retrieve user information");
throw new RuntimeException(); //TODO: 예외 처리 요망
}

Optional<User> optionalUser = userRepository.findById(userId);

if (optionalUser.isEmpty()) {
log.error("User not found");
throw new RuntimeException(); //TODO: 예외 처리 요망
}

tokenProvider.validate(reissueRequest.getAccessToken());
tokenProvider.validateToken(reissueRequest.getRefreshToken());

String newAccessToken = tokenProvider.createAccessToken(userId);
String newRefreshToken = tokenProvider.createRefreshToken(reissueRequest.getRefreshToken());
int exprTime = 3600000;
String newRefreshToken = tokenProvider.createRefreshToken(userId);

return new ReissueResponse(newAccessToken, newRefreshToken, exprTime);
return new ReissueResponse(newAccessToken, newRefreshToken);
}
}
Loading
Loading