Skip to content

Commit a73f420

Browse files
authored
feat(user) : 비밀번호 암호화 적용 / fix(jwt) : 쿠키에서 jwt를 추출하는 방식 변경
feat(user) : 비밀번호 암호화 적용 / fix(jwt) : 쿠키에서 jwt를 추출하는 방식 변경
2 parents 7bd443e + c08e11f commit a73f420

File tree

8 files changed

+72
-31
lines changed

8 files changed

+72
-31
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@
44
import com.backend.domain.user.dto.UserDto;
55
import com.backend.domain.user.service.EmailService;
66
import com.backend.domain.user.service.JwtService;
7-
import com.backend.domain.user.util.JwtUtil;
87
import com.backend.domain.user.service.UserService;
8+
import com.backend.domain.user.util.JwtUtil;
99
import com.backend.global.exception.ErrorCode;
1010
import com.backend.global.response.ApiResponse;
1111
import com.backend.global.response.ResponseCode;
1212
import jakarta.mail.MessagingException;
1313
import jakarta.servlet.http.Cookie;
1414
import jakarta.servlet.http.HttpServletRequest;
1515
import jakarta.servlet.http.HttpServletResponse;
16-
import jakarta.validation.Valid;
1716
import jakarta.validation.constraints.Email;
1817
import jakarta.validation.constraints.NotBlank;
1918
import lombok.RequiredArgsConstructor;
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
package com.backend.domain.user.dto;
22

3-
import com.backend.domain.user.dto.UserDto;
4-
5-
public record LoginResponse(UserDto user) {}
3+
public record LoginResponse(UserDto user) { }

