diff --git a/refrigerator/.idea/modules/refrigerator.main.iml b/refrigerator/.idea/modules/refrigerator.main.iml
index 397c268..afc1871 100644
--- a/refrigerator/.idea/modules/refrigerator.main.iml
+++ b/refrigerator/.idea/modules/refrigerator.main.iml
@@ -4,20 +4,5 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/refrigerator/src/main/java/moja/refrigerator/aggregate/user/User.java b/refrigerator/src/main/java/moja/refrigerator/aggregate/user/User.java
index c10c057..03eb039 100644
--- a/refrigerator/src/main/java/moja/refrigerator/aggregate/user/User.java
+++ b/refrigerator/src/main/java/moja/refrigerator/aggregate/user/User.java
@@ -31,8 +31,6 @@ public class User {
@CreationTimestamp
private LocalDate joinDate;
-// @Column(name = "leave_date")
-// private LocalDate leaveDate;
-
-
+ @Column(name = "user_role")
+ private String userRole = "ROLE_USER";
}
diff --git a/refrigerator/src/main/java/moja/refrigerator/config/SecurityConfig.java b/refrigerator/src/main/java/moja/refrigerator/config/SecurityConfig.java
index 948026c..462533f 100644
--- a/refrigerator/src/main/java/moja/refrigerator/config/SecurityConfig.java
+++ b/refrigerator/src/main/java/moja/refrigerator/config/SecurityConfig.java
@@ -1,22 +1,44 @@
package moja.refrigerator.config;
+import moja.refrigerator.jwt.JWTFilter;
+import moja.refrigerator.jwt.JWTUtil;
+import moja.refrigerator.jwt.LoginFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
+ // AuthenticationManager가 인자로 받을 AuthenticationConfiguraion 객체 생성자 주입
+ private final AuthenticationConfiguration authenticationConfiguration;
+ // JWTUtil 주입
+ private final JWTUtil jwtUtil;
+
+ public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) {
+ this.authenticationConfiguration = authenticationConfiguration;
+ this.jwtUtil = jwtUtil;
+ }
+
@Bean
// 비밀번호 암호화를 위한 인코더
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
+ // AuthenticationManager Bean 등록
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
+ return configuration.getAuthenticationManager();
+ }
+
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// csrf 보안 비활성화
@@ -43,6 +65,12 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionManagement((session) -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // JWT 사용을 위한 세션리스 설정
+ // 로그인 필터 추가
+ http
+ .addFilterBefore(new JWTFilter(jwtUtil), LoginFilter.class);
+ http
+ .addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class);
+
return http.build();
}
}
\ No newline at end of file
diff --git a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java
index 324055a..7adacab 100644
--- a/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java
+++ b/refrigerator/src/main/java/moja/refrigerator/controller/user/UserController.java
@@ -15,10 +15,11 @@ public UserController(UserService userService) {
this.userService = userService;
}
-// @GetMapping("/")
-// public String getMainPage() {
-// return "user Controller";
-// }
+ // 토큰 검증 로직 확인용
+ @GetMapping("/")
+ public String getMainPage() {
+ return "user Controller";
+ }
// 회원 가입 처리
@PostMapping("/auth/join")
diff --git a/refrigerator/src/main/java/moja/refrigerator/dto/user/CustomUserDetails.java b/refrigerator/src/main/java/moja/refrigerator/dto/user/CustomUserDetails.java
new file mode 100644
index 0000000..2fb2d20
--- /dev/null
+++ b/refrigerator/src/main/java/moja/refrigerator/dto/user/CustomUserDetails.java
@@ -0,0 +1,53 @@
+package moja.refrigerator.dto.user;
+
+import moja.refrigerator.aggregate.user.User;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class CustomUserDetails implements UserDetails {
+ private final User user;
+
+ public CustomUserDetails(User user) {
+ this.user = user;
+ }
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ Collection collection = new ArrayList<>();
+ collection.add(() -> user.getUserRole());
+ return collection;
+ }
+
+ @Override
+ public String getPassword() {
+ return user.getUserPw();
+ }
+
+ @Override
+ public String getUsername() {
+ return user.getUserId();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+}
diff --git a/refrigerator/src/main/java/moja/refrigerator/jwt/JWTFilter.java b/refrigerator/src/main/java/moja/refrigerator/jwt/JWTFilter.java
new file mode 100644
index 0000000..e22cfe8
--- /dev/null
+++ b/refrigerator/src/main/java/moja/refrigerator/jwt/JWTFilter.java
@@ -0,0 +1,64 @@
+package moja.refrigerator.jwt;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import moja.refrigerator.aggregate.user.User;
+import moja.refrigerator.dto.user.CustomUserDetails;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import java.io.IOException;
+
+public class JWTFilter extends OncePerRequestFilter {
+ private final JWTUtil jwtUtil;
+
+ public JWTFilter(JWTUtil jwtUtil) {
+ this.jwtUtil = jwtUtil;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
+ // 헤더에서 토큰 추출
+ String authorization = request.getHeader("Authorization");
+
+ // Authorization 헤더 검증
+ if (authorization == null || !authorization.startsWith("Bearer ")) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ String token = authorization.split(" ")[1];
+
+ // 토큰 소멸 시간 검증
+ if (jwtUtil.isExpired(token)) {
+ // 만료된 토큰이면 그냥 통과
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ // 토큰에서 정보 추출
+ String username = jwtUtil.getUsername(token);
+ String role = jwtUtil.getRole(token);
+
+ // User를 생성하여 값 set
+ User user = new User();
+ user.setUserId(username);
+ user.setUserPw("temppassword");
+ user.setUserRole(role);
+
+ // UserDetails에 회원 정보 객체 담기
+ CustomUserDetails customUserDetails = new CustomUserDetails(user);
+
+ // 스프링 시큐리티 인증 토큰 생성
+ Authentication authToken = new UsernamePasswordAuthenticationToken(customUserDetails, null, customUserDetails.getAuthorities());
+
+ // 세션에 사용자 등록
+ SecurityContextHolder.getContext().setAuthentication(authToken);
+
+ filterChain.doFilter(request, response);
+ }
+}
diff --git a/refrigerator/src/main/java/moja/refrigerator/jwt/JWTUtil.java b/refrigerator/src/main/java/moja/refrigerator/jwt/JWTUtil.java
new file mode 100644
index 0000000..df05cc4
--- /dev/null
+++ b/refrigerator/src/main/java/moja/refrigerator/jwt/JWTUtil.java
@@ -0,0 +1,46 @@
+package moja.refrigerator.jwt;
+
+import io.jsonwebtoken.Jwts;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+
+@Component
+public class JWTUtil {
+ private SecretKey secretKey;
+
+ // application.yml에서 jwt secret key를 가져옴
+ public JWTUtil(@Value("${spring.jwt.secret}") String secret) {
+ this.secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), Jwts.SIG.HS256.key().build().getAlgorithm());
+ }
+
+ // 토큰에서 username 추출
+ public String getUsername(String token) {
+ return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("username", String.class);
+ }
+
+ // 토큰에서 role(권한) 추출
+ public String getRole(String token) {
+ return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().get("role", String.class);
+ }
+
+ // 토큰 만료 여부 확인
+ public Boolean isExpired(String token) {
+ return Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token).getPayload().getExpiration().before(new Date());
+ }
+
+ // 토큰 생성
+ public String createJwt(String username, String role, Long expiredMs) {
+ return Jwts.builder()
+ .claim("username", username)
+ .claim("role", role)
+ .issuedAt(new Date(System.currentTimeMillis()))
+ .expiration(new Date(System.currentTimeMillis() + expiredMs))
+ .signWith(secretKey)
+ .compact();
+ }
+}
diff --git a/refrigerator/src/main/java/moja/refrigerator/jwt/LoginFilter.java b/refrigerator/src/main/java/moja/refrigerator/jwt/LoginFilter.java
new file mode 100644
index 0000000..622aac9
--- /dev/null
+++ b/refrigerator/src/main/java/moja/refrigerator/jwt/LoginFilter.java
@@ -0,0 +1,63 @@
+package moja.refrigerator.jwt;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import moja.refrigerator.dto.user.CustomUserDetails;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+public class LoginFilter extends UsernamePasswordAuthenticationFilter {
+ private final AuthenticationManager authenticationManager;
+ private final JWTUtil jwtUtil;
+
+ public LoginFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil) {
+ this.authenticationManager = authenticationManager;
+ this.jwtUtil = jwtUtil;
+ }
+
+ // 로그인 시도 처리
+ @Override
+ public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
+ // 클라이언트 요청에서 username, password 추출
+ String username = obtainUsername(request);
+ String password = obtainPassword(request);
+
+ // 이 정보를 토큰으로 만듦 (아직 인증되지 않은 상태)
+ UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password, null);
+
+ // AuthenticationManager에게 검증 요청
+ return authenticationManager.authenticate(authToken);
+ }
+
+ // 로그인 성공 처리 - JWT 토큰 발급
+ @Override
+ protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) {
+ CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
+
+ String username = customUserDetails.getUsername();
+
+ Collection extends GrantedAuthority> authorities = authentication.getAuthorities();
+ Iterator extends GrantedAuthority> iterator = authorities.iterator();
+ GrantedAuthority auth = iterator.next();
+
+ String role = auth.getAuthority();
+
+ String token = jwtUtil.createJwt(username, role, 60*60*10L);
+
+ response.addHeader("Authorization", "Bearer " + token);
+ }
+
+ // 로그인 실패 처리
+ @Override
+ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
+ response.setStatus(401);
+ }
+}
diff --git a/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java b/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java
index 80f3056..b3b44c0 100644
--- a/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java
+++ b/refrigerator/src/main/java/moja/refrigerator/repository/user/UserRepository.java
@@ -8,9 +8,9 @@
@Repository
public interface UserRepository extends JpaRepository {
- boolean existsByUserId(String userId);
+ boolean existsByUserId(String userId);
boolean existsByUserEmail(String userEmail);
boolean existsByUserNickname(String userNickname);
Optional findByUserPk(long userPk);
-
+ Optional findByUserId(String userId);
}
diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/CustomUserDetailsService.java b/refrigerator/src/main/java/moja/refrigerator/service/user/CustomUserDetailsService.java
new file mode 100644
index 0000000..604afac
--- /dev/null
+++ b/refrigerator/src/main/java/moja/refrigerator/service/user/CustomUserDetailsService.java
@@ -0,0 +1,27 @@
+package moja.refrigerator.service.user;
+
+import moja.refrigerator.aggregate.user.User;
+import moja.refrigerator.dto.user.CustomUserDetails;
+import moja.refrigerator.repository.user.UserRepository;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class CustomUserDetailsService implements UserDetailsService {
+ private final UserRepository userRepository;
+
+ public CustomUserDetailsService(UserRepository userRepository) {
+ this.userRepository = userRepository;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User userData = userRepository.findByUserId(username)
+ .orElseThrow(() -> new UsernameNotFoundException("입력하신 아이디로 가입된 사용자를 찾을 수 없습니다.: " + username));
+ return new CustomUserDetails(userData);
+ }
+}
diff --git a/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java b/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java
index b52fd64..7cd888b 100644
--- a/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java
+++ b/refrigerator/src/main/java/moja/refrigerator/service/user/UserServiceImpl.java
@@ -35,10 +35,12 @@ public void createUser(UserCreateRequest request) {
// 비밀번호 암호화
user.setUserPw(passwordEncoder.encode(request.getUserPw()));
+ user.setUserRole("ROLE_USER");
userRepository.save(user);
}
+
private void checkDuplicateUser(UserCreateRequest request) {
List errors = new ArrayList<>();
if (userRepository.existsByUserId(request.getUserId())) {