Skip to content
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

Feat #20 카카오 로그인 로직 변경 #21

Merged
merged 23 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
288dd3d
feat: 임시유저 상태 추가
koreaioi Jan 8, 2025
7815143
feat: 임시유저 인가 경로 추가 설정
koreaioi Jan 8, 2025
93aead6
feat: 임시유저 생성 시 tmpEmail 주입 (UserDetails 때문)
koreaioi Jan 8, 2025
36c7806
fix: 카카오 로그인 구현 방식 변경 (프론트에서 인가코드 전달)
koreaioi Jan 8, 2025
ce5ede4
feat: JwtProvider 임시 토큰 생성 추가
koreaioi Jan 8, 2025
197fcf5
feat: JwtService 임시 토큰 응답 메서드
koreaioi Jan 8, 2025
5c1a8a1
feat: KakaoAuthCode Dto 추가
koreaioi Jan 8, 2025
0e5d6a6
feat: application-local, dev 임시토큰 만료기간 환경변수 추가
koreaioi Jan 8, 2025
b6b2ea3
feat: 임시 유저 저장
koreaioi Jan 8, 2025
7dc49bf
feat: 회원가입 절차 중 오류 발생시 임시유저는 생겨나므로 조건에 TEMPORARY 추가
koreaioi Jan 8, 2025
5816cdb
feat: 카카오 로그인 최종 응답 - userId와 헤더에 Token 반환
koreaioi Jan 8, 2025
983b10c
feat: 회원가입 진행이 중단된 유저 엣지 케이스 로직 추가
koreaioi Jan 8, 2025
2038155
feat: TmpMemberDto 추가
koreaioi Jan 8, 2025
a3d2e44
feat: @Valid 추가
koreaioi Jan 8, 2025
c168c7a
feat: Members 경로 삭제
koreaioi Jan 8, 2025
3818a46
remove: 출력문 삭제
koreaioi Jan 8, 2025
9deb9f9
fix: 임시유저 권한 경로 삭제
koreaioi Jan 8, 2025
0fdc43d
fix: 토큰 email Claims에는 더미 데이터 저장
koreaioi Jan 8, 2025
dbaa479
fix: 임시 유저 email DB에는 null 저장
koreaioi Jan 8, 2025
863cd2a
feat: UserStatus - ACTIVE, INACTIVE 추가 및 반영
koreaioi Jan 8, 2025
d6f4cad
remove: 사용하지 않는 상수 제거
koreaioi Jan 8, 2025
feca89d
feat: UserStatus 추가
koreaioi Jan 8, 2025
650522c
remove: 임시유저 권한 경로 삭제
koreaioi Jan 8, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import com.gachtaxi.global.common.response.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import static com.gachtaxi.domain.members.controller.ResponseMessage.*;
import static com.gachtaxi.global.auth.kakao.dto.KaKaoDTO.KakaoAuthCode;
import static com.gachtaxi.global.auth.kakao.dto.KaKaoDTO.OauthKakaoResponse;