backend/src/main/java/com/backend/domain/user/entity/User.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public void changeName(String name){
6969

7070
public void changePassword(String password){
7171
if(this.password==null || password.trim().isEmpty()){
72-
throw new BusinessException(ErrorCode.NAME_NOT_FOUND);
72+
throw new BusinessException(ErrorCode.PASSWORD_NOT_FOUND);
7373
}
7474
this.password = password;
7575
}

backend/src/main/java/com/backend/domain/user/service/JwtService.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import com.backend.domain.user.repository.UserRepository;
55
import com.backend.domain.user.util.JwtUtil;
66
import com.backend.domain.user.util.RedisUtil;
7+
import com.backend.global.exception.BusinessException;
8+
import com.backend.global.exception.ErrorCode;
79
import jakarta.validation.constraints.Email;
810
import jakarta.validation.constraints.NotBlank;
911
import lombok.RequiredArgsConstructor;
12+
import org.springframework.security.crypto.password.PasswordEncoder;
1013
import org.springframework.stereotype.Service;
1114

1215
@Service
@@ -15,14 +18,18 @@ public class JwtService {
1518
private final UserRepository userRepository;
1619
private final JwtUtil jwtUtil;
1720
private final RedisUtil redisUtil;
21+
private final PasswordEncoder passwordEncoder;
1822

1923
public String login(@NotBlank(message = "이메일은 필수 입력값 입니다.") @Email(message = "이메일 형식이 아닙니다.") String email, @NotBlank(message = "비밀번호는 필수 입력값 입니다.") String password) {
20-
User user = userRepository.findByEmail(email).orElse(null);
21-
if(user.getPassword().equals(password)) {
24+
User user = userRepository.findByEmail(email).orElseThrow(()->new BusinessException(ErrorCode.EMAIL_NOT_FOUND));
25+
//비밀번호 체크
26+
checkPassword(email, password);
27+
28+
if(checkPassword(email, password)) {
2229
//email에 대응하는 비밀번호가 맞다면 jwt토큰 발급
2330
return jwtUtil.createToken(user.getEmail(), user.getName());
2431
}else{
25-
return null;
32+
throw new BusinessException(ErrorCode.Login_Failed);
2633
}
2734
}
2835

@@ -43,4 +50,12 @@ public boolean isBlacklisted(String token){
4350
String key = "jwt:blacklist:"+token;
4451
return redisUtil.hasKey(key);
4552
}
53+
54+
//암호화된 비밀번호를 체크
55+
public boolean checkPassword(String email, String password){
56+
User user = userRepository.findByEmail(email).orElseThrow(()->new BusinessException(ErrorCode.EMAIL_NOT_FOUND));
57+
String hashedPassword = user.getPassword();
58+
59+
return passwordEncoder.matches(password, hashedPassword);
60+
}
4661
}

backend/src/main/java/com/backend/domain/user/service/UserService.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import jakarta.mail.MessagingException;
99
import jakarta.validation.constraints.NotBlank;
1010
import lombok.RequiredArgsConstructor;
11+
import org.springframework.security.crypto.password.PasswordEncoder;
1112
import org.springframework.stereotype.Service;
1213
import org.springframework.transaction.annotation.Transactional;
1314

@@ -20,6 +21,7 @@
2021
public class UserService {
2122
private final UserRepository userRepository;
2223
private final RedisUtil redisUtil;
24+
private final PasswordEncoder passwordEncoder;
2325

2426

2527
public User join(@NotBlank String email, @NotBlank String password, @NotBlank String passwordCheck, @NotBlank String name) throws MessagingException {
@@ -54,7 +56,10 @@ public User join(@NotBlank String email, @NotBlank String password, @NotBlank St
5456
}
5557

5658

57-
User user = new User(email, password, name);
59+
//암호화 된 비밀번호를 저장
60+
String encodedPassword = passwordEncoder.encode(password);
61+
62+
User user = new User(email, encodedPassword, name);
5863
return userRepository.save(user);
5964

6065

@@ -114,17 +119,12 @@ public User modifyName(String email, @NotBlank(message = "이름은 필수 입
114119

115120
}
116121

117-
public User modifyPassword(String email, @NotBlank(message = "비밀번호는 필수 입력값 입니다.") String password) {
118-
User user = userRepository.findByEmail(email).orElseThrow(() -> new BusinessException(ErrorCode.VALIDATION_FAILED));
119-
user.changePassword(password);
120-
return user;
121-
}
122-
123122
public User modifyPassword(String email, @NotBlank(message = "비밀번호는 필수 입력값 입니다.") String password, String passwordCheck) {
124123
User user = userRepository.findByEmail(email).orElseThrow(() -> new BusinessException(ErrorCode.VALIDATION_FAILED));
125124

126125
if(password.equals(passwordCheck)){
127-
user.changePassword(password);
126+
127+
user.changePassword(passwordEncoder.encode(password));
128128
}else{
129129
throw new BusinessException(ErrorCode.PASSWORD_NOT_EQUAL);
130130
}

backend/src/main/java/com/backend/global/exception/ErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ public enum ErrorCode {
1717
Login_Failed("U001", HttpStatus.BAD_REQUEST, "로그인에 실패했습니다."),
1818
Email_verify_Failed("U002", HttpStatus.BAD_REQUEST, "이메일 인증코드가 일치하지 않습니다"),
1919
NAME_NOT_FOUND("U003", HttpStatus.NOT_FOUND, "이름이 입력되지 않았습니다."),
20-
PASSWORD_NOT_FOUND("U004", HttpStatus.NOT_FOUND, "이름이 입력되지 않았습니다."),
20+
PASSWORD_NOT_FOUND("U004", HttpStatus.NOT_FOUND, "비밀번호가 입력되지 않았습니다."),
2121
PASSWORD_NOT_EQUAL("U005", HttpStatus.BAD_REQUEST, "비밀번호 확인이 일치하지 않습니다."),
22+
EMAIL_NOT_FOUND("U006", HttpStatus.NOT_FOUND, "해당 이메일은 없는 계정입니다.") ,
2223

2324
// ========== analysis 도메인 에러 ==========
2425
INVALID_GITHUB_URL("A001", HttpStatus.BAD_REQUEST, "올바른 GitHub 저장소 URL이 아닙니다."),

backend/src/main/java/com/backend/global/security/JwtAuthenticationFilter.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import io.jsonwebtoken.JwtException;
99
import jakarta.servlet.FilterChain;
1010
import jakarta.servlet.ServletException;
11+
import jakarta.servlet.http.Cookie;
1112
import jakarta.servlet.http.HttpServletRequest;
1213
import jakarta.servlet.http.HttpServletResponse;
1314
import lombok.RequiredArgsConstructor;
1415
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1516
import org.springframework.security.core.Authentication;
1617
import org.springframework.security.core.context.SecurityContextHolder;
18+
import org.springframework.stereotype.Component;
1719
import org.springframework.util.AntPathMatcher;
1820
import org.springframework.web.filter.OncePerRequestFilter;
1921

@@ -24,6 +26,7 @@
2426
import java.util.List;
2527
import java.util.Map;
2628

29+
@Component
2730
@RequiredArgsConstructor
2831
public class JwtAuthenticationFilter extends OncePerRequestFilter {
2932
private final JwtUtil jwtUtil;
@@ -79,16 +82,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
7982
}
8083

8184
//
82-
String authorizationHeader = request.getHeader("Authorization");
83-
System.out.println("AuthorizationHeader: " + authorizationHeader);
8485

8586

86-
String token = null;
87-
88-
//"Bearer " 제거
87+
String token = extractTokenFromCookie(request);
88+
89+
/* //"Bearer " 제거
8990
if(authorizationHeader != null && authorizationHeader.startsWith("Bearer ")){
9091
token = authorizationHeader.substring(7);
9192
}
93+
*/
94+
9295
//token이 null이거나 비어있다면 JWT가 입력되지 않은것으로 판단
9396
if(token==null||token.isEmpty()){
9497
sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "Token error : JWT가 입력되지 않았습니다.");
@@ -134,6 +137,24 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
134137
filterChain.doFilter(request, response);
135138
}
136139

140+
private String extractTokenFromCookie(HttpServletRequest request) {
141+
if(request.getCookies()==null){
142+
return null;
143+
}
144+
for(Cookie cookie : request.getCookies()){
145+
if(cookie.getName().equals("token")){
146+
String value = cookie.getValue();
147+
if (value != null && value.startsWith("Bearer%20")) {
148+
return value.substring(9);
149+
} else if (value != null && value.startsWith("Bearer ")) {
150+
return value.substring(7);
151+
}
152+
return value;
153+
}
154+
}
155+
return null;
156+
}
157+
137158
//에러 발생시 메세지 처리
138159
private void sendErrorResponse(HttpServletResponse response, int status, String message) throws IOException {
139160
response.setContentType("application/json;charset=UTF-8");

backend/src/main/java/com/backend/global/security/SecurityConfig.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,26 @@
11
package com.backend.global.security;
22

3-
import com.backend.domain.user.service.JwtService;
4-
import com.backend.domain.user.util.JwtUtil;
5-
import lombok.RequiredArgsConstructor;
63
import org.springframework.context.annotation.Bean;
74
import org.springframework.context.annotation.Configuration;
85
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
96
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
107
import org.springframework.security.config.http.SessionCreationPolicy;
8+
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
9+
import org.springframework.security.crypto.password.PasswordEncoder;
1110
import org.springframework.security.web.SecurityFilterChain;
1211
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
1312

1413
@Configuration
1514
@EnableWebSecurity
16-
@RequiredArgsConstructor
15+
//@RequiredArgsConstructor
1716
public class SecurityConfig {
18-
private final JwtUtil jwtUtil;
19-
private final JwtService jwtService;
17+
/* private final JwtUtil jwtUtil;
18+
private final JwtService jwtService;*/
19+
20+
//private final JwtAuthenticationFilter jwtAuthenticationFilter;
2021

2122
@Bean
22-
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
23+
public SecurityFilterChain filterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
2324
http
2425
// JWT 인증을 사용하므로 세션을 사용하지 않음 (Stateless)
2526
.sessionManagement(session -> session
@@ -58,9 +59,15 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
5859
.formLogin(login -> login.disable())
5960
.httpBasic(basic -> basic.disable())
6061
//커스텀 JWT 필터를 등록
61-
.addFilterBefore(new JwtAuthenticationFilter(jwtUtil, jwtService),
62+
.addFilterBefore(jwtAuthenticationFilter,
6263
UsernamePasswordAuthenticationFilter.class);
6364

6465
return http.build();
6566
}
67+
68+
// BcryptEncoder를 Bean으로 등록합니다.
69+
@Bean
70+
public PasswordEncoder passwordEncoder() {
71+
return new BCryptPasswordEncoder();
72+
}
6673
}

0 commit comments

Comments
 (0)