diff --git a/build.gradle b/build.gradle index 9f90408..fa99fb7 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,10 @@ dependencies { //gson implementation 'com.google.code.gson:gson:2.8.8' + //jwt + implementation 'io.jsonwebtoken:jjwt:0.9.1' + + } tasks.named('test') { diff --git a/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java b/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java new file mode 100644 index 0000000..902da00 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java @@ -0,0 +1,12 @@ +package com.umc.DongnaeFriend.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class JwtConfig { + + @Value("${jwt.secret-key}") + public String SECRET_KEY; + +} diff --git a/src/main/java/com/umc/DongnaeFriend/config/SecurityConfig.java b/src/main/java/com/umc/DongnaeFriend/config/SecurityConfig.java new file mode 100644 index 0000000..865fbbf --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/config/SecurityConfig.java @@ -0,0 +1,33 @@ +package com.umc.DongnaeFriend.config; + + +import com.umc.DongnaeFriend.global.security.JwtTokenFilter; +import org.springframework.beans.factory.annotation.Autowired; +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.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private JwtTokenFilter jwtTokenFilter; + + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers("/user/login").permitAll() // 인증 없이 접근 허용하는 URL + .antMatchers("/user/reissuance").permitAll() // 인증 없이 접근 허용하는 URL + .anyRequest().authenticated(); // 그 외의 URL은 인증 필요 + http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); + } + + // 나머지 코드는 이전 예제와 동일 +} diff --git a/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/controller/accountBookSharingController.java b/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/controller/accountBookSharingController.java index 67d39ea..d067631 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/controller/accountBookSharingController.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/controller/accountBookSharingController.java @@ -7,6 +7,7 @@ import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -35,9 +36,7 @@ public class accountBookSharingController { @GetMapping("/search") public ResponseEntity searchAll(@RequestParam("keyword") String keyword, @RequestParam("category") int category, Pageable pageable) { - log.info("searching : " + keyword + category); List res = accountBookSharingService.searchByKeyword(keyword, category, pageable); - log.info("res "); return ResponseEntity.ok(res); } diff --git a/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/repository/SharingBoardRepository.java b/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/repository/SharingBoardRepository.java index 44692ff..54bc584 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/repository/SharingBoardRepository.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/account/sharing/repository/SharingBoardRepository.java @@ -9,7 +9,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import javax.persistence.EntityManager; import java.util.List; @Repository diff --git a/src/main/java/com/umc/DongnaeFriend/domain/dongnae/controller/DongnaeBoardController.java b/src/main/java/com/umc/DongnaeFriend/domain/dongnae/controller/DongnaeBoardController.java index c8e7d7f..51c4fe2 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/dongnae/controller/DongnaeBoardController.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/dongnae/controller/DongnaeBoardController.java @@ -3,8 +3,10 @@ import com.umc.DongnaeFriend.domain.dongnae.dto.DongnaeBoardDto; import com.umc.DongnaeFriend.domain.dongnae.respository.DongnaeBoardRepository; import com.umc.DongnaeFriend.domain.dongnae.service.DongnaeBoardService; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import javax.naming.AuthenticationException; @@ -13,6 +15,7 @@ * [ 가계부 공유 ] * */ +@Slf4j @RestController @RequestMapping("/town-information") public class DongnaeBoardController { @@ -52,6 +55,9 @@ public ResponseEntity getLocation() { public ResponseEntity getBoards(@RequestParam("keyword") String keyword, @RequestParam("category") int category, @RequestParam("sortBy") int sort) { + + + log.info("User Id: " + SecurityContextHolder.getContext().getAuthentication().getPrincipal()); return ResponseEntity.ok(dongnaeBoardService.searchByKeyword(keyword, category, sort)); } diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java b/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java new file mode 100644 index 0000000..cc30211 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java @@ -0,0 +1,80 @@ +package com.umc.DongnaeFriend.domain.user.contorller; + +import com.umc.DongnaeFriend.domain.user.dto.UserDto; +import com.umc.DongnaeFriend.domain.user.service.KakaoService; +import com.umc.DongnaeFriend.domain.user.service.UserService; +import com.umc.DongnaeFriend.global.exception.CustomException; +import com.umc.DongnaeFriend.global.exception.ErrorCode; +import com.umc.DongnaeFriend.global.util.JwtTokenProvider; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.repository.query.Param; +import org.springframework.http.*; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.HashMap; + +@RestController +@RequestMapping("/user") +@Slf4j +public class UserController { + + @Autowired + KakaoService kakaoService; + + @Autowired + UserService userService; + + @Autowired + JwtTokenProvider jwtTokenProvider; + + + + /** + * 유저 로그인 / 회원가입 + * 인증 절차 + */ + @PostMapping("/login") + public ResponseEntity userLogin(@RequestParam("accessToken") String accessToken, HttpServletRequest request, HttpServletResponse httpServletResponse) { + log.info("LoginController 진입"); + +// if (!type.equals("kakao")) { +// throw new CustomException(ErrorCode.SERVER_ERROR); +// } + + + try { + log.info("userLogin 진입"); + //사용자 정보 가져오기 + HashMap userInfo = kakaoService.getUserInfo(accessToken); + + //사용자 확인 기존 회원 -> 넘어가고, 없는 회원 -> 회원가입 + + UserDto.Response response = userService.userValidation(userInfo); + + return ResponseEntity.ok(response); + + } catch (IOException e) { + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } + } + + @PostMapping("/user/reissuance") + public ResponseEntity reiussnaceToken(String refreshToken) { + try { + + //토큰 재발급 + String access_token = userService.createAccessTokenFromRefreshToken(refreshToken); + return ResponseEntity.ok(access_token); + } catch (Exception e) { + // RefreshToken만료 + throw new CustomException(ErrorCode.INVALID_REFRESH_TOKEN); + } + } + + + +} diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java b/src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java new file mode 100644 index 0000000..0ebf5bc --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java @@ -0,0 +1,42 @@ +package com.umc.DongnaeFriend.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +public class UserDto { + + @Getter + @AllArgsConstructor + public static class Request { + + String accessToken; + + String type; + + } + + @Getter + @Builder + @AllArgsConstructor + public static class Response { + + String accessToken; + + String refreshToken; + + } + + @Getter + @AllArgsConstructor + public static class SignUpDto { + + String nickName; + + String email; + + Long kakaoId; + + } + +} diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/entity/User.java b/src/main/java/com/umc/DongnaeFriend/domain/user/entity/User.java index ea0df4c..2ed546f 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/entity/User.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/entity/User.java @@ -13,6 +13,7 @@ import com.umc.DongnaeFriend.domain.type.YesNo; import javax.persistence.*; import lombok.*; +import org.springframework.lang.Nullable; @Getter @Builder @@ -28,7 +29,8 @@ public class User extends BaseTimeEntity { private Long id; @ManyToOne(fetch = LAZY) - @JoinColumn(name = "dongnae_id", nullable = false) + @JoinColumn(name = "dongnae_id") + @Nullable private Dongnae dongnae; @Column(nullable = false) diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java b/src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java index fbb81ef..1807bef 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java @@ -2,10 +2,13 @@ import com.umc.DongnaeFriend.domain.user.entity.User; import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; +import java.util.Optional; -@Repository public interface UserRepository extends JpaRepository { + Optional findById(Long id); + Optional findByRefreshToken(String refresh_token); + + Optional findByKakaoId(Long id); } diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java new file mode 100644 index 0000000..d3cd29f --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java @@ -0,0 +1,16 @@ +package com.umc.DongnaeFriend.domain.user.service; + + +import org.springframework.beans.factory.annotation.Value; + +import java.io.IOException; +import java.util.HashMap; + +public interface KakaoService { + + + @SuppressWarnings("unchecked") + HashMap getUserInfo(String access_Token) throws IOException; +} + + diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java new file mode 100644 index 0000000..6c147f5 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java @@ -0,0 +1,81 @@ +package com.umc.DongnaeFriend.domain.user.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Service +public class KakaoServiceimpl implements KakaoService { + +// @Autowired +// public IACDao dao; + + @SuppressWarnings("unchecked") + @Override + public HashMap getUserInfo(String access_Token) throws IOException { + // 클라이언트 요청 정보 + HashMap userInfo = new HashMap(); + + + //------kakao GET 요청------ + String reqURL = "https://kapi.kakao.com/v2/user/me"; + URL url = new URL(reqURL); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Authorization", "Bearer " + access_Token); + + int responseCode = conn.getResponseCode(); + System.out.println("responseCode : " + responseCode); + + BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); + + String line = ""; + String result = ""; + + while ((line = br.readLine()) != null) { + result += line; + } + System.out.println("response body : " + result); + System.out.println("result type" + result.getClass().getName()); // java.lang.String + + // jackson objectmapper 객체 생성 + ObjectMapper objectMapper = new ObjectMapper(); + // JSON String -> Map + Map jsonMap = objectMapper.readValue(result, new TypeReference>() { + }); + + + System.out.println(jsonMap.get("properties")); + + Long id = (Long) jsonMap.get("id"); + Map properties = (Map) jsonMap.get("properties"); + Map kakao_account = (Map) jsonMap.get("kakao_account"); + Map profile = (Map) kakao_account.get("profile"); + + log.info("profile : " + profile.toString()); + log.info("kakao_acount : " + kakao_account.toString()); + + String nickname = properties.get("nickname").toString(); + String profileImage = properties.get("profile_image").toString(); + String email = kakao_account.get("email").toString(); + + userInfo.put("id", id); + userInfo.put("nickname", nickname); + userInfo.put("profileImage", profileImage); + userInfo.put("email", email); + + + return userInfo; + } + +} \ No newline at end of file diff --git a/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java b/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java new file mode 100644 index 0000000..40e600c --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java @@ -0,0 +1,124 @@ +package com.umc.DongnaeFriend.domain.user.service; + +import com.umc.DongnaeFriend.domain.type.Age; +import com.umc.DongnaeFriend.domain.type.Gender; +import com.umc.DongnaeFriend.domain.type.YesNo; +import com.umc.DongnaeFriend.domain.user.dto.UserDto; +import com.umc.DongnaeFriend.domain.user.entity.User; +import com.umc.DongnaeFriend.domain.user.repository.UserRepository; +import com.umc.DongnaeFriend.global.exception.CustomException; +import com.umc.DongnaeFriend.global.exception.ErrorCode; +import com.umc.DongnaeFriend.global.util.JwtTokenProvider; +import com.umc.DongnaeFriend.global.util.JwtUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Optional; + +@Service +@Slf4j +public class UserService { + + @Autowired + UserRepository userRepository; + + KakaoService kakaoService; + + @Autowired + JwtTokenProvider jwtTokenProvider; + + + public UserDto.Response userValidation(HashMap userInfo) { + Long kakao_id = (Long) userInfo.get("id"); + Optional user= userRepository.findByKakaoId(kakao_id); + if (user.isEmpty()) { + User new_user = userRegister(userInfo); + return UserDto.Response.builder() + .accessToken(jwtTokenProvider.createAccessToken(new_user.getId())) + .refreshToken(new_user.getRefreshToken()) + .build(); + } else { + return UserDto.Response.builder() + .accessToken(jwtTokenProvider.createAccessToken(user.get().getId())) + .refreshToken(user.get().getRefreshToken()) + .build(); + } + + } + + + //유저 회원가입 -> Refresh Token을 return + public User userRegister(HashMap userInfo) { + //필수 + String nickName = userInfo.get("nickname").toString(); + //필수 + String email = userInfo.get("email").toString(); + + String profileImage = userInfo.get("profileImage").toString(); + + Long kakaoId = (Long) userInfo.get("id"); + +// Optional gender = Optional.ofNullable(userInfo.get("gender").toString()); +// String strGender = ""; +// log.info("Gender : {}", gender.get()); +// if(gender.get()=="F"){ +// strGender="여성"; +// }else { +// strGender = "남성"; +// } +// log.info("strGender : {}", strGender); +// +// +// Optional age = Optional.ofNullable(userInfo.get("age").toString()); +// String[] ageRange = age.get().split("-"); +// +// +// // refreshToken userId를 claim 으로 생성 뒤, User의 필드에 넣고 User를 저장 + String refresh_Token = jwtTokenProvider.createRefreshToken((Long) userInfo.get("id")); + + return userRepository.save( + User.builder() + .nickname(nickName) +// .dongnae( +// +// ) + .email(email) + //TODO : Gender 결정[O] + .gender( +// Gender.valueOf(strGender) + Gender.MALE + ) + //TODO : Age 결정[O] + .age( +// Age.valueOf(ageRange[0]+"대") + Age.AGE20 + ) + .townCert(YesNo.NO) + .townCertCnt(0) + .infoCert(YesNo.NO) + .profileImage(profileImage) + .kakaoId(kakaoId) + .refreshToken(refresh_Token) + .build() + ); + } + + // RefreshToken으로 AccessToken 재발급 + public String createAccessTokenFromRefreshToken(String refreshToken) { + String accessToken = ""; + + // 전달받은 RefreshToken 정보로 사용자 조회(유효하지 않은 토큰일 시, 예외 발생) + Optional userByRefreshToken =Optional.ofNullable(userRepository.findByRefreshToken(refreshToken) + .orElseThrow(()-> new CustomException(ErrorCode.INVALID_REFRESH_TOKEN,"해당 refreshToken 이 존재하지 않음"))); + + // AccessToken 재발행 + accessToken = jwtTokenProvider.createAccessToken(userByRefreshToken.get().getId()); + log.info("AcessToken 재발행 성공"); + + return accessToken; + } + + +} diff --git a/src/main/java/com/umc/DongnaeFriend/global/exception/GlobalExceptionHandler.java b/src/main/java/com/umc/DongnaeFriend/global/exception/GlobalExceptionHandler.java index da2dd2c..137db6e 100644 --- a/src/main/java/com/umc/DongnaeFriend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/umc/DongnaeFriend/global/exception/GlobalExceptionHandler.java @@ -29,12 +29,14 @@ protected ResponseEntity handleMethodArgumentNotValidException(MethodArgument .body(new ErrorResponse(validException)); } - /*//일반 예외처리 - @ExceptionHandler({Exception.class}) - protected ResponseEntity handleServerException(Exception ex) { - CustomException exception = new CustomException(SERVER_ERROR); - return ResponseEntity - .status(SERVER_ERROR.getHttpStatus()) - .body(new ErrorResponse(exception)); - }*/ + + //일반 예외처리 +// @ExceptionHandler({Exception.class}) +// protected ResponseEntity handleServerException(Exception ex) { +// CustomException exception = new CustomException(SERVER_ERROR); +// return ResponseEntity +// .status(SERVER_ERROR.getHttpStatus()) +// .body(new ErrorResponse(exception)); +// } + } \ No newline at end of file diff --git a/src/main/java/com/umc/DongnaeFriend/global/security/JwtTokenFilter.java b/src/main/java/com/umc/DongnaeFriend/global/security/JwtTokenFilter.java new file mode 100644 index 0000000..6c0cb55 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/global/security/JwtTokenFilter.java @@ -0,0 +1,78 @@ +package com.umc.DongnaeFriend.global.security; + +import com.umc.DongnaeFriend.global.util.JwtUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +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 javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Configuration +@Slf4j +public class JwtTokenFilter extends OncePerRequestFilter { + + + private final JwtUtil jwtUtil; + + public JwtTokenFilter(JwtUtil jwtUtil) { + this.jwtUtil = jwtUtil; + } + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + log.info("JwtTokenFilter 진입"); + + if (request.getServletPath().contains("/user/login")) { + log.info("/user/login 진입"); + } + + // Request Header에서 JWT 토큰 가져오기 + String authorizationHeader = request.getHeader("Authorization"); + log.info("authorizationHeader : {}",authorizationHeader); + + //🛑 첫 로그인 시에도 이곳에서 걸리기 때문에 로그인이 안됨.(null) + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + String token = authorizationHeader.substring(7); + log.info("token : {}", token); + try { + // JWT 토큰 검증 + jwtUtil.validateToken(token); + log.info("JWT 토큰 검증완료"); + + // JWT 토큰에서 사용자 정보 추출 (예: 사용자 ID) + Long userId = jwtUtil.getUserIdFromToken(token); + + // 인증 객체 생성 + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken( userId,null, null); + + // SecurityContextHolder에 인증 객체 저장 + + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + + filterChain.doFilter(request, response); + + + } catch (Exception e) { + e.printStackTrace(); + + // JWT 토큰 검증 실패 시, 인증 객체를 null로 설정 + SecurityContextHolder.clearContext(); + filterChain.doFilter(request, response); + } + } else { + log.info("Header None"); + filterChain.doFilter(request, response); + } + + } +} \ No newline at end of file diff --git a/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java b/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java new file mode 100644 index 0000000..344ba00 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java @@ -0,0 +1,70 @@ +package com.umc.DongnaeFriend.global.util; + +import com.umc.DongnaeFriend.config.JwtConfig; +import com.umc.DongnaeFriend.domain.user.repository.UserRepository; +import io.jsonwebtoken.*; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Date; + + + +@Log4j2 +@Component +public class JwtTokenProvider { + + private final JwtConfig jwtConfig; + + @Autowired + private UserRepository userRepository; + + public JwtTokenProvider(JwtConfig jwtConfig, UserRepository userRepository) { + this.jwtConfig = jwtConfig; + this.userRepository = userRepository; + } + + private final Long ACCESS_TOKEN_EXPIRE_LENGTH = 1000L * 60 * 60 * 24 * 14; //2WEEK + private final Long REFRESH_TOKEN_EXPIRE_LENGTH = 1000L * 60 * 60 * 24 * 30; //30DAY + + + //accessToken 생성 + public String createAccessToken(Long userId) { + + log.info("SECRET KEY FROM PROVIDER: "+ jwtConfig.SECRET_KEY); + Date now = new Date(); //현재 시간 + Date validity = new Date(now.getTime() + ACCESS_TOKEN_EXPIRE_LENGTH); + + +// CustomAuthentication user = (CustomAuthentication) authentication.getPrincipal(); +// +// Claims claims = Jwts.claims().setSubject(user.getUsername()); +// claims.put("userId", user.getId()); // 사용자 아이디 +// claims.put("email", user.getEmail()); // 사용자 이메일 + + return Jwts.builder() + .signWith(SignatureAlgorithm.HS512, String.valueOf(jwtConfig.SECRET_KEY)) + .claim("userId", userId) + .setIssuedAt(now) //token 발행 시간 + .setExpiration(validity) + .compact(); + } + + // RefreshToken 생성 + public String createRefreshToken(Long userId) { + Date now = new Date(); + Date validity = new Date(now.getTime() + REFRESH_TOKEN_EXPIRE_LENGTH); + + return Jwts.builder() + .signWith(SignatureAlgorithm.HS512, String.valueOf(jwtConfig.SECRET_KEY)) + .claim("userId", userId) + .setIssuedAt(now) + .setExpiration(validity) + .compact(); + } + + + + +} \ No newline at end of file diff --git a/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java new file mode 100644 index 0000000..a1c15b9 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java @@ -0,0 +1,61 @@ +package com.umc.DongnaeFriend.global.util; + +import com.umc.DongnaeFriend.config.JwtConfig; +import com.umc.DongnaeFriend.global.exception.CustomException; +import com.umc.DongnaeFriend.global.exception.ErrorCode; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + + +@Slf4j +@Component +public class JwtUtil { + + private final JwtConfig jwtConfig; + + public JwtUtil(JwtConfig jwtConfig) { + this.jwtConfig = jwtConfig; + } + + public Long getUserIdFromToken(String token) { + log.info("token에서 ID 추출"); + Claims claims = Jwts.parser().setSigningKey(jwtConfig.SECRET_KEY).parseClaimsJws(token).getBody(); + return Long.parseLong(claims.get("userId").toString()); + } + + //token 유효성 검증 + public Boolean validateToken(String token) { + try { + log.info("SECRET KEY :"+ jwtConfig.SECRET_KEY); + + Jwts.parser().setSigningKey(jwtConfig.SECRET_KEY).parseClaimsJws(token); + return true; + } catch (SignatureException e) { + log.info("Sign 오류"); + e.printStackTrace(); + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (IllegalArgumentException e) { + log.info("잘못된 토큰"); + e.printStackTrace(); + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (MalformedJwtException e) { + log.info("토큰 잘림"); + e.printStackTrace(); + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (ExpiredJwtException e) { + log.info("만료된 토큰"); + e.printStackTrace(); + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (NullPointerException e) { + log.info("토큰 없음"); + e.printStackTrace(); + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (UnsupportedJwtException e) { + log.info("지원되지 않는 토큰"); + e.printStackTrace(); + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a8707ff..6b69a6d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -3,12 +3,6 @@ logging: com.example.carrotmarket: debug org.hibernate.SQL: debug -spring: - jpa: - properties: - hibernate: - format_sql: true - show_sql: true --- # Settings for local spring: @@ -19,4 +13,10 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate.ddl-auto: update - database: mysql + properties: + hibernate: + format_sql: true + show_sql: true + +jwt: + secret-key: 6B64DCA4EA2F53EDIKU9AAB215FE7