Skip to content

Commit

Permalink
feat: 로그인 관련 기능 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Soyeon-Cha committed Nov 22, 2023
1 parent 92bc207 commit 89c6549
Show file tree
Hide file tree
Showing 14 changed files with 379 additions and 30 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ repositories {

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.security:spring-security-test'
}

tasks.named('test') {
Expand Down
51 changes: 51 additions & 0 deletions src/main/java/MARKETFUBY/Member/controller/MemberController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package MARKETFUBY.Member.controller;

import MARKETFUBY.Member.dto.MemberJoinRequestDto;
import MARKETFUBY.Member.dto.MemberLoginRequestDto;
import MARKETFUBY.Member.dto.MemberLoginResponseDto;
import MARKETFUBY.Member.dto.RefreshTokenRequestDto;
import MARKETFUBY.Member.service.MemberService;
import MARKETFUBY.Member.service.RefreshTokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/members")
public class MemberController {
private final MemberService memberService;
private final RefreshTokenService refreshTokenService;

// 회원가입
@PostMapping("/signup")
public ResponseEntity<String> join (@RequestBody MemberJoinRequestDto requestDto) {
return ResponseEntity.ok().body(memberService.join(requestDto.getFubyId(), requestDto.getPasswd(), requestDto.getName(), requestDto.getEmail(), requestDto.getPhone(), requestDto.getHome(), requestDto.getSex(), requestDto.getBirthday()));
}

// 로그인
@PostMapping("/login")
public MemberLoginResponseDto login (@RequestBody MemberLoginRequestDto requestDto) {
return memberService.login(requestDto.getFubyId(), requestDto.getPasswd());
}

// 로그아웃
@DeleteMapping("/logout")
public String logout(@RequestBody RefreshTokenRequestDto requestDto) {
refreshTokenService.deleteRefreshToken(requestDto.getRefreshToken());
return "로그아웃되었습니다.";
}

// 회원탈퇴
@DeleteMapping("/{memberId}")
public ResponseEntity<String> delete(@PathVariable Long memberId, Authentication authentication) {
return ResponseEntity.ok().body(memberService.delete(memberId, authentication));
}

// RefreshToken을 이용해 새로운 AccessToken을 발급받기
@PostMapping("/refreshtoken")
public MemberLoginResponseDto requestRefresh (@RequestBody RefreshTokenRequestDto refreshTokenDto) {
return memberService.requestRefresh(refreshTokenDto.getRefreshToken());
}
}
17 changes: 5 additions & 12 deletions src/main/java/MARKETFUBY/Member/domain/Member.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package MARKETFUBY.Member.domain;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

@Getter
@Setter
@Entity
@Getter
@Table(name="member")
@NoArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -35,13 +34,9 @@ public class Member {
private String birthday;
@Column
private String level;
@Column
private Boolean selectAgreement;
@Column
private UseAgreement useAgreement;

@Builder
public Member(String fubyId, String passwd, String name, String email, String phone, String home, Sex sex, String birthday, String level, boolean selectAgreement, UseAgreement useAgreement){
public Member(String fubyId, String passwd, String name, String email, String phone, String home, Sex sex, String birthday, String level) {
this.fubyId = fubyId;
this.passwd = passwd;
this.name = name;
Expand All @@ -51,7 +46,5 @@ public Member(String fubyId, String passwd, String name, String email, String ph
this.sex = sex;
this.birthday = birthday;
this.level = level;
this.selectAgreement = selectAgreement;
this. useAgreement = useAgreement;
}
}
}
24 changes: 24 additions & 0 deletions src/main/java/MARKETFUBY/Member/domain/RefreshToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package MARKETFUBY.Member.domain;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

@Entity
@Getter
@Setter
@NoArgsConstructor
public class RefreshToken {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long refreshTokenId;

@Column(nullable = false)
private Long memberId;

@Column(nullable = false)
private String value;
}

15 changes: 0 additions & 15 deletions src/main/java/MARKETFUBY/Member/domain/UseAgreement.java

This file was deleted.

17 changes: 17 additions & 0 deletions src/main/java/MARKETFUBY/Member/dto/MemberJoinRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package MARKETFUBY.Member.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class MemberJoinRequestDto {
private String fubyId;
private String passwd;
private String name;
private String email;
private String phone;
private String home;
private String sex;
private String birthday;
}
12 changes: 12 additions & 0 deletions src/main/java/MARKETFUBY/Member/dto/MemberLoginRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package MARKETFUBY.Member.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class MemberLoginRequestDto {
private String fubyId;
private String passwd;
}

23 changes: 23 additions & 0 deletions src/main/java/MARKETFUBY/Member/dto/MemberLoginResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package MARKETFUBY.Member.dto;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberLoginResponseDto {
private Long memberId;
private String username;
private String accessToken;
private String refreshToken;

@Builder
public MemberLoginResponseDto(Long memberId, String fubyId, String accessToken, String refreshToken) {
this.memberId = memberId;
this.username = fubyId;
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}
11 changes: 11 additions & 0 deletions src/main/java/MARKETFUBY/Member/dto/RefreshTokenRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package MARKETFUBY.Member.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class RefreshTokenRequestDto {
private String refreshToken;
}

Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package MARKETFUBY.Member.repository;

import MARKETFUBY.Member.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import MARKETFUBY.Member.domain.Member;
import java.util.Optional;

@Repository
public interface MemberRepository extends JpaRepository<Member, Long> {
// fubyId 중복 검사
Boolean existsByFubyId(String fubyId);
Optional<Member> findByFubyId(String fubyId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package MARKETFUBY.Member.repository;

import MARKETFUBY.Member.domain.RefreshToken;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByValue(String value);
}
133 changes: 133 additions & 0 deletions src/main/java/MARKETFUBY/Member/service/MemberService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package MARKETFUBY.Member.service;

import MARKETFUBY.Member.domain.Member;
import MARKETFUBY.Member.domain.RefreshToken;
import MARKETFUBY.Member.domain.Sex;
import MARKETFUBY.Member.dto.MemberLoginResponseDto;
import MARKETFUBY.Member.repository.MemberRepository;
import MARKETFUBY.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityNotFoundException;

@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
private final BCryptPasswordEncoder encoder;
private final RefreshTokenService refreshTokenService;

// AccessToken 만료 시간을 1시간으로 설정
private Long AccessExpireTimeMs = 1000 * 60 * 60L;
// RefreshToken 만료 시간을 7일로 설정
private Long RefreshExpireTimeMs = 7 * 24 * 1000 * 60 * 60L;

// application-secret.yml 에서 키값 가져오기
@Value("${spring.jwt.secret-key}")
private String accessKey;
@Value("${spring.jwt.refresh-key}")
private String refreshKey;

// 회원가입
public String join(String fubyId, String passwd, String name, String email, String phone, String home, String sex, String birthday) {
// fubyId 중복 체크
if(existsByFubyId(fubyId)) throw new RuntimeException(fubyId + "은 이미 존재하는 아이디입니다!");

// 중복되지 않는다면 저장
memberRepository.save(
Member.builder()
.fubyId(fubyId)
.passwd(encoder.encode(passwd))
.name(name)
.email(email)
.phone(phone)
.home(home)
.sex(Sex.valueOf(sex))
.birthday(birthday)
.level("프렌즈")
.build()
);
return "성공적으로 회원가입되었습니다!";
}

@Transactional(readOnly = true)
public boolean existsByFubyId(String fubyId) {
return memberRepository.existsByFubyId(fubyId);
}

// 로그인
public MemberLoginResponseDto login(String fubyId, String passwd) {
// 존재하지 않는 fubyId로 로그인을 시도하는 경우
Member foundMember = findMemberByFubyId(fubyId);

// 존재하는 fubyId를 입력했지만 잘못된 비밀번호를 입력하는 경우
if(!encoder.matches(passwd, foundMember.getPasswd())) throw new RuntimeException("잘못된 비밀번호입니다.");

// 로그인 성공 -> 토큰 생성
String accessToken = JwtUtil.createAccessToken(foundMember.getFubyId(), accessKey, AccessExpireTimeMs);
String refreshToken = JwtUtil.createRefreshToken(foundMember.getFubyId(), refreshKey, RefreshExpireTimeMs);

// RefreshToken을 DB에 저장
RefreshToken refreshTokenEntity = new RefreshToken();
refreshTokenEntity.setValue(refreshToken);
refreshTokenEntity.setMemberId(foundMember.getMemberId());
refreshTokenService.addRefreshToken(refreshTokenEntity);

return MemberLoginResponseDto.builder()
.memberId(foundMember.getMemberId())
.fubyId(foundMember.getFubyId())
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}

// 회원탈퇴
public String delete(Long memberId, Authentication authentication) {
Member member = findMemberById(memberId);
memberRepository.delete(member);
return "성공적으로 탈퇴되었습니다!";
}

// fubyId로 회원 찾기
@Transactional(readOnly = true)
public Member findMemberByFubyId(String fubyId) {
return memberRepository.findByFubyId(fubyId)
.orElseThrow(() -> new EntityNotFoundException(fubyId + "은 존재하지 않는 아이디입니다."));
}

// memberId로 Member 정보 찾기
@Transactional(readOnly = true)
public Member findMemberById(Long id) {
return memberRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Member ID가 " + id + "인 회원이 존재하지 않습니다."));
}

// 새로운 AccessToken 발급받기
public MemberLoginResponseDto requestRefresh(String refreshToken) {
// 해당 RefreshToken이 유효한지 DB에서 탐색
RefreshToken foundRefreshToken = refreshTokenService.findRefreshToken(refreshToken);
// RefreshToken에 들어있는 username 값 가져오기
Claims claims = JwtUtil.parseRefreshToken(foundRefreshToken.getValue(), refreshKey);
String fubyId = claims.get("username").toString();
System.out.println("Username found in RefreshToken: " + fubyId);
// 가져온 fubyId에 해당하는 회원이 존재하는지 확인
Member member = findMemberByFubyId(fubyId);

// 새 AccessKey 생성
String accessToken = JwtUtil.createAccessToken(member.getFubyId(), accessKey, AccessExpireTimeMs);
// 새 AccessKey와 기존 RefreshKey를 DTO에 담아 리턴
return MemberLoginResponseDto
.builder()
.memberId(member.getMemberId())
.fubyId(member.getFubyId())
.accessToken(accessToken)
.refreshToken(refreshToken)
.build();
}
}
Loading

0 comments on commit 89c6549

Please sign in to comment.