Skip to content

Commit

Permalink
Merge pull request #57 from ghdcksgml1/dev
Browse files Browse the repository at this point in the history
merge: Dev Branch
  • Loading branch information
ghdcksgml1 authored Sep 26, 2023
2 parents ce59cb8 + ac9c5d6 commit 9bb93ac
Show file tree
Hide file tree
Showing 43 changed files with 1,301 additions and 200 deletions.
1 change: 1 addition & 0 deletions .github/workflows/CI_main_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
echo "${{ secrets.HEACHINOTIFY_APPLICATION }}" > ./heachi-notify/src/main/resources/application.yml
echo "${{ secrets.HEACHISUPPORT_COMMON_COMMON }}" > ./heachi-support/common/src/main/resources/common.yml
echo "${{ secrets.HEACHISUPPORT_EXTERNALCLIENTS_APPLICATION }}" > ./heachi-support/external-clients/src/main/resources/application.yml
echo "${{ secrets.HEACHIDOMAINREDIS_DATABASE }}" > ./heachi-domain-redis/src/main/resources/redis.yml
# docker Build에 필요한 gradle.properties 설정
- name: gradle.properties 생성
Expand Down
1 change: 1 addition & 0 deletions heachi-core/auth-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies {
implementation project(':heachi-support:logging')
implementation project(':heachi-support:external-clients')
implementation project(':heachi-domain-mysql')
implementation project(':heachi-domain-redis')

implementation 'org.springframework.boot:spring-boot-starter-web' // Spring Web
implementation 'org.springframework.boot:spring-boot-starter-validation' // Bean Validation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ public class HeachiAuthApplication {
public static void main(String[] args) {
SpringApplication.run(HeachiAuthApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
package com.heachi.auth.api.controller.auth;

import com.heachi.admin.common.exception.ExceptionMessage;
import com.heachi.admin.common.exception.auth.AuthException;
import com.heachi.admin.common.exception.oauth.OAuthException;
import com.heachi.admin.common.response.JsonResult;
import com.heachi.auth.api.controller.auth.request.AuthRegisterRequest;
import com.heachi.auth.api.controller.auth.response.UserSimpleInfoResponse;
import com.heachi.auth.api.controller.auth.response.ReissueAccessTokenResponse;
import com.heachi.auth.api.service.auth.AuthService;
import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest;
import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse;
import com.heachi.auth.api.service.oauth.OAuthService;
import com.heachi.auth.api.service.state.LoginStateService;
import com.heachi.mysql.define.user.User;
import com.heachi.mysql.define.user.constant.UserPlatformType;
import com.heachi.mysql.define.user.constant.UserRole;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

import java.security.Principal;

import static com.heachi.mysql.define.user.constant.UserRole.*;
import java.util.Arrays;
import java.util.List;

@Slf4j
@RequiredArgsConstructor
Expand All @@ -32,12 +29,13 @@
public class AuthController {
private final AuthService authService;
private final OAuthService oAuthService;
private final LoginStateService loginStateService;

@GetMapping("/{platformType}/loginPage")
public JsonResult<String> loginPage(
@PathVariable("platformType") UserPlatformType platformType,
HttpServletRequest request) {
String loginPage = oAuthService.loginPage(platformType, request.getSession().getId());
@PathVariable("platformType") UserPlatformType platformType) {
String loginState = loginStateService.generateLoginState();
String loginPage = oAuthService.loginPage(platformType, loginState);

return JsonResult.successOf(loginPage);
}
Expand All @@ -46,11 +44,11 @@ public JsonResult<String> loginPage(
public JsonResult<String> login(
@PathVariable("platformType") UserPlatformType platformType,
@RequestParam("code") String code,
@RequestParam("state") String state,
HttpServletRequest request) {
// state 값 검증 (redis)

AuthServiceLoginResponse loginResponse = authService.login(platformType, code, request.getSession().getId());
@RequestParam("state") String loginState) {
if (!loginStateService.isValidLoginState(loginState)) {
throw new OAuthException(ExceptionMessage.OAUTH_INVALID_STATE);
}
AuthServiceLoginResponse loginResponse = authService.login(platformType, code, loginState);

return JsonResult.successOf(loginResponse);
}
Expand All @@ -69,4 +67,40 @@ public JsonResult<UserSimpleInfoResponse> userInfo(@AuthenticationPrincipal User

return JsonResult.successOf(UserSimpleInfoResponse.of(user));
}
}

@GetMapping("/logout")
public JsonResult<?> logout(@RequestHeader(name = "Authorization") String token) {
List<String> tokens = Arrays.asList(token.split(" "));

if (tokens.size() == 3) {
authService.logout(tokens.get(2));

return JsonResult.successOf("Logout successfully.");
} else {
log.warn(">>>> Invalid Header Access : {}", ExceptionMessage.JWT_INVALID_HEADER.getText());
return JsonResult.failOf(ExceptionMessage.JWT_INVALID_HEADER.getText());
}

}

@PostMapping("/delete")
public JsonResult<?> userDelete(@AuthenticationPrincipal User user) {
authService.userDelete(user.getEmail());

return JsonResult.successOf();
}

@PostMapping("/reissue")
public JsonResult<?> reissueAccessToken(@RequestHeader(name = "Authorization") String token) {
List<String> tokens = Arrays.asList(token.split(" "));

if (tokens.size() == 3) {
ReissueAccessTokenResponse reissueResponse = authService.reissueAccessToken(tokens.get(2));

return JsonResult.successOf(reissueResponse);
} else {
log.warn(">>>> Invalid Header Access : {}", ExceptionMessage.JWT_INVALID_HEADER.getText());
return JsonResult.failOf(ExceptionMessage.JWT_INVALID_HEADER.getText());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.heachi.auth.api.controller.auth.response;

import lombok.Builder;
import lombok.Getter;

@Getter
public class ReissueAccessTokenResponse {
private String accessToken;
private String refreshToken;

@Builder
public ReissueAccessTokenResponse(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@

import com.heachi.admin.common.exception.ExceptionMessage;
import com.heachi.admin.common.exception.auth.AuthException;
import com.heachi.admin.common.exception.oauth.OAuthException;
import com.heachi.admin.common.exception.jwt.JwtException;
import com.heachi.auth.api.controller.auth.response.ReissueAccessTokenResponse;
import com.heachi.auth.api.service.auth.request.AuthServiceRegisterRequest;
import com.heachi.auth.api.service.auth.response.AuthServiceLoginResponse;
import com.heachi.auth.api.service.jwt.JwtService;
import com.heachi.auth.api.service.jwt.JwtTokenDTO;
import com.heachi.auth.api.service.oauth.OAuthService;
import com.heachi.auth.api.service.oauth.response.OAuthResponse;
import com.heachi.auth.api.service.token.RefreshTokenService;
import com.heachi.mysql.define.user.User;
import com.heachi.mysql.define.user.constant.UserPlatformType;
import com.heachi.mysql.define.user.constant.UserRole;
import com.heachi.mysql.define.user.repository.UserRepository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.TypedQuery;
import com.heachi.redis.define.refreshToken.RefreshToken;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -33,9 +34,7 @@ public class AuthService {
private final UserRepository userRepository;
private final OAuthService oAuthService;
private final JwtService jwtService;
private final BCryptPasswordEncoder passwordEncoder;
@PersistenceContext
private EntityManager entityManager;
private final RefreshTokenService refreshTokenService;

private static final String ROLE_CLAIM = "role";
private static final String NAME_CLAIM = "name";
Expand Down Expand Up @@ -64,50 +63,111 @@ public AuthServiceLoginResponse login(UserPlatformType platformType, String code
// 기존 회원의 경우 name, profileImageUrl 변하면 update
findUser.updateProfile(loginResponse.getName(), loginResponse.getProfileImageUrl());

// JWT 토큰 발급
final String token = createJwtToken(findUser);
// JWT Access Token, Refresh Token 발급
JwtTokenDTO tokens = createJwtToken(findUser);

return AuthServiceLoginResponse.builder()
.token(token)
.accessToken(tokens.getAccessToken())
.refreshToken(tokens.getRefreshToken())
.role(findUser.getRole())
.build();
}

@Transactional
public void logout(String refreshToken) {
refreshTokenService.logout(refreshToken);
}

@Transactional
public AuthServiceLoginResponse register(AuthServiceRegisterRequest request) {
User findUser = userRepository.findByEmail(request.getEmail()).orElseThrow(() -> {
// UNAUTH인 토큰을 받고 회원 탈퇴 후 그 토큰으로 회원가입 요청시 예외 처리
log.warn(">>>> User Not Exist : {}", ExceptionMessage.AUTH_INVALID_REGISTER.getText());
throw new AuthException(ExceptionMessage.AUTH_INVALID_REGISTER);
});

// UNAUTH 토큰으로 회원가입을 요청했지만 이미 update되어 UNAUTH가 아닌 사용자 예외 처리
if (findUser.getRole() != UserRole.UNAUTH) {
log.warn(">>>> Not UNAUTH User : {}", ExceptionMessage.AUTH_DUPLICATE_UNAUTH_REGISTER.getText());
throw new AuthException(ExceptionMessage.AUTH_DUPLICATE_UNAUTH_REGISTER);
}

// 회원가입 정보 DB 반영
findUser.updateRegister(request.getRole(), request.getPhoneNumber());

// JWT 토큰 재발급
final String token = createJwtToken(findUser);
// JWT Access Token, Refresh Token 재발급
JwtTokenDTO tokens = createJwtToken(findUser);

return AuthServiceLoginResponse.builder()
.token(token)
.accessToken(tokens.getAccessToken())
.refreshToken(tokens.getRefreshToken())
.role(findUser.getRole())
.build();
}

private String createJwtToken(User user) {
private JwtTokenDTO createJwtToken(User user) {
// JWT 토큰 생성을 위한 claims 생성
HashMap<String, String> claims = new HashMap<>();
claims.put(ROLE_CLAIM, user.getRole().name());
claims.put(NAME_CLAIM, user.getName());
claims.put(PROFILE_IMAGE_CLAIM, user.getProfileImageUrl());

// JWT 토큰 생성 (claims, UserDetails)
final String token = jwtService.generateToken(claims, user);
// Access Token 생성
final String accessToken = jwtService.generateAccessToken(claims, user);
// Refresh Token 생성
final String refreshToken = jwtService.generateRefreshToken(claims, user);

log.info(">>>> {} generate Tokens", user.getName());

// Refresh Token 저장 - REDIS
RefreshToken rt = RefreshToken.builder()
.refreshToken(refreshToken)
.email(user.getEmail())
.build();
refreshTokenService.saveRefreshToken(rt);


return JwtTokenDTO.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}

@Transactional
public void userDelete(String email) {
User user = userRepository.findByEmail(email).orElseThrow(() -> {
log.warn(">>>> User Delete Fail : {}", ExceptionMessage.AUTH_NOT_FOUND.getText());
throw new AuthException(ExceptionMessage.AUTH_NOT_FOUND);
});

try {
userRepository.deleteById(user.getId());
log.info(">>>> {} Info is Deleted.", user.getName());
} catch (IllegalArgumentException e) {
log.error(">>>> ID = {} : 계정 삭제에 실패했습니다.", user.getId());
throw new AuthException(ExceptionMessage.AUTH_DELETE_FAIL);
}
}

@Transactional
public ReissueAccessTokenResponse reissueAccessToken(String refreshToken) {
Claims claims = jwtService.extractAllClaims(refreshToken);

// 토큰 검증
if (jwtService.isTokenValid(refreshToken, claims.getSubject())) {
// 리프레시 토큰을 이용해 새로운 엑세스 토큰 발급
String accessToken = refreshTokenService.reissue(claims, refreshToken);
log.info(">>>> {} reissue AccessToken.", claims.getSubject());

return ReissueAccessTokenResponse.builder()
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();

} else {
log.warn(">>>> Token Validation Fail : {}", ExceptionMessage.JWT_INVALID_RTK.getText());
throw new JwtException(ExceptionMessage.JWT_INVALID_RTK);
}

// 로그인 반환 객체 생성
return token;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@

@Getter
public class AuthServiceLoginResponse {
private String token;
private String accessToken;
private String refreshToken;
private UserRole role;

@Builder
private AuthServiceLoginResponse(String token, UserRole role) {
this.token = token;
public AuthServiceLoginResponse(String accessToken, String refreshToken, UserRole role) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
this.role = role;
}
}

This file was deleted.

Loading

0 comments on commit 9bb93ac

Please sign in to comment.