Skip to content

Commit

Permalink
[fix]: merge pull request
Browse files Browse the repository at this point in the history
  • Loading branch information
realisshomyang committed Jan 31, 2024
2 parents bb48973 + 7df1f0b commit dac111e
Show file tree
Hide file tree
Showing 39 changed files with 882 additions and 197 deletions.
12 changes: 12 additions & 0 deletions .ebextensions_dev/00-makeFiles.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
files:
"/sbin/appstart" :
mode: "000755"
owner: webapp
group: webapp
content: |
#!/usr/bin/env bash
JAR_PATH=/var/app/current/application.jar

# run app
killall java
java -Dfile.encoding=UTF-8 -jar $JAR_PATH
3 changes: 3 additions & 0 deletions .ebextensions_dev/01-set-timezone.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
commands:
set_time_zone:
command: ln -f -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
61 changes: 61 additions & 0 deletions .github/workflows/dev_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Onnoff Dev CI/CD

on:
pull_request:
types: [closed]
workflow_dispatch: # (2).수동 실행도 가능하도록

jobs:
build:
runs-on: ubuntu-latest # (3).OS환경
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev'

steps:
- name: Checkout
uses: actions/checkout@v2 # (4).코드 check out

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: 17 # (5).자바 설치
distribution: 'adopt'

- name: Grant execute permission for gradlew
run: chmod +x ./gradlew
shell: bash # (6).권한 부여

- name: Build with Gradle
run: ./gradlew clean build -x test
shell: bash # (7).build 시작

- name: Get current time
uses: 1466587594/get-current-time@v2
id: current-time
with:
format: YYYY-MM-DDTHH-mm-ss
utcOffset: "+09:00" # (8).build 시점의 시간확보

- name: Show Current Time
run: echo "CurrentTime=$"
shell: bash # (9).확보한 시간 보여주기

