Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions refrigerator/.idea/modules/refrigerator.main.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Original file line number Diff line number Diff line change
@@ -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 보안 비활성화
Expand All @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -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<GrantedAuthority> 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;
}
}
64 changes: 64 additions & 0 deletions refrigerator/src/main/java/moja/refrigerator/jwt/JWTFilter.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
46 changes: 46 additions & 0 deletions refrigerator/src/main/java/moja/refrigerator/jwt/JWTUtil.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
63 changes: 63 additions & 0 deletions refrigerator/src/main/java/moja/refrigerator/jwt/LoginFilter.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByUserId(String userId);
boolean existsByUserId(String userId);
boolean existsByUserEmail(String userEmail);
boolean existsByUserNickname(String userNickname);
Optional<User> findByUserPk(long userPk);

Optional<User> findByUserId(String userId);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> errors = new ArrayList<>();
if (userRepository.existsByUserId(request.getUserId())) {
Expand Down