From 00a376fbee61638198d63abb5601664fbab6382b Mon Sep 17 00:00:00 2001 From: DDonghyeo Date: Mon, 24 Jul 2023 22:22:44 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=A5=95Feat=20#23:=20[OAuth2]=20?= =?UTF-8?q?=EC=86=8C=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 + .../umc/DongnaeFriend/config/JwtConfig.java | 12 +++ .../DongnaeFriend/config/SecurityConfig.java | 33 ++++++++ .../controller/DongnaeBoardController.java | 1 + .../user/contorller/UserController.java | 56 ++++++++++++++ .../domain/user/dto/UserDto.java | 40 ++++++++++ .../domain/user/entity/User.java | 2 +- .../user/repository/UserRepository.java | 11 +++ .../domain/user/service/KakaoService.java | 12 +++ .../domain/user/service/KakaoServiceimpl.java | 76 +++++++++++++++++++ .../domain/user/service/UserService.java | 58 ++++++++++++++ .../global/util/JwtTokenProvider.java | 53 +++++++++++++ .../DongnaeFriend/global/util/JwtUtil.java | 39 ++++++++++ .../security/JwtTokenFilter.java | 45 +++++++++++ src/main/resources/application.yml | 17 +++-- 15 files changed, 451 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java create mode 100644 src/main/java/com/umc/DongnaeFriend/config/SecurityConfig.java create mode 100644 src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java create mode 100644 src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java create mode 100644 src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java create mode 100644 src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java create mode 100644 src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java create mode 100644 src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java create mode 100644 src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java create mode 100644 src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java create mode 100644 src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java diff --git a/build.gradle b/build.gradle index b6ac063..f8c9d8b 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,10 @@ dependencies { testImplementation 'org.springframework.security:spring-security-test' + //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..af15fc2 --- /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 static 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..6931738 --- /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.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/dongnae/controller/DongnaeBoardController.java b/src/main/java/com/umc/DongnaeFriend/domain/dongnae/controller/DongnaeBoardController.java index c8e7d7f..b168f28 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 @@ -5,6 +5,7 @@ import com.umc.DongnaeFriend.domain.dongnae.service.DongnaeBoardService; 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; 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..be5b883 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java @@ -0,0 +1,56 @@ +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.HashMap; + +@RestController +@RequestMapping("/user") +public class UserController { + + @Autowired + KakaoService kakaoService; + + @Autowired + UserService userService; + + JwtTokenProvider jwtTokenProvider; + + /** + * 유저 로그인 / 회원가입 + * 인증 절차 + */ + @PostMapping("/login") + public ResponseEntity userLogin(@RequestBody UserDto.Request request) { + try { + //사용자 정보 가져오기 + HashMap userInfo = kakaoService.getUserInfo(request.getAccessToken()); + + //사용자 확인 기존 회원 -> 넘어가고, 없는 회원 -> 회원가입 + userService.userValidation(userInfo); + + //토큰 생성 + String access_token = jwtTokenProvider.createAccessToken((Long) userInfo.get("usreId")); + + return ResponseEntity.ok(access_token); + + } catch (IOException e) { + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } + } + + @PostMapping("/user/reissuance") + public ResponseEntity reiussnaceToken(String access_oto) + + + +} 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..469dcdb --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java @@ -0,0 +1,40 @@ +package com.umc.DongnaeFriend.domain.user.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +public class UserDto { + + @Getter + @AllArgsConstructor + public static class Request { + + String accessToken; + + String type; + + } + + @Getter + @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 665c1db..b6e8772 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 @@ -28,7 +28,7 @@ public class User extends BaseTimeEntity { private Long id; @ManyToOne(fetch = LAZY) - @JoinColumn(name = "dongnae_id", nullable = false) + @JoinColumn(name = "dongnae_id") 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 new file mode 100644 index 0000000..869602d --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java @@ -0,0 +1,11 @@ +package com.umc.DongnaeFriend.domain.user.repository; + +import com.umc.DongnaeFriend.domain.user.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface UserRepository extends JpaRepository { + + Optional findById(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..a4b2aaf --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java @@ -0,0 +1,12 @@ +package com.umc.DongnaeFriend.domain.user.service; + + +import java.io.IOException; +import java.util.HashMap; + +public interface KakaoService { + + 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..4955e10 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java @@ -0,0 +1,76 @@ +package com.umc.DongnaeFriend.domain.user.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +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; + +@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")); + + Map properties = (Map) jsonMap.get("properties"); + Map kakao_account = (Map) jsonMap.get("kakao_account"); + + // System.out.println(properties.get("nickname")); + // System.out.println(kakao_account.get("email")); + + String nickname = properties.get("nickname").toString(); + String email = kakao_account.get("email").toString(); + String gender = kakao_account.get("gender").toString(); + String age = kakao_account.get("age").toString(); + + userInfo.put("nickname", nickname); + userInfo.put("email", email); + userInfo.put("gender", gender); + userInfo.put("age", age); + + 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..61a0500 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java @@ -0,0 +1,58 @@ +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 org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Optional; + +@Service +public class UserService { + + @Autowired + UserRepository userRepository; + + KakaoService kakaoService; + + public void userValidation(HashMap userInfo) { + Optional user= userRepository.findById((Long) userInfo.get("userId")); + if (user.isEmpty()) { + userRegister(userInfo); + } + } + + + //유저 회원가입 + public void userRegister(HashMap userInfo) { + //필수 + String nickName = userInfo.get("nickname").toString(); + //필수 + String email = userInfo.get("email").toString(); + + Optional gender = Optional.ofNullable(userInfo.get("gender").toString()); + Optional age = Optional.ofNullable(userInfo.get("age").toString()); + + + + userRepository.save( + User.builder() + .nickname(nickName) + .email(email) + //TODO : Gender 결정 + .gender(Gender.FEMALE) + //TODO : Age 결정 + .age(Age.AGE10) + .townCert(YesNo.NO) + .townCertCnt(0) + .infoCert(YesNo.NO) + .build() + ); + } +} 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..427a8bd --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java @@ -0,0 +1,53 @@ +package com.umc.DongnaeFriend.global.util; + +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.security.core.Authentication; +import org.springframework.stereotype.Component; + +import java.util.Date; + +import static com.umc.DongnaeFriend.config.JwtConfig.SECRET_KEY; + +@Log4j2 +@Component +public class JwtTokenProvider { + + @Autowired + private UserRepository userRepository; + + public JwtTokenProvider(UserRepository userRepository) { + 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) { + 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(SECRET_KEY)) + .claim("userId", userId) + .setIssuedAt(now) //token 발행 시간 + .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..84ea9b2 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java @@ -0,0 +1,39 @@ +package com.umc.DongnaeFriend.global.util; + +import com.umc.DongnaeFriend.global.exception.CustomException; +import com.umc.DongnaeFriend.global.exception.ErrorCode; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.*; + +import static com.umc.DongnaeFriend.config.JwtConfig.SECRET_KEY; + +public class JwtUtil { + + public static Long getUserIdFromToken(String token) { + Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); + return Long.parseLong(claims.get("userId").toString()); + } + + //token 유효성 검증 + public static Boolean validateToken(String token) { + try { + Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); + return true; + } catch (SignatureException ex) { + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (MalformedJwtException ex) { + log.error("Invalid JWT token"); + } catch (ExpiredJwtException ex) { + throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); + } catch (UnsupportedJwtException ex) { + log.error("Unsupported JWT token"); + } catch (IllegalArgumentException ex) { + log.error("JWT claims string is empty."); + } catch (NullPointerException ex){ + log.error("JWT is empty"); + } catch (IllegalStateException e) { + log.info("JWT is illegal"); + } + return false; + } +} diff --git a/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java b/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java new file mode 100644 index 0000000..df0fd76 --- /dev/null +++ b/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java @@ -0,0 +1,45 @@ +package com.umc.DongnaeFriend.security; + +import com.umc.DongnaeFriend.global.util.JwtUtil; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +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; + +public class JwtTokenFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + // Request Header에서 JWT 토큰 가져오기 + String authorizationHeader = request.getHeader("Authorization"); + + // JWT 토큰이 "Bearer "로 시작하는지 확인하고 토큰 추출 + if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { + String token = authorizationHeader.substring(7); + + try { + // JWT 토큰 검증 + JwtUtil.validateToken(token); + + // JWT 토큰에서 사용자 정보 추출 (예: 사용자 ID) + Long userId = JwtUtil.getUserIdFromToken(token); + + // 인증 객체 생성 + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(userId, null, null); + + // SecurityContextHolder에 인증 객체 저장 + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } catch (Exception e) { + // JWT 토큰 검증 실패 시, 인증 객체를 null로 설정 + SecurityContextHolder.clearContext(); + } + } + + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 04772a2..588b310 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: @@ -18,4 +12,13 @@ spring: password: qwe335577! driver-class-name: com.mysql.cj.jdbc.Driver jpa: - hibernate.ddl-auto: update \ No newline at end of file + hibernate.ddl-auto: update + properties: + hibernate: + format_sql: true + show_sql: true + + + +jwt: + secret-key: 6B64DCA4EA2F53EDIKU9AAB215FE7 \ No newline at end of file From 35a0121e62df0369dbc9c859896774612c501a7b Mon Sep 17 00:00:00 2001 From: DDonghyeo Date: Mon, 24 Jul 2023 22:26:09 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=A5=95Feat=20#23:=20[OAuth2]=20?= =?UTF-8?q?=EC=86=8C=EC=85=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/contorller/UserController.java | 4 +++- .../domain/user/service/UserService.java | 1 + .../umc/DongnaeFriend/global/util/JwtUtil.java | 16 ++-------------- src/main/resources/application.yml | 4 ++-- 4 files changed, 8 insertions(+), 17 deletions(-) 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 index be5b883..b5b9805 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java @@ -49,7 +49,9 @@ public ResponseEntity userLogin(@RequestBody UserDto.Request request) { } @PostMapping("/user/reissuance") - public ResponseEntity reiussnaceToken(String access_oto) + public ResponseEntity reiussnaceToken(String access_oto) { + return null; + } 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 index 61a0500..3e16261 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java @@ -37,6 +37,7 @@ public void userRegister(HashMap userInfo) { String email = userInfo.get("email").toString(); Optional gender = Optional.ofNullable(userInfo.get("gender").toString()); + Optional age = Optional.ofNullable(userInfo.get("age").toString()); diff --git a/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java index 84ea9b2..09f0e95 100644 --- a/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java @@ -19,21 +19,9 @@ public static Boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; - } catch (SignatureException ex) { + } catch (SignatureException | IllegalArgumentException | MalformedJwtException | ExpiredJwtException | + UnsupportedJwtException | NullPointerException | IllegalStateException ex) { throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); - } catch (MalformedJwtException ex) { - log.error("Invalid JWT token"); - } catch (ExpiredJwtException ex) { - throw new CustomException(ErrorCode.INVALID_AUTH_TOKEN); - } catch (UnsupportedJwtException ex) { - log.error("Unsupported JWT token"); - } catch (IllegalArgumentException ex) { - log.error("JWT claims string is empty."); - } catch (NullPointerException ex){ - log.error("JWT is empty"); - } catch (IllegalStateException e) { - log.info("JWT is illegal"); } - return false; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 588b310..857383e 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,8 +8,8 @@ logging: spring: datasource: url: jdbc:mysql://localhost:3306/dongnae?characterEncoding=UTF-8&serverTimezone=UTC&useLegacyDatetimeCode=false - username: root - password: qwe335577! + username: + password: driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate.ddl-auto: update From 921e7a8e5b0406a28186a4296ca2675242037e57 Mon Sep 17 00:00:00 2001 From: Limwngur Date: Wed, 26 Jul 2023 09:07:19 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9A=BDfeat#23:=20[Auth2]=20refresh=20Tok?= =?UTF-8?q?en=20=EC=83=9D=EC=84=B1,=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85?= =?UTF-8?q?,=20AccessToken=EC=9E=AC=EB=B0=9C=EA=B8=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/contorller/UserController.java | 23 +++++++-- .../user/repository/UserRepository.java | 2 + .../domain/user/service/UserService.java | 47 ++++++++++++++++--- .../global/util/JwtTokenProvider.java | 13 +++++ .../security/JwtTokenFilter.java | 17 ++++++- 5 files changed, 89 insertions(+), 13 deletions(-) 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 index b5b9805..d8d0374 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java @@ -6,8 +6,9 @@ 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.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import java.io.IOException; @@ -15,6 +16,7 @@ @RestController @RequestMapping("/user") +@Slf4j public class UserController { @Autowired @@ -25,6 +27,8 @@ public class UserController { JwtTokenProvider jwtTokenProvider; + + /** * 유저 로그인 / 회원가입 * 인증 절차 @@ -32,6 +36,7 @@ public class UserController { @PostMapping("/login") public ResponseEntity userLogin(@RequestBody UserDto.Request request) { try { + log.info("userLogin 진입"); //사용자 정보 가져오기 HashMap userInfo = kakaoService.getUserInfo(request.getAccessToken()); @@ -39,8 +44,8 @@ public ResponseEntity userLogin(@RequestBody UserDto.Request request) { userService.userValidation(userInfo); //토큰 생성 - String access_token = jwtTokenProvider.createAccessToken((Long) userInfo.get("usreId")); - + String access_token = jwtTokenProvider.createAccessToken((Long) userInfo.get("userId")); + log.info("access_token : {}", access_token); return ResponseEntity.ok(access_token); } catch (IOException e) { @@ -49,8 +54,16 @@ public ResponseEntity userLogin(@RequestBody UserDto.Request request) { } @PostMapping("/user/reissuance") - public ResponseEntity reiussnaceToken(String access_oto) { - return null; + 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/repository/UserRepository.java b/src/main/java/com/umc/DongnaeFriend/domain/user/repository/UserRepository.java index 869602d..5edd72b 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 @@ -8,4 +8,6 @@ public interface UserRepository extends JpaRepository { Optional findById(Long id); + + Optional findByRefreshToken(String refresh_token); } 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 index 3e16261..3f7e6f1 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java @@ -3,17 +3,21 @@ 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.io.IOException; import java.util.HashMap; import java.util.Optional; @Service +@Slf4j public class UserService { @Autowired @@ -21,8 +25,11 @@ public class UserService { KakaoService kakaoService; + JwtTokenProvider jwtTokenProvider; + public void userValidation(HashMap userInfo) { Optional user= userRepository.findById((Long) userInfo.get("userId")); + if (user.isEmpty()) { userRegister(userInfo); } @@ -37,23 +44,51 @@ public void userRegister(HashMap userInfo) { String email = userInfo.get("email").toString(); 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("usreId")); userRepository.save( User.builder() .nickname(nickName) .email(email) - //TODO : Gender 결정 - .gender(Gender.FEMALE) - //TODO : Age 결정 - .age(Age.AGE10) + //TODO : Gender 결정[O] + .gender(Gender.valueOf(strGender)) + //TODO : Age 결정[O] + .age(Age.valueOf(ageRange[0]+"대")) .townCert(YesNo.NO) .townCertCnt(0) .infoCert(YesNo.NO) + .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/util/JwtTokenProvider.java b/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java index 427a8bd..85fd74b 100644 --- a/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java @@ -7,6 +7,7 @@ import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; +import javax.persistence.Id; import java.util.Date; import static com.umc.DongnaeFriend.config.JwtConfig.SECRET_KEY; @@ -46,6 +47,18 @@ public String createAccessToken(Long userId) { .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.ES512, String.valueOf(SECRET_KEY)) + .claim("userId", userId) + .setIssuedAt(now) + .setExpiration(validity) + .compact(); + } diff --git a/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java b/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java index df0fd76..76834db 100644 --- a/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java +++ b/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java @@ -1,6 +1,8 @@ package com.umc.DongnaeFriend.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.context.SecurityContextHolder; import org.springframework.web.filter.OncePerRequestFilter; @@ -11,20 +13,27 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; +@Configuration +@Slf4j public class JwtTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + + log.info("JwtTOkenFilter 진입"); + // Request Header에서 JWT 토큰 가져오기 String authorizationHeader = request.getHeader("Authorization"); + log.info("authorizationHeader : {}",authorizationHeader); - // JWT 토큰이 "Bearer "로 시작하는지 확인하고 토큰 추출 + //🛑 첫 로그인 시에도 이곳에서 걸리기 때문에 로그인이 안됨.(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); @@ -35,7 +44,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // SecurityContextHolder에 인증 객체 저장 SecurityContextHolder.getContext().setAuthentication(authenticationToken); + + } catch (Exception e) { + log.info("예외발생"); + // JWT 토큰 검증 실패 시, 인증 객체를 null로 설정 SecurityContextHolder.clearContext(); } From 2e33b3a31264645229bb1d94540f7b349dac05c2 Mon Sep 17 00:00:00 2001 From: DDonghyeo Date: Thu, 27 Jul 2023 23:43:38 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8FFeat=20#23:=20[Securit?= =?UTF-8?q?y]=20User=20Security=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umc/DongnaeFriend/config/JwtConfig.java | 2 +- .../DongnaeFriend/config/SecurityConfig.java | 4 +- .../accountBookSharingController.java | 3 +- .../repository/SharingBoardRepository.java | 1 - .../controller/DongnaeBoardController.java | 5 ++ .../user/contorller/UserController.java | 23 ++++-- .../domain/user/dto/UserDto.java | 2 + .../domain/user/entity/User.java | 2 + .../user/repository/UserRepository.java | 2 + .../domain/user/service/KakaoService.java | 4 + .../domain/user/service/KakaoServiceimpl.java | 17 ++-- .../domain/user/service/UserService.java | 82 +++++++++++++------ .../exception/GlobalExceptionHandler.java | 14 ++-- .../{ => global}/security/JwtTokenFilter.java | 36 ++++++-- .../global/util/JwtTokenProvider.java | 16 ++-- .../DongnaeFriend/global/util/JwtUtil.java | 48 +++++++++-- src/main/resources/application.yml | 6 +- 17 files changed, 190 insertions(+), 77 deletions(-) rename src/main/java/com/umc/DongnaeFriend/{ => global}/security/JwtTokenFilter.java (70%) diff --git a/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java b/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java index af15fc2..902da00 100644 --- a/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java +++ b/src/main/java/com/umc/DongnaeFriend/config/JwtConfig.java @@ -7,6 +7,6 @@ public class JwtConfig { @Value("${jwt.secret-key}") - public static String 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 index 6931738..865fbbf 100644 --- a/src/main/java/com/umc/DongnaeFriend/config/SecurityConfig.java +++ b/src/main/java/com/umc/DongnaeFriend/config/SecurityConfig.java @@ -1,6 +1,7 @@ package com.umc.DongnaeFriend.config; -import com.umc.DongnaeFriend.security.JwtTokenFilter; + +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; @@ -30,4 +31,3 @@ protected void configure(HttpSecurity http) throws Exception { // 나머지 코드는 이전 예제와 동일 } - 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 b168f28..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,6 +3,7 @@ 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; @@ -14,6 +15,7 @@ * [ 가계부 공유 ] * */ +@Slf4j @RestController @RequestMapping("/town-information") public class DongnaeBoardController { @@ -53,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 index d8d0374..cc30211 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/contorller/UserController.java @@ -8,9 +8,12 @@ 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; @@ -25,6 +28,7 @@ public class UserController { @Autowired UserService userService; + @Autowired JwtTokenProvider jwtTokenProvider; @@ -34,19 +38,24 @@ public class UserController { * 인증 절차 */ @PostMapping("/login") - public ResponseEntity userLogin(@RequestBody UserDto.Request request) { + 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(request.getAccessToken()); + HashMap userInfo = kakaoService.getUserInfo(accessToken); //사용자 확인 기존 회원 -> 넘어가고, 없는 회원 -> 회원가입 - userService.userValidation(userInfo); - //토큰 생성 - String access_token = jwtTokenProvider.createAccessToken((Long) userInfo.get("userId")); - log.info("access_token : {}", access_token); - return ResponseEntity.ok(access_token); + UserDto.Response response = userService.userValidation(userInfo); + + return ResponseEntity.ok(response); } catch (IOException e) { throw new CustomException(ErrorCode.INVALID_AUTH_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 index 469dcdb..0ebf5bc 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/dto/UserDto.java @@ -1,6 +1,7 @@ package com.umc.DongnaeFriend.domain.user.dto; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; public class UserDto { @@ -16,6 +17,7 @@ public static class Request { } @Getter + @Builder @AllArgsConstructor public static class Response { 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 b6e8772..201db2e 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 @@ -29,6 +30,7 @@ public class User extends BaseTimeEntity { @ManyToOne(fetch = LAZY) @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 5edd72b..d6d323a 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 @@ -10,4 +10,6 @@ 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 index a4b2aaf..d3cd29f 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoService.java @@ -1,11 +1,15 @@ 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 index 4955e10..6c147f5 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/KakaoServiceimpl.java @@ -2,6 +2,7 @@ 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; @@ -12,6 +13,7 @@ import java.util.HashMap; import java.util.Map; +@Slf4j @Service public class KakaoServiceimpl implements KakaoService { @@ -52,23 +54,26 @@ public HashMap getUserInfo(String access_Token) throws IOExcepti 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"); - // System.out.println(properties.get("nickname")); - // System.out.println(kakao_account.get("email")); + 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(); - String gender = kakao_account.get("gender").toString(); - String age = kakao_account.get("age").toString(); + userInfo.put("id", id); userInfo.put("nickname", nickname); + userInfo.put("profileImage", profileImage); userInfo.put("email", email); - userInfo.put("gender", gender); - userInfo.put("age", age); + return userInfo; } 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 index 3f7e6f1..40e600c 100644 --- a/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java +++ b/src/main/java/com/umc/DongnaeFriend/domain/user/service/UserService.java @@ -3,6 +3,7 @@ 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; @@ -25,53 +26,80 @@ public class UserService { KakaoService kakaoService; + @Autowired JwtTokenProvider jwtTokenProvider; - public void userValidation(HashMap userInfo) { - Optional user= userRepository.findById((Long) userInfo.get("userId")); + public UserDto.Response userValidation(HashMap userInfo) { + Long kakao_id = (Long) userInfo.get("id"); + Optional user= userRepository.findByKakaoId(kakao_id); if (user.isEmpty()) { - userRegister(userInfo); + 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(); } + } - //유저 회원가입 - public void userRegister(HashMap userInfo) { + //유저 회원가입 -> Refresh Token을 return + public User userRegister(HashMap userInfo) { //필수 String nickName = userInfo.get("nickname").toString(); //필수 String email = userInfo.get("email").toString(); - 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("usreId")); - - userRepository.save( + 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( +// Gender.valueOf(strGender) + Gender.MALE + ) //TODO : Age 결정[O] - .age(Age.valueOf(ageRange[0]+"대")) + .age( +// Age.valueOf(ageRange[0]+"대") + Age.AGE20 + ) .townCert(YesNo.NO) .townCertCnt(0) .infoCert(YesNo.NO) + .profileImage(profileImage) + .kakaoId(kakaoId) .refreshToken(refresh_Token) .build() ); @@ -91,4 +119,6 @@ public String createAccessTokenFromRefreshToken(String refreshToken) { 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 986c73d..37fb335 100644 --- a/src/main/java/com/umc/DongnaeFriend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/umc/DongnaeFriend/global/exception/GlobalExceptionHandler.java @@ -30,11 +30,11 @@ protected ResponseEntity handleMethodArgumentNotValidException(MethodArgument } //일반 예외처리 - @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/security/JwtTokenFilter.java b/src/main/java/com/umc/DongnaeFriend/global/security/JwtTokenFilter.java similarity index 70% rename from src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java rename to src/main/java/com/umc/DongnaeFriend/global/security/JwtTokenFilter.java index 76834db..6c0cb55 100644 --- a/src/main/java/com/umc/DongnaeFriend/security/JwtTokenFilter.java +++ b/src/main/java/com/umc/DongnaeFriend/global/security/JwtTokenFilter.java @@ -1,9 +1,10 @@ -package com.umc.DongnaeFriend.security; +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; @@ -17,10 +18,22 @@ @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 진입"); + log.info("JwtTokenFilter 진입"); + + if (request.getServletPath().contains("/user/login")) { + log.info("/user/login 진입"); + } // Request Header에서 JWT 토큰 가져오기 String authorizationHeader = request.getHeader("Authorization"); @@ -29,30 +42,37 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse //🛑 첫 로그인 시에도 이곳에서 걸리기 때문에 로그인이 안됨.(null) if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { String token = authorizationHeader.substring(7); - log.info("token : {}",token); + log.info("token : {}", token); try { // JWT 토큰 검증 - JwtUtil.validateToken(token); + jwtUtil.validateToken(token); log.info("JWT 토큰 검증완료"); // JWT 토큰에서 사용자 정보 추출 (예: 사용자 ID) - Long userId = JwtUtil.getUserIdFromToken(token); + Long userId = jwtUtil.getUserIdFromToken(token); // 인증 객체 생성 UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(userId, null, null); + new UsernamePasswordAuthenticationToken( userId,null, null); // SecurityContextHolder에 인증 객체 저장 + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + filterChain.doFilter(request, response); + } catch (Exception e) { - log.info("예외발생"); + 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 index 85fd74b..344ba00 100644 --- a/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtTokenProvider.java @@ -1,25 +1,27 @@ 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.security.core.Authentication; import org.springframework.stereotype.Component; -import javax.persistence.Id; import java.util.Date; -import static com.umc.DongnaeFriend.config.JwtConfig.SECRET_KEY; + @Log4j2 @Component public class JwtTokenProvider { + private final JwtConfig jwtConfig; + @Autowired private UserRepository userRepository; - public JwtTokenProvider(UserRepository userRepository) { + public JwtTokenProvider(JwtConfig jwtConfig, UserRepository userRepository) { + this.jwtConfig = jwtConfig; this.userRepository = userRepository; } @@ -29,6 +31,8 @@ public JwtTokenProvider(UserRepository userRepository) { //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); @@ -40,7 +44,7 @@ public String createAccessToken(Long userId) { // claims.put("email", user.getEmail()); // 사용자 이메일 return Jwts.builder() - .signWith(SignatureAlgorithm.HS512, String.valueOf(SECRET_KEY)) + .signWith(SignatureAlgorithm.HS512, String.valueOf(jwtConfig.SECRET_KEY)) .claim("userId", userId) .setIssuedAt(now) //token 발행 시간 .setExpiration(validity) @@ -53,7 +57,7 @@ public String createRefreshToken(Long userId) { Date validity = new Date(now.getTime() + REFRESH_TOKEN_EXPIRE_LENGTH); return Jwts.builder() - .signWith(SignatureAlgorithm.ES512, String.valueOf(SECRET_KEY)) + .signWith(SignatureAlgorithm.HS512, String.valueOf(jwtConfig.SECRET_KEY)) .claim("userId", userId) .setIssuedAt(now) .setExpiration(validity) diff --git a/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java index 09f0e95..a1c15b9 100644 --- a/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java +++ b/src/main/java/com/umc/DongnaeFriend/global/util/JwtUtil.java @@ -1,26 +1,60 @@ 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; -import static com.umc.DongnaeFriend.config.JwtConfig.SECRET_KEY; +@Slf4j +@Component public class JwtUtil { - public static Long getUserIdFromToken(String token) { - Claims claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody(); + 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 static Boolean validateToken(String token) { + public Boolean validateToken(String token) { try { - Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); + log.info("SECRET KEY :"+ jwtConfig.SECRET_KEY); + + Jwts.parser().setSigningKey(jwtConfig.SECRET_KEY).parseClaimsJws(token); return true; - } catch (SignatureException | IllegalArgumentException | MalformedJwtException | ExpiredJwtException | - UnsupportedJwtException | NullPointerException | IllegalStateException ex) { + } 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 857383e..c0bae2f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,8 +8,8 @@ logging: spring: datasource: url: jdbc:mysql://localhost:3306/dongnae?characterEncoding=UTF-8&serverTimezone=UTC&useLegacyDatetimeCode=false - username: - password: + username: root + password: qwe335577! driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate.ddl-auto: update @@ -18,7 +18,5 @@ spring: format_sql: true show_sql: true - - jwt: secret-key: 6B64DCA4EA2F53EDIKU9AAB215FE7 \ No newline at end of file