Skip to content

Commit 9f27dcb

Browse files
committed
로그인 시 JWT를 Cookie에 담아서 발행
1 parent 1344bc3 commit 9f27dcb

File tree

12 files changed

+157
-48
lines changed

12 files changed

+157
-48
lines changed

.idea/encodings.xml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules/backend.main.iml

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

backend/build.gradle.kts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import org.gradle.kotlin.dsl.implementation
2-
31
plugins {
42
java
53
id("org.springframework.boot") version "3.5.6"
@@ -48,6 +46,11 @@ dependencies {
4846
implementation("org.springframework.boot:spring-boot-starter-validation")
4947
implementation("org.springframework.boot:spring-boot-starter-mail")
5048
implementation("org.springframework.boot:spring-boot-starter-data-redis")
49+
50+
implementation("io.jsonwebtoken:jjwt-api:0.13.0")
51+
implementation("io.jsonwebtoken:jjwt-impl:0.13.0")
52+
implementation("io.jsonwebtoken:jjwt-jackson:0.13.0")
53+
implementation("org.springframework.boot:spring-boot-starter-security")
5154
}
5255

5356
tasks.withType<Test> {

backend/src/main/java/com/backend/domain/user/controller/AuthController.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
package com.backend.domain.user.controller;
22

33
import com.backend.domain.user.service.EmailService;
4+
import com.backend.domain.user.service.JwtService;
45
import com.backend.global.exception.ErrorCode;
56
import com.backend.global.response.ApiResponse;
67
import jakarta.mail.MessagingException;
8+
import jakarta.servlet.http.Cookie;
9+
import jakarta.servlet.http.HttpServletResponse;
10+
import jakarta.validation.Valid;
11+
import jakarta.validation.constraints.Email;
12+
import jakarta.validation.constraints.NotBlank;
713
import lombok.RequiredArgsConstructor;
14+
import org.springframework.beans.factory.annotation.Value;
815
import org.springframework.web.bind.annotation.PostMapping;
916
import org.springframework.web.bind.annotation.RequestBody;
10-
import org.springframework.web.bind.annotation.RequestParam;
1117
import org.springframework.web.bind.annotation.RestController;
1218

1319
@RestController
1420
@RequiredArgsConstructor
1521
public class AuthController {
1622
private final EmailService emailService;
23+
private final JwtService jwtService;
1724

25+
@Value("${jwt.access-token-expiration-in-milliseconds}")
26+
private int tokenValidityMilliSeconds;
1827

1928
/**
2029
* 입력받은 이메일에 인증코드를 보냅니다.
@@ -54,4 +63,39 @@ public ApiResponse<String> verifyAuthCode(@RequestBody VerifyRequest request)
5463

5564
}
5665

66+
67+
/**
68+
* 로그인
69+
*/
70+
record LoginRequest(
71+
@NotBlank(message = "이메일은 필수 입력값 입니다.")
72+
@Email(message = "이메일 형식이 아닙니다.")
73+
String email,
74+
75+
@NotBlank(message = "비밀번호는 필수 입력값 입니다.")
76+
String password
77+
){
78+
79+
}
80+
81+
82+
83+
@PostMapping("/api/login")
84+
public ApiResponse<String> login(
85+
@Valid @RequestBody LoginRequest loginRequest,
86+
HttpServletResponse response
87+
){
88+
String token = jwtService.login(loginRequest.email, loginRequest.password);
89+
90+
Cookie cookie = new Cookie("token", token);
91+
cookie.setHttpOnly(true); // JavaScript 접근 방지 (XSS 공격 방어)
92+
cookie.setSecure(true); //HTTPS 통신에서만 전송
93+
cookie.setPath("/");
94+
95+
cookie.setMaxAge(tokenValidityMilliSeconds);
96+
97+
response.addCookie(cookie); //응답에 쿠키 추가
98+
99+
return ApiResponse.success("success");
100+
}
57101
}

backend/src/main/java/com/backend/domain/user/controller/UserController.java

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
import com.backend.domain.user.entity.User;
55
import com.backend.domain.user.service.UserService;
66
import com.backend.global.response.ApiResponse;
7-
import com.backend.global.response.ResponseCode;
87
import jakarta.mail.MessagingException;
98
import jakarta.validation.Valid;
109
import jakarta.validation.constraints.Email;
1110
import jakarta.validation.constraints.NotBlank;
12-
import jakarta.validation.constraints.NotNull;
1311
import lombok.RequiredArgsConstructor;
1412
import org.springframework.web.bind.annotation.*;
1513

@@ -163,31 +161,4 @@ public ApiResponse<RestoreResponse> restoreUser(
163161
return ApiResponse.success(new RestoreResponse(new UserDto(user)));
164162
}
165163

166-
/*
167-
*//**
168-
* 로그인
169-
*//*
170-
record LoginRequest(
171-
@NotBlank(message = "이메일은 필수 입력값 입니다.")
172-
@Email(message = "이메일 형식이 아닙니다.")
173-
String email,
174-
175-
@NotBlank(message = "비밀번호는 필수 입력값 입니다.")
176-
String password
177-
){
178-
179-
}
180-
181-
record LoginResponse(
182-
183-
){
184-
185-
}
186-
187-
@PostMapping("/api/user/login")
188-
public ApiResponse<LoginResponse> login(
189-
@Valid @RequestBody LoginRequest loginRequest
190-
){
191-
192-
}*/
193164
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.backend.domain.user.service;
2+
3+
import com.backend.domain.user.entity.User;
4+
import com.backend.domain.user.repository.UserRepository;
5+
import com.backend.domain.user.util.JwtUtil;
6+
import jakarta.validation.constraints.Email;
7+
import jakarta.validation.constraints.NotBlank;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.stereotype.Service;
10+
11+
@Service
12+
@RequiredArgsConstructor
13+
public class JwtService {
14+
private final UserRepository userRepository;
15+
private final JwtUtil jwtUtil;
16+
17+
public String login(@NotBlank(message = "이메일은 필수 입력값 입니다.") @Email(message = "이메일 형식이 아닙니다.") String email, @NotBlank(message = "비밀번호는 필수 입력값 입니다.") String password) {
18+
User user = userRepository.findByEmail(email).orElse(null);
19+
if(user.getPassword().equals(password)) {
20+
//email에 대응하는 비밀번호가 맞다면 jwt토큰 발급
21+
return jwtUtil.createToken(user.getEmail(), user.getName());
22+
}else{
23+
return null;
24+
}
25+
}
26+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.backend.domain.user.util;
2+
3+
import io.jsonwebtoken.Claims;
4+
import io.jsonwebtoken.Jwts;
5+
import io.jsonwebtoken.SignatureAlgorithm;
6+
import io.jsonwebtoken.security.Keys;
7+
import jakarta.annotation.PostConstruct;
8+
import org.springframework.beans.factory.annotation.Value;
9+
import org.springframework.stereotype.Component;
10+
11+
import java.nio.charset.StandardCharsets;
12+
import java.security.Key;
13+
import java.util.Date;
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
@Component
18+
public class JwtUtil {
19+
@Value("${jwt.secret}")
20+
private String secretKey;
21+
22+
@Value("${jwt.access-token-expiration-in-milliseconds}")
23+
private int tokenValidityMilliSeconds;
24+
25+
//SecretKey를 Base64로 인코딩하여 Key객체로 변환
26+
private Key key;
27+
28+
//key값 초기화
29+
@PostConstruct //의존성 주입이 될때 딱 1번만 실행되기 때문에 key값은 이후로 변하지 않음
30+
public void init() {
31+
this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
32+
System.out.println(key);
33+
}
34+
35+
//
36+
public String createToken(String email, String name) {
37+
Map<String, Object> claims = new HashMap<>();
38+
claims.put(Claims.SUBJECT, email); //Claims.SUBJECT = "sub"이다.
39+
claims.put("name", name);
40+
41+
Date now = new Date();
42+
System.out.println("now : "+now.getTime());
43+
Date expiration = new Date(now.getTime() + tokenValidityMilliSeconds);
44+
System.out.println("expiration : "+expiration.getTime());
45+
return Jwts.builder()
46+
.claims(claims)
47+
.issuedAt(now)
48+
.expiration(expiration)
49+
.signWith(key, SignatureAlgorithm.HS256)
50+
.compact();
51+
}
52+
53+
}

backend/src/main/resources/application-dev.yml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
spring:
22
datasource:
3-
#url: jdbc:mysql://localhost:3306/my_project_db?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
4-
#username: user
5-
#password: 1234
63
url: ${DB_URL}
74
username: ${DB_USERNAME}
85
password: ${DB_PASSWORD}
6+
driver-class-name: org.h2.Driver
7+
#url: jdbc:mysql://localhost:3306/my_project_db?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
8+
#username: user
9+
#password: 1234
910
mail:
1011
host: smtp.gmail.com # ✅ Gmail SMTP 서버 주소 (고정)
1112
port: 587 # ✅ TLS/STARTTLS를 위한 포트 (고정)
@@ -25,4 +26,13 @@ spring:
2526

2627
jpa:
2728
hibernate:
28-
ddl-auto: update
29+
ddl-auto: update
30+
properties:
31+
hibernate:
32+
# 🚨 H2 Dialect를 명시적으로 설정 🚨
33+
dialect: org.hibernate.dialect.H2Dialect
34+
35+
h2:
36+
console:
37+
enabled: true
38+
path: /h2-console

0 commit comments

Comments
 (0)