Skip to content
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: application-ci.yml 주입
run: |
mkdir -p src/main/resources
echo "${{ secrets.APPLICATION_YML_CI }}" | base64 --decode > src/main/resources/application-ci.yml
echo "${{ secrets.APPLICATION_YML_CI }}" > src/main/resources/application-ci.yml

- name: 권한 세팅
run: chmod +x ./gradlew
Expand Down
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ dependencies {
implementation 'com.github.ben-manes.caffeine:caffeine'

implementation 'org.springframework.retry:spring-retry:2.0.12'

//jwt
implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.oronaminc.join;

import com.oronaminc.join.member.token.JwtConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@EnableJpaAuditing
@SpringBootApplication
@EnableConfigurationProperties(JwtConfiguration.class)
public class Web57OronaminCBeApplication {

public static void main(String[] args) {
Expand Down
139 changes: 66 additions & 73 deletions src/main/java/com/oronaminc/join/global/dev/DevController.java
Original file line number Diff line number Diff line change
@@ -1,73 +1,66 @@
package com.oronaminc.join.global.dev;

import java.util.List;

import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.oronaminc.join.member.dao.MemberRepository;
import com.oronaminc.join.member.domain.Member;
import com.oronaminc.join.member.domain.MemberType;
import com.oronaminc.join.member.security.MemberDetails;

import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;

@Profile("local")
@RestController
@Tag(name = "개발용 API")
@RequestMapping("/dev")
@RequiredArgsConstructor
public class DevController {
private final MemberRepository memberRepository;

@PostMapping("/join")
@ResponseStatus(HttpStatus.OK)
public Member devJoin(@RequestBody DevJoinRequest devJoinRequest) {
return memberRepository.save(
Member.builder()
.email(devJoinRequest.email())
.nickname(devJoinRequest.nickname())
.memberType(MemberType.MEMBER)
.build()
);
}

@PostMapping("/login")
@ResponseStatus(HttpStatus.OK)
public void devLogin(@RequestBody DevLoginRequest devLoginRequest, HttpServletRequest request) {
Member member = memberRepository.findById(devLoginRequest.memberId())
.orElseThrow(() -> new IllegalArgumentException("해당 ID의 사용자가 존재하지 않습니다."));

MemberDetails memberDetails = MemberDetails.builder()
.id(member.getId())
.name(member.getEmail())
.nickname(member.getNickname())
.role(member.getMemberType())
.build();

Authentication authentication = new UsernamePasswordAuthenticationToken(
memberDetails,
null,
List.of(new SimpleGrantedAuthority(memberDetails.getRole()))
);

SecurityContextHolder.getContext().setAuthentication(authentication);

HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
}


}
//package com.oronaminc.join.global.dev;
//
//import com.oronaminc.join.member.dao.MemberRepository;
//import com.oronaminc.join.member.domain.Member;
//import com.oronaminc.join.member.domain.MemberType;
//import com.oronaminc.join.member.security.MemberDetails;
//import io.swagger.v3.oas.annotations.tags.Tag;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpSession;
//import lombok.RequiredArgsConstructor;
//import org.springframework.http.HttpStatus;
//import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
//import org.springframework.security.core.Authentication;
//import org.springframework.security.core.authority.SimpleGrantedAuthority;
//import org.springframework.security.core.context.SecurityContextHolder;
//import org.springframework.web.bind.annotation.*;
//
//import java.util.List;
//
/// /@Profile("local")
//@RestController
//@Tag(name = "개발용 API")
//@RequestMapping("/dev")
//@RequiredArgsConstructor
//public class DevController {
// private final MemberRepository memberRepository;
//
// @PostMapping("/join")
// @ResponseStatus(HttpStatus.OK)
// public Member devJoin(@RequestBody DevJoinRequest devJoinRequest) {
// return memberRepository.save(
// Member.builder()
// .email(devJoinRequest.email())
// .nickname(devJoinRequest.nickname())
// .memberType(MemberType.MEMBER)
// .build()
// );
// }
//
// @PostMapping("/login")
// @ResponseStatus(HttpStatus.OK)
// public void devLogin(@RequestBody DevLoginRequest devLoginRequest, HttpServletRequest request) {
// Member member = memberRepository.findById(devLoginRequest.memberId())
// .orElseThrow(() -> new IllegalArgumentException("해당 ID의 사용자가 존재하지 않습니다."));
//
// MemberDetails memberDetails = MemberDetails.builder()
// .id(member.getId())
// .name(member.getEmail())
// .nickname(member.getNickname())
// .role(member.getMemberType())
// .build();
//
// Authentication authentication = new UsernamePasswordAuthenticationToken(
// memberDetails,
// null,
// List.of(new SimpleGrantedAuthority(memberDetails.getRole()))
// );
//
// SecurityContextHolder.getContext().setAuthentication(authentication);
//
// HttpSession session = request.getSession(true);
// session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
// }
//
//
//}
56 changes: 28 additions & 28 deletions src/main/java/com/oronaminc/join/global/dev/HealthController.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
package com.oronaminc.join.global.dev;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;

@RestController
@Tag(name = "헬스체크 API")
public class HealthController {

@Operation(summary = "애플리케이션 헬스체크")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/health")
public String health() {
return "Server is Healthy!";
}

@Operation(summary = "홈")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/")
public String home() {
return "It's Home!";
}
}
//package com.oronaminc.join.global.dev;
//
//import org.springframework.http.HttpStatus;
//import org.springframework.web.bind.annotation.GetMapping;
//import org.springframework.web.bind.annotation.ResponseStatus;
//import org.springframework.web.bind.annotation.RestController;
//
//import io.swagger.v3.oas.annotations.Operation;
//import io.swagger.v3.oas.annotations.tags.Tag;
//
//@RestController
//@Tag(name = "헬스체크 API")
//public class HealthController {
//
// @Operation(summary = "애플리케이션 헬스체크")
// @ResponseStatus(HttpStatus.OK)
// @GetMapping("/health")
// public String health() {
// return "Server is Healthy!";
// }
//
// @Operation(summary = "홈")
// @ResponseStatus(HttpStatus.OK)
// @GetMapping("/")
// public String home() {
// return "It's Home!";
// }
//}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,31 +1,11 @@
package com.oronaminc.join.member.security;


import static com.oronaminc.join.member.util.MemberMapper.toSessionInfoResponse;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.oronaminc.join.member.dto.GuestLoginRequest;
import com.oronaminc.join.member.dto.GuestLoginResponse;
import com.oronaminc.join.member.dto.KakaoLoginRequest;
import com.oronaminc.join.member.dto.KakaoLoginResponse;
import com.oronaminc.join.member.dto.SessionInfoResponse;

import com.oronaminc.join.member.token.AuthTokenResponse;
import com.oronaminc.join.member.token.JwtUtils;
import com.oronaminc.join.member.token.LoginResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -34,82 +14,63 @@
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/auth")
@Tag(name = "Auth", description = "로그인 관련 API")
@RequiredArgsConstructor
public class AuthController {

private final AuthService authService;

@Operation(
summary = "카카오 로그인",
description = "redirect url 에 포함된 파라미터의 code와 state를 입력해주세요. 이후 모든 요청에 세션 인증이 적용됩니다."
summary = "카카오 로그인"
)
@PostMapping("/kakao")
@ResponseStatus(HttpStatus.OK)
public SessionInfoResponse kakaoLogin(
@RequestBody KakaoLoginRequest kakaoLoginRequest,
HttpServletRequest request
public Map<String, AuthTokenResponse> kakaoLogin(
@RequestBody KakaoLoginRequest kakaoLoginRequest,
HttpServletResponse response
) {
MemberDetails memberDetails = authService.kakaoLogin(kakaoLoginRequest.code());
LoginResponse loginResponse = authService.kakaoLogin(kakaoLoginRequest.code());

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
memberDetails, null, List.of(new SimpleGrantedAuthority(memberDetails.getRole()))
);
String refreshToken = loginResponse.refreshToken();

SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
JwtUtils.addRefreshTokenCookie(response, refreshToken,
loginResponse.refreshTokenExpiresIn());

SecurityContextHolder.getContext().setAuthentication(authentication);

request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);

return toSessionInfoResponse(memberDetails);
return Map.of("token", loginResponse.authTokenResponse());
}

@Operation(
summary = "비회원 로그인",
description = "닉네임을 입력하면 비회원 세션이 생성되고 인증이 설정됩니다. 이후 모든 요청에 세션 인증이 적용됩니다.",
responses = {
@ApiResponse(responseCode = "201", description = "비회원 로그인 성공"),
@ApiResponse(responseCode = "400", description = "닉네임 누락 또는 유효성 검증 실패")
}
)
@PostMapping("/guest")
@ResponseStatus(HttpStatus.CREATED)
public SessionInfoResponse guestLogin(@RequestBody @Valid GuestLoginRequest guestLoginRequest, HttpServletRequest request) {
MemberDetails guest = authService.loadGuest(guestLoginRequest);

Authentication authentication = new UsernamePasswordAuthenticationToken(
guest, null, List.of(new SimpleGrantedAuthority(guest.getRole()))
);

SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
public Map<String, AuthTokenResponse> guestLogin(
@RequestBody @Valid GuestLoginRequest guestLoginRequest,
HttpServletResponse response) {
LoginResponse loginResponse = authService.loadGuest(guestLoginRequest);

request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);
String refreshToken = loginResponse.refreshToken();

return toSessionInfoResponse(guest);
}

@Operation(
summary = "현재 세션 사용자 정보 조회",
description = "로그인한 사용자의 세션 정보를 반환합니다. 로그인하지 않은 경우 403 또는 401이 발생합니다.",
responses = {
@ApiResponse(responseCode = "200", description = "세션 사용자 정보 조회 성공"),
@ApiResponse(responseCode = "401", description = "로그인되지 않은 사용자"),
@ApiResponse(responseCode = "403", description = "인증된 사용자 아님")
}
)
@GetMapping("/session")
@ResponseStatus(HttpStatus.OK)
public SessionInfoResponse getSessionInfo(@AuthenticationPrincipal MemberDetails memberDetails) {
JwtUtils.addRefreshTokenCookie(response, refreshToken,
loginResponse.refreshTokenExpiresIn());

return toSessionInfoResponse(memberDetails);
return Map.of("token", loginResponse.authTokenResponse());
}

@Operation(
Expand Down
Loading