-
Notifications
You must be signed in to change notification settings - Fork 0
fix: [DM-50] 인증/인가 시스템 통합 검증 및 미구현 비즈니스 로직 전수 조사 #18
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
Merged
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c658951
refactor: [DM-50] AuthService 분리
ParkGoeun00 2c22269
refactor: [DM-50] 테스트 코드 작성
ParkGoeun00 e5a194b
feat: [DM-50] 예외처리 추가 및 오류 수정
ParkGoeun00 7c46024
feat: [DM-50] AI리뷰 반영
ParkGoeun00 685f3a9
feat: [DM-50] Spotless적용
ParkGoeun00 4a3a116
refactor: [DM-50] 코드 리뷰 반영 및 리팩터링
ParkGoeun00 ea91160
refactor: [DM-50] spotless적용
ParkGoeun00 ef7759e
refactor: [DM-50] AI 코드 리뷰 적용
ParkGoeun00 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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
99 changes: 99 additions & 0 deletions
99
src/main/java/kr/java/documind/domain/auth/service/AuthService.java
This file contains hidden or 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,99 @@ | ||
| package kr.java.documind.domain.auth.service; | ||
|
|
||
| import io.jsonwebtoken.ExpiredJwtException; | ||
| import java.util.UUID; | ||
| import kr.java.documind.domain.auth.model.enums.GlobalRole; | ||
| import kr.java.documind.domain.member.model.entity.Member; | ||
| import kr.java.documind.domain.member.service.MemberService; | ||
| import kr.java.documind.global.config.JwtProperties; | ||
| import kr.java.documind.global.exception.ForbiddenException; | ||
| import kr.java.documind.global.exception.UnauthorizedException; | ||
| import kr.java.documind.global.security.RedisTokenService; | ||
| import kr.java.documind.global.security.jwt.TokenProvider; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Slf4j | ||
| @Service | ||
| @RequiredArgsConstructor | ||
| public class AuthService { | ||
|
|
||
| private final TokenProvider jwtProvider; | ||
| private final RedisTokenService redisTokenService; | ||
| private final MemberService memberService; | ||
| private final JwtProperties jwtProperties; | ||
|
|
||
| @Transactional | ||
| public AuthTokens refresh(String refreshToken) { | ||
| if (refreshToken == null) { | ||
| throw new UnauthorizedException("Refresh Token이 없습니다."); | ||
| } | ||
|
|
||
| try { | ||
| jwtProvider.validateToken(refreshToken); | ||
| } catch (ExpiredJwtException e) { | ||
| throw new UnauthorizedException("Refresh Token이 만료되었습니다. 다시 로그인하세요.", e); | ||
| } catch (UnauthorizedException e) { | ||
| log.warn("[AuthService] Refresh Token 검증 실패: {}", e.getMessage()); | ||
| throw new UnauthorizedException("유효하지 않은 Refresh Token입니다. 다시 로그인하세요.", e); | ||
| } | ||
|
|
||
| UUID memberId = jwtProvider.getMemberId(refreshToken); | ||
| GlobalRole globalRole = jwtProvider.getGlobalRole(refreshToken); | ||
|
|
||
| String storedToken = redisTokenService.consumeRefreshToken(memberId); | ||
ParkGoeun00 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (!refreshToken.equals(storedToken)) { | ||
| log.warn("Refresh Token 불일치 — 탈취 가능성: memberId={}", memberId); | ||
| throw new UnauthorizedException("유효하지 않은 Refresh Token입니다. 다시 로그인하세요."); | ||
| } | ||
|
|
||
| Member member = memberService.getMemberWithCompany(memberId); | ||
| if (!member.isActive()) { | ||
| log.warn( | ||
| "[AuthService] 비활성 계정의 토큰 갱신 시도: memberId={} status={}", | ||
| memberId, | ||
| member.getAccountStatus()); | ||
| throw new ForbiddenException("계정이 비활성화되었습니다. 다시 로그인하세요."); | ||
| } | ||
|
|
||
| String newAccessToken = jwtProvider.generateAccessToken(memberId, globalRole); | ||
| String newRefreshToken = jwtProvider.generateRefreshToken(memberId, globalRole); | ||
|
|
||
| redisTokenService.saveRefreshToken( | ||
| memberId, newRefreshToken, jwtProperties.getRefreshExpirationSeconds()); | ||
|
|
||
| log.debug("[AuthService] Access Token 재발급 완료: memberId={}", memberId); | ||
|
|
||
| return new AuthTokens(newAccessToken, newRefreshToken); | ||
| } | ||
|
|
||
| @Transactional | ||
| public void logout(String accessToken, String refreshToken, UUID memberId) { | ||
| if (memberId != null) { | ||
| // 인증된 상태에서의 로그아웃 | ||
| if (accessToken != null) { | ||
| long remainingMillis = jwtProvider.getRemainingMillis(accessToken); | ||
| if (remainingMillis > 0) { | ||
| redisTokenService.addToBlacklist(accessToken, remainingMillis); | ||
| } | ||
| } | ||
| redisTokenService.deleteRefreshToken(memberId); | ||
| log.info("[AuthService] 로그아웃: memberId={}", memberId); | ||
| } else if (refreshToken != null) { | ||
| // 만료된 토큰 등으로 인증 정보가 없을 때 Refresh Token을 이용한 로그아웃 시도 | ||
| try { | ||
| UUID extractedMemberId = jwtProvider.getMemberIdFromExpiredToken(refreshToken); | ||
| redisTokenService.deleteRefreshToken(extractedMemberId); | ||
| log.info( | ||
| "[AuthService] 만료된 토큰으로 로그아웃 처리 (Refresh Token 정리): memberId={}", | ||
| extractedMemberId); | ||
| } catch (Exception e) { | ||
| log.debug("Refresh Token에서 memberId 추출 실패 (이미 정리되었거나 서명 오류): {}", e.getMessage()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| public record AuthTokens(String accessToken, String refreshToken) {} | ||
| } | ||
39 changes: 39 additions & 0 deletions
39
src/main/java/kr/java/documind/domain/member/controller/AdminViewController.java
This file contains hidden or 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,39 @@ | ||
| package kr.java.documind.domain.member.controller; | ||
|
|
||
| import kr.java.documind.domain.auth.model.enums.GlobalRole; | ||
| import kr.java.documind.domain.member.service.CompanyService; | ||
| import kr.java.documind.domain.member.service.CompanyService.AdminPageData; | ||
| import kr.java.documind.global.security.jwt.CustomUserDetails; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
| import org.springframework.stereotype.Controller; | ||
| import org.springframework.ui.Model; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
|
|
||
| @Controller | ||
| @RequestMapping("/admin") | ||
| @RequiredArgsConstructor | ||
| public class AdminViewController { | ||
|
|
||
| private final CompanyService companyService; | ||
|
|
||
| @GetMapping("/companies") | ||
| public String companyAdmin(@AuthenticationPrincipal CustomUserDetails authMember, Model model) { | ||
|
|
||
| if (authMember.getGlobalRole() != GlobalRole.ADMIN) { | ||
| return "redirect:/my/company"; | ||
| } | ||
|
|
||
| AdminPageData pageData = companyService.getAdminCompanyPageData(authMember.getMemberId()); | ||
|
|
||
| model.addAttribute("headerInfo", pageData.headerInfo()); | ||
| model.addAttribute("pending", pageData.pendingCompanies()); | ||
| model.addAttribute("approved", pageData.approvedCompanies()); | ||
| model.addAttribute("suspended", pageData.suspendedCompanies()); | ||
| model.addAttribute("pendingCount", pageData.pendingCount()); | ||
| model.addAttribute("approvedCount", pageData.approvedCount()); | ||
| model.addAttribute("suspendedCount", pageData.suspendedCount()); | ||
| return "member/company-admin"; | ||
| } | ||
| } |
This file contains hidden or 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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.