로그인시 pending 상태일때도 토큰정보 저장
This commit is contained in:
@@ -55,55 +55,55 @@ public class AuthController {
|
|||||||
@Operation(summary = "로그인", description = "사번으로 로그인하여 액세스/리프레시 토큰을 발급.")
|
@Operation(summary = "로그인", description = "사번으로 로그인하여 액세스/리프레시 토큰을 발급.")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
description = "로그인 성공",
|
description = "로그인 성공",
|
||||||
content = @Content(schema = @Schema(implementation = TokenResponse.class))),
|
content = @Content(schema = @Schema(implementation = TokenResponse.class))),
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "401",
|
responseCode = "401",
|
||||||
description = "로그인 실패 (아이디/비밀번호 오류, 계정잠금 등)",
|
description = "로그인 실패 (아이디/비밀번호 오류, 계정잠금 등)",
|
||||||
content =
|
content =
|
||||||
@Content(
|
@Content(
|
||||||
schema = @Schema(implementation = ErrorResponse.class),
|
schema = @Schema(implementation = ErrorResponse.class),
|
||||||
examples = {
|
examples = {
|
||||||
@ExampleObject(
|
@ExampleObject(
|
||||||
name = "아이디 입력 오류",
|
name = "아이디 입력 오류",
|
||||||
description = "존재하지 않는 아이디",
|
description = "존재하지 않는 아이디",
|
||||||
value =
|
value =
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"code": "LOGIN_ID_NOT_FOUND",
|
"code": "LOGIN_ID_NOT_FOUND",
|
||||||
"message": "아이디를 잘못 입력하셨습니다."
|
"message": "아이디를 잘못 입력하셨습니다."
|
||||||
}
|
}
|
||||||
"""),
|
"""),
|
||||||
@ExampleObject(
|
@ExampleObject(
|
||||||
name = "비밀번호 입력 오류 (4회 이하)",
|
name = "비밀번호 입력 오류 (4회 이하)",
|
||||||
description = "아이디는 정상, 비밀번호를 여러 번 틀린 경우",
|
description = "아이디는 정상, 비밀번호를 여러 번 틀린 경우",
|
||||||
value =
|
value =
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"code": "LOGIN_PASSWORD_MISMATCH",
|
"code": "LOGIN_PASSWORD_MISMATCH",
|
||||||
"message": "비밀번호를 잘못 입력하셨습니다."
|
"message": "비밀번호를 잘못 입력하셨습니다."
|
||||||
}
|
}
|
||||||
"""),
|
"""),
|
||||||
@ExampleObject(
|
@ExampleObject(
|
||||||
name = "비밀번호 오류 횟수 초과",
|
name = "비밀번호 오류 횟수 초과",
|
||||||
description = "비밀번호 5회 이상 오류로 계정 잠김",
|
description = "비밀번호 5회 이상 오류로 계정 잠김",
|
||||||
value =
|
value =
|
||||||
"""
|
"""
|
||||||
{
|
{
|
||||||
"code": "LOGIN_PASSWORD_EXCEEDED",
|
"code": "LOGIN_PASSWORD_EXCEEDED",
|
||||||
"message": "비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다. 로그인 오류에 대해 관리자에게 문의하시기 바랍니다."
|
"message": "비밀번호 오류 횟수를 초과하여 이용하실 수 없습니다. 로그인 오류에 대해 관리자에게 문의하시기 바랍니다."
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
public ApiResponseDto<TokenResponse> signin(
|
public ApiResponseDto<TokenResponse> signin(
|
||||||
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
@io.swagger.v3.oas.annotations.parameters.RequestBody(
|
||||||
description = "로그인 요청 정보",
|
description = "로그인 요청 정보",
|
||||||
required = true)
|
required = true)
|
||||||
@RequestBody
|
@RequestBody
|
||||||
SignInRequest request,
|
SignInRequest request,
|
||||||
HttpServletResponse response) {
|
HttpServletResponse response) {
|
||||||
|
|
||||||
// 사용자 상태 조회
|
// 사용자 상태 조회
|
||||||
String status = authService.getUserStatus(request);
|
String status = authService.getUserStatus(request);
|
||||||
@@ -111,8 +111,8 @@ public class AuthController {
|
|||||||
MembersDto.Member member = new MembersDto.Member();
|
MembersDto.Member member = new MembersDto.Member();
|
||||||
|
|
||||||
authentication =
|
authentication =
|
||||||
authenticationManager.authenticate(
|
authenticationManager.authenticate(
|
||||||
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
|
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
|
||||||
|
|
||||||
String username = authentication.getName(); // UserDetailsService 에서 사용한 username
|
String username = authentication.getName(); // UserDetailsService 에서 사용한 username
|
||||||
|
|
||||||
@@ -121,17 +121,17 @@ public class AuthController {
|
|||||||
|
|
||||||
// Redis에 RefreshToken 저장 (TTL = 7일)
|
// Redis에 RefreshToken 저장 (TTL = 7일)
|
||||||
refreshTokenService.save(
|
refreshTokenService.save(
|
||||||
username, refreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());
|
username, refreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());
|
||||||
|
|
||||||
// HttpOnly + Secure 쿠키에 RefreshToken 저장
|
// HttpOnly + Secure 쿠키에 RefreshToken 저장
|
||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(refreshCookieName, refreshToken)
|
ResponseCookie.from(refreshCookieName, refreshToken)
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
.secure(refreshCookieSecure)
|
.secure(refreshCookieSecure)
|
||||||
.path("/")
|
.path("/")
|
||||||
.maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
|
.maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
|
||||||
.sameSite("Strict")
|
.sameSite("Strict")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||||
|
|
||||||
@@ -156,16 +156,16 @@ public class AuthController {
|
|||||||
@Operation(summary = "토큰 재발급", description = "리프레시 토큰으로 새로운 액세스/리프레시 토큰을 재발급합니다.")
|
@Operation(summary = "토큰 재발급", description = "리프레시 토큰으로 새로운 액세스/리프레시 토큰을 재발급합니다.")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
description = "재발급 성공",
|
description = "재발급 성공",
|
||||||
content = @Content(schema = @Schema(implementation = TokenResponse.class))),
|
content = @Content(schema = @Schema(implementation = TokenResponse.class))),
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "403",
|
responseCode = "403",
|
||||||
description = "만료되었거나 유효하지 않은 리프레시 토큰",
|
description = "만료되었거나 유효하지 않은 리프레시 토큰",
|
||||||
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
|
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
|
||||||
})
|
})
|
||||||
public ResponseEntity<TokenResponse> refresh(String refreshToken, HttpServletResponse response)
|
public ResponseEntity<TokenResponse> refresh(String refreshToken, HttpServletResponse response)
|
||||||
throws AccessDeniedException {
|
throws AccessDeniedException {
|
||||||
if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) {
|
if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) {
|
||||||
throw new AccessDeniedException("만료되었거나 유효하지 않은 리프레시 토큰 입니다.");
|
throw new AccessDeniedException("만료되었거나 유효하지 않은 리프레시 토큰 입니다.");
|
||||||
}
|
}
|
||||||
@@ -182,17 +182,17 @@ public class AuthController {
|
|||||||
|
|
||||||
// Redis 갱신
|
// Redis 갱신
|
||||||
refreshTokenService.save(
|
refreshTokenService.save(
|
||||||
username, newRefreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());
|
username, newRefreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());
|
||||||
|
|
||||||
// 쿠키 갱신
|
// 쿠키 갱신
|
||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(refreshCookieName, newRefreshToken)
|
ResponseCookie.from(refreshCookieName, newRefreshToken)
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
.secure(refreshCookieSecure)
|
.secure(refreshCookieSecure)
|
||||||
.path("/")
|
.path("/")
|
||||||
.maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
|
.maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
|
||||||
.sameSite("Strict")
|
.sameSite("Strict")
|
||||||
.build();
|
.build();
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||||
|
|
||||||
MembersDto.Member member = new MembersDto.Member();
|
MembersDto.Member member = new MembersDto.Member();
|
||||||
@@ -203,12 +203,12 @@ public class AuthController {
|
|||||||
@Operation(summary = "로그아웃", description = "현재 사용자의 토큰을 무효화(리프레시 토큰 삭제)합니다.")
|
@Operation(summary = "로그아웃", description = "현재 사용자의 토큰을 무효화(리프레시 토큰 삭제)합니다.")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@ApiResponse(
|
@ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
description = "로그아웃 성공",
|
description = "로그아웃 성공",
|
||||||
content = @Content(schema = @Schema(implementation = Void.class)))
|
content = @Content(schema = @Schema(implementation = Void.class)))
|
||||||
})
|
})
|
||||||
public ApiResponseDto<ResponseEntity<Object>> logout(
|
public ApiResponseDto<ResponseEntity<Object>> logout(
|
||||||
Authentication authentication, HttpServletResponse response) {
|
Authentication authentication, HttpServletResponse response) {
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
String username = authentication.getName();
|
String username = authentication.getName();
|
||||||
// Redis에서 RefreshToken 삭제
|
// Redis에서 RefreshToken 삭제
|
||||||
@@ -217,13 +217,13 @@ public class AuthController {
|
|||||||
|
|
||||||
// 쿠키 삭제 (Max-Age=0)
|
// 쿠키 삭제 (Max-Age=0)
|
||||||
ResponseCookie cookie =
|
ResponseCookie cookie =
|
||||||
ResponseCookie.from(refreshCookieName, "")
|
ResponseCookie.from(refreshCookieName, "")
|
||||||
.httpOnly(true)
|
.httpOnly(true)
|
||||||
.secure(refreshCookieSecure)
|
.secure(refreshCookieSecure)
|
||||||
.path("/")
|
.path("/")
|
||||||
.maxAge(0)
|
.maxAge(0)
|
||||||
.sameSite("Strict")
|
.sameSite("Strict")
|
||||||
.build();
|
.build();
|
||||||
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
|
||||||
|
|
||||||
return ApiResponseDto.createOK(ResponseEntity.noContent().build());
|
return ApiResponseDto.createOK(ResponseEntity.noContent().build());
|
||||||
|
|||||||
Reference in New Issue
Block a user