@RequestMapping("/auth")
Expand All @@ -22,9 +24,9 @@ public class AuthController {
private final AuthService authService;
private final JwtService jwtService;

@GetMapping("/login/kakao")
public ApiResponse<OauthKakaoResponse> kakaoLogin(@RequestParam("code") String authcode, HttpServletResponse response) {
OauthKakaoResponse res = authService.kakaoLogin(authcode, response);
@PostMapping("/login/kakao")
public ApiResponse<OauthKakaoResponse> kakaoLogin(@RequestBody @Valid KakaoAuthCode kakaoAuthCode, HttpServletResponse response) {
OauthKakaoResponse res = authService.kakaoLogin(kakaoAuthCode.authCode(), response);
ResponseMessage OAUTH_STATUS = (res.status() == OauthLoginStatus.LOGIN)
? LOGIN_SUCCESS
: UN_REGISTER;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.gachtaxi.domain.members.dto.request;

import com.gachtaxi.domain.members.entity.Members;
import com.gachtaxi.domain.members.entity.enums.Role;
import lombok.Builder;

@Builder
public record TmpMemberDto(
Long userId,
String email,
Role role
) {
public static TmpMemberDto of(Members tmpMember) {
return TmpMemberDto.builder()
.userId(tmpMember.getId())
.email(tmpMember.getEmail())
.role(tmpMember.getRole())
.build();
}
}
29 changes: 21 additions & 8 deletions src/main/java/com/gachtaxi/domain/members/entity/Members.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.gachtaxi.domain.members.dto.request.UserSignUpRequestDto;
import com.gachtaxi.domain.members.entity.enums.Gender;
import com.gachtaxi.domain.members.entity.enums.Role;
import com.gachtaxi.domain.members.entity.enums.UserStatus;
import com.gachtaxi.global.common.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
Expand All @@ -18,19 +19,19 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Members extends BaseEntity {

@Column(name = "email", nullable = false, unique = true)
@Column(name = "email", unique = true)
private String email;

@Column(name = "profile_picture")
private String profilePicture;

@Column(name = "nickname", nullable = false)
@Column(name = "nickname")
private String nickname;

@Column(name = "real_name", nullable = false)
@Column(name = "real_name")
private String realName;

@Column(name = "student_number", nullable = false, unique = true)
@Column(name = "student_number", unique = true)
private Long studentNumber;

@Column(name = "phone_number", unique = true) // 피그마 참고, 일단 null 허용
Expand All @@ -48,23 +49,26 @@ public class Members extends BaseEntity {
@Enumerated(EnumType.STRING)
private Gender gender;

@Enumerated(EnumType.STRING)
private UserStatus status;

// 이용 약관 동의
@Column(name = "terms_agreement", nullable = false)
@Column(name = "terms_agreement")
@ColumnDefault("true")
private Boolean termsAgreement;

// 개인정보 수집 동의
@Column(name = "privacy_agreement", nullable = false)
@Column(name = "privacy_agreement")
@ColumnDefault("true")
private Boolean privacyAgreement;

// 광고성 정보 수신 동의
@Column(name = "marketing_agreement", nullable = false)
@Column(name = "marketing_agreement")
@ColumnDefault("false")
private Boolean marketingAgreement;

// 2차 인증 (전화번호)
@Column(name = "two_factor_authentication", nullable = false)
@Column(name = "two_factor_authentication")
@ColumnDefault("false")
private Boolean twoFactorAuthentication;

Expand All @@ -86,11 +90,20 @@ public static Members of(UserSignUpRequestDto dto){
.kakaoId(dto.kakaoId())
.googleId(dto.googleId())
.role(Role.MEMBER)
.status(UserStatus.ACTIVE)
.gender(dto.gender())
.termsAgreement(dto.termsAgreement())
.privacyAgreement(dto.privacyAgreement())
.marketingAgreement(dto.marketingAgreement())
.twoFactorAuthentication(dto.twoFactorAuthentication())
.build();
}

public static Members ofKakaoId(Long kakaoId){
return Members.builder()
.kakaoId(kakaoId)
.status(UserStatus.INACTIVE)
.role(Role.MEMBER)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.gachtaxi.domain.members.entity.enums;

public enum UserStatus {
ACTIVE, INACTIVE
}
17 changes: 15 additions & 2 deletions src/main/java/com/gachtaxi/domain/members/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.gachtaxi.domain.members.service;

import com.gachtaxi.domain.members.dto.request.TmpMemberDto;
import com.gachtaxi.domain.members.entity.Members;
import com.gachtaxi.global.auth.jwt.service.JwtService;
import com.gachtaxi.global.auth.kakao.util.KakaoUtil;
import com.gachtaxi.global.auth.mapper.OauthMapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;

import static com.gachtaxi.domain.members.entity.enums.UserStatus.INACTIVE;
import static com.gachtaxi.global.auth.kakao.dto.KaKaoDTO.*;


Expand All @@ -34,11 +37,21 @@ public OauthKakaoResponse kakaoLogin(String authCode, HttpServletResponse respon
Optional<Members> optionalMember = memberService.findByKakaoId(kakaoId);

if(optionalMember.isEmpty()) {
return oauthMapper.toKakaoUnRegisterResponse(userInfo);
TmpMemberDto tmpDto = memberService.saveTmpMember(kakaoId);

jwtService.responseTmpAccessToken(tmpDto, response);
return oauthMapper.toKakaoUnRegisterResponse(tmpDto.userId());
}

// 회원 가입 진행 중 중단된 유저 또한 다시 임시 토큰을 재발급해준다.
if(optionalMember.get().getStatus() == INACTIVE){
TmpMemberDto tmpDto = TmpMemberDto.of(optionalMember.get());
jwtService.responseTmpAccessToken(tmpDto, response);
return oauthMapper.toKakaoUnRegisterResponse(tmpDto.userId());
}

Members member = optionalMember.get();
jwtService.responseJwtToken(member.getId(), member.getEmail(), member.getRole(), response);
return oauthMapper.toKakaoLoginResponse(userInfo, member.getId());
return oauthMapper.toKakaoLoginResponse(member.getId());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gachtaxi.domain.members.service;

import com.gachtaxi.domain.members.dto.request.TmpMemberDto;
import com.gachtaxi.domain.members.dto.request.UserSignUpRequestDto;
import com.gachtaxi.domain.members.entity.Members;
import com.gachtaxi.domain.members.exception.DuplicatedStudentNumberException;
Expand All @@ -19,6 +20,7 @@ public class MemberService {
private final JwtService jwtService;
private final MemberRepository memberRepository;

//TODO 최종 회원가입 절차에서 사용
@Transactional
public void saveMember(UserSignUpRequestDto dto, HttpServletResponse response) {
checkDuplicatedStudentNumber(dto);
Expand All @@ -27,6 +29,14 @@ public void saveMember(UserSignUpRequestDto dto, HttpServletResponse response) {
jwtService.responseJwtToken(newMember.getId(), newMember.getEmail(), newMember.getRole(), response);
}

// 임시 유저 저장
@Transactional
public TmpMemberDto saveTmpMember(Long kakaoId){
Members tmpMember = Members.ofKakaoId(kakaoId);
memberRepository.save(tmpMember);
return TmpMemberDto.of(tmpMember);
}

public Optional<Members> findByKakaoId(Long kakaoId) {
return memberRepository.findByKakaoId(kakaoId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gachtaxi.global.auth.jwt.service;

import com.gachtaxi.domain.members.dto.request.TmpMemberDto;
import com.gachtaxi.domain.members.entity.enums.Role;
import com.gachtaxi.global.auth.jwt.dto.JwtTokenDto;
import com.gachtaxi.global.auth.jwt.exception.CookieNotFoundException;
Expand Down Expand Up @@ -36,6 +37,11 @@ public void responseJwtToken(Long userId, String email, Role role, HttpServletRe
setCookie(jwtToken.refreshToken(), response);
}

public void responseTmpAccessToken(TmpMemberDto tmpMemberDto, HttpServletResponse response) {
String tmpAccessToken = jwtProvider.generateTmpAccessToken(tmpMemberDto.userId(), tmpMemberDto.role().name());
setHeader(tmpAccessToken, response);
}

public JwtTokenDto reissueJwtToken(HttpServletRequest request) {
String refreshToken = extractRefreshToken(request);
if(jwtExtractor.isExpired(refreshToken)){
Expand Down
21 changes: 19 additions & 2 deletions src/main/java/com/gachtaxi/global/auth/jwt/util/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public class JwtProvider {
private static final String ID_CLAIM = "id";
private static final String EMAIL_CLAIM = "email";
private static final String ROLE_CLAIM = "role";
private static final String ROLE_PREFIX = "ROLE_";
private static final String DUMMY_EMAIL = "dummy_email";
private final Key key;

public JwtProvider(@Value("${gachtaxi.auth.jwt.key}") String secretKey) {
Expand All @@ -27,26 +29,41 @@ public JwtProvider(@Value("${gachtaxi.auth.jwt.key}") String secretKey) {
@Value("${gachtaxi.auth.jwt.accessTokenExpiration}")
private Long accessTokenExpiration;

@Value("${gachtaxi.auth.jwt.tmpAccessTokenExpiration}")
private Long tmpAccessTokenExpiration;

@Value("${gachtaxi.auth.jwt.refreshTokenExpiration}")
private Long refreshTokenExpiration;

public String generateAccessToken(Long id, String email, String role) {
return Jwts.builder()
.claim(ID_CLAIM, id)
.claim(EMAIL_CLAIM, email)
.claim(ROLE_CLAIM, role)
.claim(ROLE_CLAIM, ROLE_PREFIX+role)
.setSubject(ACCESS_TOKEN_SUBJECT) // 사용자 정보(고유 식별자)
.setIssuedAt(new Date()) // 발행 시간
.setExpiration(new Date(System.currentTimeMillis() + accessTokenExpiration)) // 만료 시간
.signWith(key, SignatureAlgorithm.HS256) // 서명 알고리즘
.compact(); // 최종 문자열 생성
}

public String generateTmpAccessToken(Long id, String role) {
return Jwts.builder()
.claim(ID_CLAIM, id)
.claim(EMAIL_CLAIM, DUMMY_EMAIL)
.claim(ROLE_CLAIM, ROLE_PREFIX+role)
.setSubject(ACCESS_TOKEN_SUBJECT) // 사용자 정보(고유 식별자)
.setIssuedAt(new Date()) // 발행 시간
.setExpiration(new Date(System.currentTimeMillis() + tmpAccessTokenExpiration)) // 만료 시간
.signWith(key, SignatureAlgorithm.HS256) // 서명 알고리즘
.compact(); // 최종 문자열 생성
}

public String generateRefreshToken(Long id, String email, String role) {
return Jwts.builder()
.claim(ID_CLAIM, id)
.claim(EMAIL_CLAIM, email)
.claim(ROLE_CLAIM, role)
.claim(ROLE_CLAIM, ROLE_PREFIX+role)
.setSubject(REFRESH_TOKEN_SUBJECT) // 사용자 정보(고유 식별자)
.setIssuedAt(new Date()) // 발행 시간
.setExpiration(new Date(System.currentTimeMillis() + refreshTokenExpiration)) // 만료 시간
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.gachtaxi.global.auth.kakao.dto;

import com.gachtaxi.global.auth.enums.OauthLoginStatus;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;

public class KaKaoDTO {

public record KakaoAuthCode(
@NotBlank String authCode
){}

public record KakaoAccessToken(
String access_token,
String token_type,
Expand Down Expand Up @@ -34,7 +39,6 @@ public record Profile(
@Builder
public record OauthKakaoResponse(
Long userId,
Long kakaoId,
OauthLoginStatus status
){}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,17 @@
@Component
public class OauthMapper {

public OauthKakaoResponse toKakaoUnRegisterResponse(KakaoUserInfoResponse userInfo) {
public OauthKakaoResponse toKakaoUnRegisterResponse(Long userId) {
return OauthKakaoResponse.builder()
.kakaoId(userInfo.id())
.userId(userId)
.status(UN_REGISTER)
.build();
}

// jwt 토큰 추가 할 것.
public OauthKakaoResponse toKakaoLoginResponse(KakaoUserInfoResponse userInfo, Long userId) {
public OauthKakaoResponse toKakaoLoginResponse(Long userId) {
return OauthKakaoResponse.builder()
.userId(userId)
.kakaoId(userInfo.id())
.status(LOGIN)
.build();
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/gachtaxi/global/config/PermitUrlConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ public String[] getPublicUrl(){
};
}

public String[] getTmpMemberUrl(){
return new String[]{
"/api/tmp-members/**"
};
}

public String[] getMemberUrl(){
return new String[]{

Expand Down
7 changes: 4 additions & 3 deletions src/main/java/com/gachtaxi/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.gachtaxi.global.config;


import com.gachtaxi.domain.members.entity.enums.Role;
import com.gachtaxi.global.auth.jwt.authentication.CustomAccessDeniedHandler;
import com.gachtaxi.global.auth.jwt.authentication.CustomAuthenticationEntryPoint;
import com.gachtaxi.global.auth.jwt.filter.JwtAuthenticationFilter;
Expand All @@ -24,6 +23,8 @@
import java.util.Arrays;
import java.util.List;

import static com.gachtaxi.domain.members.entity.enums.Role.*;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
Expand All @@ -46,8 +47,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti

http.authorizeHttpRequests((auth) -> auth
.requestMatchers(permitUrlConfig.getPublicUrl()).permitAll()
.requestMatchers(permitUrlConfig.getMemberUrl()).hasRole(Role.MEMBER.name())
.requestMatchers(permitUrlConfig.getAdminUrl()).hasRole(Role.ADMIN.name())
.requestMatchers(permitUrlConfig.getMemberUrl()).hasAnyRole(MEMBER.name(), ADMIN.name())
.requestMatchers(permitUrlConfig.getAdminUrl()).hasRole(ADMIN.name())
.anyRequest().authenticated());

http.exceptionHandling(e -> e
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ gachtaxi:
key: ${JWT_SECRET_KEY}
accessTokenExpiration: ${JWT_ACCESS_TOKEN_EXPIRATION}
refreshTokenExpiration: ${JWT_REFRESH_TOKEN_EXPIRATION}
tmpAccessTokenExpiration: ${JWT_TMP_ACCESS_TOKEN_EXPIRATION}
cookieMaxAge: ${JWT_COOKIE_MAX_AGE}
secureOption: ${COOKIE_SECURE_OPTION}
cookiePathOption: ${COOKIE_PATH_OPTION}
1 change: 1 addition & 0 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ gachtaxi:
key: ${JWT_SECRET_KEY}
accessTokenExpiration: ${JWT_ACCESS_TOKEN_EXPIRATION}
refreshTokenExpiration: ${JWT_REFRESH_TOKEN_EXPIRATION}
tmpAccessTokenExpiration: ${JWT_TMP_ACCESS_TOKEN_EXPIRATION}
cookieMaxAge: ${JWT_COOKIE_MAX_AGE}
secureOption: ${COOKIE_SECURE_OPTION}
cookiePathOption: ${COOKIE_PATH_OPTION}
Loading