- name: Generate deployment package
run: |
mkdir -p deploy
cp build/libs/*.jar deploy/application.jar
cp Procfile deploy/Procfile
cp -r .ebextensions_dev deploy/.ebextensions
cp -r .platform deploy/.platform
cd deploy && zip -r deploy.zip .
- name: Beanstalk Deploy
uses: einaregilsson/beanstalk-deploy@v20
with:
aws_access_key: ${{ secrets.AWS_ACTION_ACCESS_KEY_ID }}
aws_secret_key: ${{ secrets.AWS_ACTION_SECRET_ACCESS_KEY }}
application_name: onnoff-dev # 원하는 어플 이름
environment_name: Onnoff-dev-env # 원하는 환경 이름
version_label: github-action-${{ steps.current-time.outputs.formattedTime }}
region: ap-northeast-2
deployment_package: deploy/deploy.zip
wait_for_deployment: false
63 changes: 63 additions & 0 deletions .platform/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
user nginx;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 33282;

events {
use epoll;
worker_connections 1024;
multi_accept on;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;


log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

include conf.d/*.conf;

map $http_upgrade $connection_upgrade {
default "upgrade";
}

upstream springboot {
server 127.0.0.1:8080;
keepalive 1024;
}

server {
listen 80 default_server;
listen [::]:80 default_server;

location / {
proxy_pass http://springboot;
# CORS 관련 헤더 추가
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type';
proxy_http_version 1.1;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Upgrade $http_upgrade;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

access_log /var/log/nginx/access.log main;

client_header_timeout 60;
client_body_timeout 60;
keepalive_timeout 60;
gzip off;
gzip_comp_level 4;

# Include the Elastic Beanstalk generated locations
include conf.d/elasticbeanstalk/healthd.conf;
}
}
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: appstart
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ tasks.named('bootBuildImage') {
tasks.named('test') {
useJUnitPlatform()
}

jar {
enabled = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@
@AllArgsConstructor
public enum SuccessStatus implements BaseCode {
// 일반적인 응답
_OK(HttpStatus.OK, "COMMON200", "성공입니다."),
_OK(HttpStatus.OK, "COMMON200", "성공입니다.");

// 로그인 응답
NEED_USER_DETAIL(HttpStatus.OK, "LOGIN200", "토큰이 유효하고 유저의 추가정보가 필요합니다.");
private final HttpStatus httpStatus;
private final String code;
private final String message;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/onnoff/onnoff/auth/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new UserInterceptor(userService, jwtUtil))
.addPathPatterns("/**") // 스프링 경로는 /*와 /**이 다름
.excludePathPatterns("/swagger-ui/**", "/v3/api-docs/**", "/oauth2/**", "/on/**", "/message/**");
.excludePathPatterns("/swagger-ui/**", "/v3/api-docs/**", "/oauth2/**", "/on/**", "/health","/message/**");
}
}
108 changes: 67 additions & 41 deletions src/main/java/com/onnoff/onnoff/auth/controller/LoginController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.onnoff.onnoff.apiPayload.ApiResponse;
import com.onnoff.onnoff.apiPayload.code.status.SuccessStatus;
import com.onnoff.onnoff.auth.UserContext;
import com.onnoff.onnoff.auth.feignClient.dto.KakaoOauth2DTO;
import com.onnoff.onnoff.auth.dto.LoginRequestDTO;
import com.onnoff.onnoff.auth.feignClient.dto.TokenResponse;
import com.onnoff.onnoff.auth.feignClient.dto.kakao.KakaoOauth2DTO;
import com.onnoff.onnoff.auth.jwt.dto.JwtToken;
import com.onnoff.onnoff.auth.jwt.service.JwtUtil;
import com.onnoff.onnoff.auth.service.LoginService;
import com.onnoff.onnoff.auth.service.AppleLoginService;
import com.onnoff.onnoff.auth.service.KakaoLoginService;
import com.onnoff.onnoff.domain.user.User;
import com.onnoff.onnoff.domain.user.converter.UserConverter;
import com.onnoff.onnoff.domain.user.dto.UserResponseDTO;
import com.onnoff.onnoff.domain.user.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
Expand All @@ -29,82 +29,108 @@
@Controller
@RequiredArgsConstructor
public class LoginController {
private final LoginService loginService;
private final KakaoLoginService kakaoLoginService;
private final AppleLoginService appleLoginService;
private final UserService userService;
private final JwtUtil jwtUtil;

@Value("${kakao.redirect-uri}")
private String redirectUri;




/*
테스트용 API, CORS 때문에 직접 호출하지 않고 redirect
*/
@GetMapping("/oauth2/authorize/kakao")
public String login(){
String redirectUri = UriComponentsBuilder.fromUriString("https://kauth.kakao.com/oauth/authorize")
String toRedirectUri = UriComponentsBuilder.fromUriString("https://kauth.kakao.com/oauth/authorize")
.queryParam("response_type", "code")
.queryParam("client_id", "32c0787d1b1e9fcabcc24af247903ba8")
.queryParam("redirect_uri", "http://localhost:8080/oauth2/login/kakao")
.queryParam("redirect_uri", redirectUri)
.toUriString();
return "redirect:" + redirectUri;
return "redirect:" + toRedirectUri;
}
/*
테스트용 API
*/
@GetMapping("/oauth2/login/kakao")
public ResponseEntity<String> getAccessToken(@RequestParam(name = "code") String code){
String accessToken = loginService.getAccessToken(code);
return ResponseEntity.ok("http://localhost:8080/oauth2/kakao/token/validate?accessToken="+accessToken);
TokenResponse tokenResponse = kakaoLoginService.getAccessTokenByCode(code);
return ResponseEntity.ok("accessToken="+ tokenResponse.getAccessToken() +
"idToken=" + tokenResponse.getIdToken());

}
/*
1. 토큰 유효성 검증
1. ID 토큰 유효성 검증
2. 사용자 정보 얻어오기
3. DB 조회 및 추가
4. 응답 헤더에 Jwt 토큰 추가
*/

