Skip to content

Commit 118c9a3

Browse files
committed
Feat : 스프링 시큐리티 session & JWT 방식
1 parent d6dbc2d commit 118c9a3

File tree

20 files changed

+541
-11
lines changed

20 files changed

+541
-11
lines changed

build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ dependencies {
3434
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
3535
testImplementation 'com.h2database:h2'
3636

37+
implementation 'org.springframework.boot:spring-boot-starter-security'
38+
testImplementation 'org.springframework.security:spring-security-test'
39+
40+
// Jwt
41+
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
42+
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
43+
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
44+
implementation 'org.springframework.boot:spring-boot-configuration-processor'
45+
3746
// QueryDSL : OpenFeign
3847
implementation "io.github.openfeign.querydsl:querydsl-jpa:7.0"
3948
implementation "io.github.openfeign.querydsl:querydsl-core:7.0"
Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
package com.example.umc9th.domain.member.controller;
22

3+
34
import com.example.umc9th.domain.member.dto.req.MemberReqDTO;
45
import com.example.umc9th.domain.member.dto.res.MemberResDTO;
56
import com.example.umc9th.domain.member.exception.code.MemberSuccessCode;
67
import com.example.umc9th.domain.member.service.command.MemberCommandService;
8+
import com.example.umc9th.domain.member.service.query.MemberQueryService;
79
import com.example.umc9th.global.apiPayload.ApiResponse;
10+
import jakarta.servlet.http.HttpServletRequest;
11+
import jakarta.servlet.http.HttpServletResponse;
12+
import jakarta.servlet.http.HttpSession;
813
import jakarta.validation.Valid;
14+
import org.springframework.http.HttpHeaders;
15+
import org.springframework.http.ResponseCookie;
16+
import org.springframework.security.core.context.SecurityContextHolder;
17+
import org.springframework.web.bind.annotation.RequestBody;
918
import lombok.RequiredArgsConstructor;
1019
import org.springframework.web.bind.annotation.PostMapping;
11-
import org.springframework.web.bind.annotation.RequestBody;
1220
import org.springframework.web.bind.annotation.RestController;
1321

1422
@RestController
1523
@RequiredArgsConstructor
1624
public class MemberController {
1725

1826
private final MemberCommandService memberCommandService;
27+
private final MemberQueryService memberQueryService;
1928

2029
// 회원가입
2130
@PostMapping("/sign-up")
@@ -24,4 +33,28 @@ public ApiResponse<MemberResDTO.JoinDTO> signUp(
2433
){
2534
return ApiResponse.onSuccess(MemberSuccessCode.FOUND, memberCommandService.signup(dto));
2635
}
36+
37+
// 로그인
38+
@PostMapping("/login")
39+
public ApiResponse<MemberResDTO.LoginDTO> login(
40+
@RequestBody @Valid MemberReqDTO.LoginDTO dto
41+
){
42+
return ApiResponse.onSuccess(MemberSuccessCode.FOUND, memberQueryService.login(dto));
43+
}
44+
45+
@PostMapping("/logout")
46+
public ApiResponse<String> logout(HttpServletRequest request, HttpServletResponse response) {
47+
HttpSession session = request.getSession(false);
48+
if (session != null) session.invalidate();
49+
50+
SecurityContextHolder.clearContext();
51+
52+
ResponseCookie cookie = ResponseCookie.from("JSESSIONID", "")
53+
.path("/")
54+
.maxAge(0)
55+
.build();
56+
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
57+
58+
return ApiResponse.onSuccess(MemberSuccessCode.FOUND, "로그아웃 완료");
59+
}
2760
}

src/main/java/com/example/umc9th/domain/member/converter/MemberConverter.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.example.umc9th.domain.member.dto.req.MemberReqDTO;
44
import com.example.umc9th.domain.member.dto.res.MemberResDTO;
55
import com.example.umc9th.domain.member.entity.Member;
6+
import com.example.umc9th.global.auth.enums.Role;
67

78
public class MemberConverter {
89

@@ -18,14 +19,30 @@ public static MemberResDTO.JoinDTO toJoinDTO(
1819

1920
// DTO -> Entity
2021
public static Member toMember(
21-
MemberReqDTO.JoinDTO dto
22+
MemberReqDTO.JoinDTO dto,
23+
String password,
24+
Role role
2225
){
2326
return Member.builder()
2427
.name(dto.name())
28+
.email(dto.email()) // 추가된 코드
29+
.password(password) // 추가된 코드
30+
.role(role) // 추가된 코드
2531
.birth(dto.birth())
2632
.address(dto.address())
33+
.phoneNum(dto.phoneNum())
2734
.detailAddress(dto.specAddress())
2835
.gender(dto.gender())
2936
.build();
3037
}
38+
39+
40+
// Entity -> DTO (로그인 응답) 추가
41+
public static MemberResDTO.LoginDTO toLoginDTO(Member member, String accessToken) {
42+
return MemberResDTO.LoginDTO.builder()
43+
.memberId(member.getId())
44+
.accessToken(accessToken)
45+
.build();
46+
}
47+
3148
}

src/main/java/com/example/umc9th/domain/member/dto/req/MemberReqDTO.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22

33
import com.example.umc9th.domain.member.enums.Gender;
44
import com.example.umc9th.global.annotation.ExistFoods;
5+
import jakarta.validation.constraints.Email;
56
import jakarta.validation.constraints.NotBlank;
67
import jakarta.validation.constraints.NotNull;
78

89
import java.time.LocalDate;
910
import java.util.List;
1011

1112
public class MemberReqDTO {
13+
14+
// 회원가입
1215
public record JoinDTO(
1316
@NotBlank
1417
String name,
18+
@Email
19+
String email, // 추가된 속성
20+
@NotBlank
21+
String password, // 추가된 속성
1522
@NotNull
1623
Gender gender,
1724
@NotNull
@@ -20,7 +27,16 @@ public record JoinDTO(
2027
String address,
2128
@NotNull
2229
String specAddress,
30+
@NotBlank String phoneNum,
2331
@ExistFoods
2432
List<Long> preferCategory
2533
){}
34+
35+
// 로그인
36+
public record LoginDTO(
37+
@NotBlank
38+
String email,
39+
@NotBlank
40+
String password
41+
){}
2642
}

src/main/java/com/example/umc9th/domain/member/dto/res/MemberResDTO.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@
55
import java.time.LocalDateTime;
66

77
public class MemberResDTO {
8+
// 회원가입
89
@Builder
910
public record JoinDTO(
1011
Long memberId,
1112
LocalDateTime createAt
1213
){}
14+
15+
// 로그인
16+
@Builder
17+
public record LoginDTO(
18+
Long memberId,
19+
String accessToken
20+
){}
1321
}

src/main/java/com/example/umc9th/domain/member/entity/Member.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.example.umc9th.domain.member.enums.Gender;
77
import com.example.umc9th.domain.member.enums.PhoneVerificationStatus;
88
import com.example.umc9th.domain.member.enums.SocialType;
9+
import com.example.umc9th.global.auth.enums.Role;
910
import com.example.umc9th.global.entity.BaseEntity;
1011
import jakarta.persistence.*;
1112
import lombok.*;
@@ -54,9 +55,15 @@ public class Member extends BaseEntity {
5455
@Enumerated(EnumType.STRING)
5556
private SocialType socialType;
5657

57-
@Column(name = "email", length = 50)
58+
@Column(name = "email", unique = true, length = 50)
5859
private String email;
5960

61+
@Column(nullable = false)
62+
private String password;
63+
64+
@Enumerated(EnumType.STRING)
65+
private Role role;
66+
6067
@Column(name = "phone_verification_status", nullable = false)
6168
@Enumerated(EnumType.STRING)
6269
@Builder.Default

src/main/java/com/example/umc9th/domain/member/exception/code/MemberErrorCode.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ public enum MemberErrorCode implements BaseErrorCode {
1212
NOT_FOUND(HttpStatus.NOT_FOUND,
1313
"MEMBER404_1",
1414
"해당 사용자를 찾지 못했습니다."),
15-
;
15+
16+
INVALID(HttpStatus.BAD_REQUEST,
17+
"MEMBER400_1",
18+
"유효하지 않은 사용자 요청입니다.");
1619

1720
private final HttpStatus status;
1821
private final String code;

src/main/java/com/example/umc9th/domain/member/repository/MemberRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ public interface MemberRepository extends JpaRepository<Member,Long> {
1010
// 활성 상태인 회원 단건 조회
1111
Optional<Member> findByIdAndIsActiveTrue(Long id);
1212

13+
Optional<Member> findByEmail(String email);
14+
1315
}

src/main/java/com/example/umc9th/domain/member/service/command/MemberCommandServiceImpl.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
import com.example.umc9th.domain.member.repository.FoodRepository;
1212
import com.example.umc9th.domain.member.repository.MemberFoodRepository;
1313
import com.example.umc9th.domain.member.repository.MemberRepository;
14+
import com.example.umc9th.global.auth.enums.Role;
1415
import jakarta.transaction.Transactional;
1516
import lombok.RequiredArgsConstructor;
17+
import org.springframework.security.crypto.password.PasswordEncoder;
1618
import org.springframework.stereotype.Service;
1719

1820
import java.util.ArrayList;
@@ -27,19 +29,26 @@ public class MemberCommandServiceImpl implements MemberCommandService{
2729
private final MemberFoodRepository memberFoodRepository;
2830
private final FoodRepository foodRepository;
2931

32+
// Password Encoder
33+
private final PasswordEncoder passwordEncoder;
34+
3035
// 회원가입
3136
@Override
3237
@Transactional
3338
public MemberResDTO.JoinDTO signup(
3439
MemberReqDTO.JoinDTO dto
3540
){
41+
42+
// 솔트된 비밀번호 생성
43+
String salt = passwordEncoder.encode(dto.password());
44+
3645
// 사용자 생성
37-
Member member = MemberConverter.toMember(dto);
46+
Member member = MemberConverter.toMember(dto, salt, Role.ROLE_USER);
3847
// DB 적용
3948
memberRepository.save(member);
4049

4150
// 선호 음식 존재 여부 확인
42-
if (dto.preferCategory().size() > 1){
51+
if (dto.preferCategory() != null && !dto.preferCategory().isEmpty()) {
4352
List<MemberFood> memberFoodList = new ArrayList<>();
4453

4554
// 선호 음식 ID별 조회

src/main/java/com/example/umc9th/domain/member/service/query/MemberQuerySercvice.java

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)