@Operation(summary = "토큰 검증 API",description = "토큰을 검증 하고 이에 대한 결과를 응답합니다. 추가 정보 입력 여부도 같이 응답 합니다.")
@ApiResponses(value = {
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "202", description = "토큰 검증 성공," + " 추가 정보 기입이 필요합니다.",
content = @Content(schema = @Schema(implementation = UserResponseDTO.ApiResponseLoginDTO.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "토큰 검증 성공",
content = @Content(schema = @Schema(implementation = UserResponseDTO.ApiResponseUserDetailDTO.class)))
})
@ResponseBody
@PostMapping("/oauth2/kakao/token/validate")
public ApiResponse<?> validateToken(HttpServletResponse response, @RequestBody String accessToken) {
// 토큰 검증
loginService.validate(accessToken);
public ApiResponse<UserResponseDTO.LoginDTO> validateKakoToken(HttpServletResponse response, @RequestBody LoginRequestDTO.KakaoTokenValidateDTO requestDTO) {
// identity 토큰 검증
kakaoLoginService.validate(requestDTO.getIdentityToken());
// ok -> 유저 정보 가져오기
KakaoOauth2DTO.UserInfoResponseDTO userInfoResponseDTO = null;
KakaoOauth2DTO.UserInfoResponseDTO userInfo;
try {
userInfoResponseDTO = loginService.getUserInfo(accessToken);
userInfo = kakaoLoginService.getUserInfo(requestDTO.getAccessToken());
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
// 유저 정보에 DB 조회하고 정보 있으면 응답만, 없으면 저장까지, 추가정보 입력 여부에 따라서 응답 다르게
Long oauthId = userInfoResponseDTO.getId();
String oauthId = userInfo.getSub();
User user;
if( userService.isExistByOauthId(oauthId)){
User user = userService.getUserByOauthId(oauthId);
// 응답헤더에 토큰 추가
JwtToken token = jwtUtil.generateToken(String.valueOf(user.getId()));
response.addHeader("Access-Token", token.getAccessToken());
response.addHeader("Refresh-Token", token.getRefreshToken());
if(user.isInfoSet()){
return ApiResponse.onSuccess(UserConverter.toUserDetailDTO(user));
}
return ApiResponse.of(SuccessStatus.NEED_USER_DETAIL, UserConverter.toLoginDTO(user));
user = userService.getUserByOauthId(oauthId);
}
else{
User user = UserConverter.toUser(userInfoResponseDTO);
Long id = userService.create(user);
// 응답헤더에 토큰 추가
JwtToken token = jwtUtil.generateToken(String.valueOf(id));
response.addHeader("Access-Token", token.getAccessToken());
response.addHeader("Refresh-Token", token.getRefreshToken());
return ApiResponse.of(SuccessStatus.NEED_USER_DETAIL, UserConverter.toLoginDTO(user));
user = UserConverter.toUser(userInfo);
user = userService.create(user);
}
// 응답헤더에 토큰 추가
JwtToken token = jwtUtil.generateToken(String.valueOf(user.getId()));
response.addHeader("Access-Token", token.getAccessToken());
response.addHeader("Refresh-Token", token.getRefreshToken());
return ApiResponse.onSuccess(UserConverter.toLoginDTO(user));
}

@ResponseBody
@PostMapping("/oauth2/apple/token/validate")
public ApiResponse<?> validateAppleToken(HttpServletResponse response, @RequestBody LoginRequestDTO.AppleTokenValidateDTO requestDTO) {
// 검증하기
appleLoginService.validate(requestDTO.getIdentityToken());
// 검증 성공 시 리프레시 토큰 발급받아 저장(기한 무제한, 회원탈퇴 시 필요)
TokenResponse tokenResponse = appleLoginService.getAccessTokenByCode(requestDTO.getAuthorizationCode());
// 유저 정보 조회 및 저장
String oauthId = requestDTO.getOauthId();
User user;
if( userService.isExistByOauthId(oauthId)){
user = userService.getUserByOauthId(oauthId);
}
else{
user = UserConverter.toUser(requestDTO);
user.setAppleRefreshToken(tokenResponse.getRefreshToken());
user = userService.create(user);
}
// 응답헤더에 토큰 추가
JwtToken token = jwtUtil.generateToken(String.valueOf(user.getId()));
response.addHeader("Access-Token", token.getAccessToken());
response.addHeader("Refresh-Token", token.getRefreshToken());
return ApiResponse.onSuccess(UserConverter.toLoginDTO(user));
}


@GetMapping("/token/validate")
public ApiResponse<String> validateServerToken(@RequestParam(name = "code") String code){
TokenResponse tokenResponse = kakaoLoginService.getAccessTokenByCode(code);
return ApiResponse.onSuccess(null);
}
/*
테스트용 API
*/
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/com/onnoff/onnoff/auth/dto/LoginRequestDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.onnoff.onnoff.auth.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;

public class LoginRequestDTO {
@Getter
public static class AppleTokenValidateDTO{
@JsonProperty("user")
private String oauthId;
@JsonProperty("full_name")
private String fullName;
private String email;
@JsonProperty("identity_token")
private String identityToken;
@JsonProperty("authorization_code")
private String authorizationCode;
}
@Getter
public static class KakaoTokenValidateDTO{
@JsonProperty("identity_token")
private String identityToken;
@JsonProperty("access_token")
private String accessToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.onnoff.onnoff.apiPayload.code.status.ErrorStatus;
import com.onnoff.onnoff.apiPayload.exception.GeneralException;
import com.onnoff.onnoff.auth.feignClient.dto.ErrorResponseDTO;
import com.onnoff.onnoff.auth.feignClient.dto.kakao.ErrorResponseDTO;
import feign.Response;
import feign.codec.ErrorDecoder;
import lombok.extern.slf4j.Slf4j;
Expand Down
Loading

0 comments on commit dac111e

Please sign in to comment.