03.02 Mini_Project 서버 및 로그인, 회원가입 (추가적으로 스케줄 완료, 취소) 기능 구현
2023. 3. 2. 03:09ㆍ개발일지
이번에 Mini_Project에서 제가 개발한 부분과 팀원 부재로 추가적으로 개발한 스케줄 완료, 취소 입니다.
Controller
package com.sparta.schedule.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.sparta.schedule.dto.request.UserRequestDto;
import com.sparta.schedule.dto.response.MessageResponseDto;
import com.sparta.schedule.dto.response.UserResponseDto;
import com.sparta.schedule.jwt.JwtUtil;
import com.sparta.schedule.service.KakaoService;
import com.sparta.schedule.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/user",produces = "application/json")
public class UserController {
private final UserService userService;
private final KakaoService kakaoService;
@PostMapping("/login")
public ResponseEntity<UserResponseDto> login(@RequestBody UserRequestDto userRequestDto){
return userService.login(userRequestDto);
}
@GetMapping("/idCheck")
public ResponseEntity<MessageResponseDto> idCheck(@RequestBody UserRequestDto userRequestDto){
return userService.idCheck(userRequestDto);
}
@PostMapping("/signup")
public ResponseEntity<MessageResponseDto>signup(@Valid @RequestBody UserRequestDto userRequestDto){
return userService.signup(userRequestDto);
}
@PostMapping("/kakao/login")
public String kakaoLogin(@RequestParam String code, HttpServletResponse response)throws JsonProcessingException {
// code : 카카오 서버로부터 받은 인가 코드
String createToken = kakaoService.kakaoLogin(code, response);
// Cookie 생성 및 직접 브라우저에 Set
Cookie cookie = new Cookie(JwtUtil.AUTHORIZATION_HEADER, createToken.substring(7));
cookie.setPath("/");
response.addCookie(cookie);
return "redirect:/15.164.158.158:8080/date";
}
}
patchComplete(Controller)
@PatchMapping("/schedule/complete/{id}")
public ResponseEntity<CompleteResponseDto> patchComplete(@PathVariable Long id,
@RequestBody CompleteRequestDto completeRequestDto,
@AuthenticationPrincipal UserDetailsImpl userDetails){
return scheduleService.patchComplete(id, completeRequestDto, userDetails);
}
@PatchMapping("/schedule/cancel/{id}")
public ResponseEntity<CompleteResponseDto> cancelComplete(@PathVariable Long id,
@RequestBody CompleteRequestDto completeRequestDto,
@AuthenticationPrincipal UserDetailsImpl userDetails){
return scheduleService.cancelComplete(id, completeRequestDto, userDetails);
}
UserRequestDto
package com.sparta.schedule.dto.request;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.validation.constraints.Pattern;
@Getter
@RequiredArgsConstructor
public class UserRequestDto {
@Pattern(regexp = "^(?:\\w+\\.?)*\\w+@(?:\\w+\\.)+\\w+$", message = "이메일 형식이 올바르지 않습니다.")
private String email;
@Pattern(regexp = "^[a-zA-Z0-9]{8,15}$",message = "비밀번호는 8~15자리 영문 대소문자,숫자를 포함시켜주세요")
private String password;
private String username;
}
UserResponseDto
package com.sparta.schedule.dto.response;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public class UserResponseDto {
private String msg;
private int code;
private String username;
@Builder
public UserResponseDto(String msg, int code, String username) {
this.msg = msg;
this.username = username;
this.code = code;
}
// HttpStatus 상태 입력으로 Dto 만들기
public static UserResponseDto User_ServiceCode(HttpStatus status, String msg, String username){
return UserResponseDto.builder()
.code(status.value())
.username(username)
.msg(msg)
.build();
}
// code입력으로 Dto 만들기
public static UserResponseDto jwt_filter(int code, String msg, String username){
return UserResponseDto.builder()
.code(code)
.msg(msg)
.build();
}
}
KakaoUserInfoDto
package com.sparta.schedule.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@NoArgsConstructor
public class KakaoUserInfoDto {
private Long id;
private String email;
private String nickname;
public KakaoUserInfoDto(Long id, String email, String nickname) {
this.id = id;
this.email = email;
this.nickname = nickname;
}
}
User
package com.sparta.schedule.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Getter
@Entity(name = "users")
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
@Column(nullable = false)
private String username;
private Long kakaoId;
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
@Builder
public User(String username, String password, String email, Long kakaoId, UserRoleEnum role) {
this.username = username;
this.password = password;
this.email = email;
this.kakaoId = kakaoId;
this.role = role;
}
public static User user_service(String username, String password, String email, UserRoleEnum role){
return User.builder()
.username(username)
.password(password)
.role(role)
.email(email)
.build();
}
public User kakaoIdUpdate(Long kakaoId){
this.kakaoId = kakaoId;
return this;
}
}
UserRoleEnum
package com.sparta.schedule.entity;
public enum UserRoleEnum {
USER(Authority.USER), // 사용자 권한
ADMIN(Authority.ADMIN); // 관리자 권한
private final String authority;
UserRoleEnum(String authority) {
this.authority = authority;
}
public String getAuthority() {
return this.authority;
}
public static class Authority {
public static final String USER = "ROLE_USER";
public static final String ADMIN = "ROLE_ADMIN";
}
}
JwtAuthFilter
package com.sparta.schedule.jwt;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.schedule.dto.response.MessageResponseDto;
import com.sparta.schedule.exception.ErrorCode;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
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;
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {
private final JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// request 에 담긴 토큰을 가져온다.
String token = jwtUtil.resolveToken(request);
// 토큰이 null 이면 다음 필터로 넘어간다.
if (token == null) {
// jwtExceptionHandler(response, ErrorType.NOT_VALID_TOKEN);
filterChain.doFilter(request, response);
return;
}
// 토큰이 유효하지 않으면 예외처리
if (!jwtUtil.validateToken(token)) {
// throw new JwtException(ErrorType.NOT_VALID_TOKEN);
jwtExceptionHandler(response, ErrorCode.NOT_VALID_TOKEN);
return;
}
// 유효한 토큰이라면, 토큰으로부터 사용자 정보를 가져온다.
Claims info = jwtUtil.getUserInfoFromToken(token);
setAuthentication(info.getSubject()); // 사용자 정보로 인증 객체 만들기
// 다음 필터로 넘어간다.
filterChain.doFilter(request, response);
}
private void setAuthentication(String username) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication = jwtUtil.createAuthentication(username); // 인증 객체 만들기
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
// 토큰에 대한 오류가 발생했을 때, 커스터마이징해서 Exception 처리 값을 클라이언트에게 알려준다.
public void jwtExceptionHandler(HttpServletResponse response, ErrorCode error) {
response.setStatus(error.getCode());
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
try {
String json = new ObjectMapper().writeValueAsString(MessageResponseDto.jwt_filter(error.getCode(), error.getMessage()));
response.getWriter().write(json);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
JwtUtil
package com.sparta.schedule.jwt;
import com.sparta.schedule.entity.UserRoleEnum;
import com.sparta.schedule.security.UserDetailsServiceImpl;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecurityException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {
private final UserDetailsServiceImpl userDetailsService;
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String AUTHORIZATION_KEY = "auth";
private static final String BEARER_PREFIX = "Bearer ";
private static final long TOKEN_TIME = 60 * 60 * 1000L;
@Value("${jwt.secret.key}")
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey);
key = Keys.hmacShaKeyFor(bytes);
}
// header 토큰을 가져오기
public String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7);
}
return null;
}
// 토큰 생성
public String createToken(String username, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder()
.setSubject(username)
.claim(AUTHORIZATION_KEY, role)
.setExpiration(new Date(date.getTime() + TOKEN_TIME))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
}
// 토큰 검증
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException e) {
log.info("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
} catch (ExpiredJwtException e) {
log.info("Expired JWT token, 만료된 JWT token 입니다.");
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
} catch (IllegalArgumentException e) {
log.info("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
}
return false;
}
// 토큰에서 사용자 정보 가져오기
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
// 인증 객체 생성
public Authentication createAuthentication(String username) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
}
}
UserDetailsImpl
package com.sparta.schedule.security;
import com.sparta.schedule.entity.User;
import com.sparta.schedule.entity.UserRoleEnum;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
public class UserDetailsImpl implements UserDetails {
private final User user;
private final String username;
// 인증이 완료된 사용자 추가하기
public UserDetailsImpl(User user, String username) {
this.user = user;
this.username = username;
}
public User getUser() {
return user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
UserRoleEnum role = user.getRole();
String authority = role.getAuthority();
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(authority);
Collection<GrantedAuthority> authorities = new ArrayList<>(); // 사용자 권한을 GrantedAuthority 로 추상화
authorities.add(simpleGrantedAuthority);
return authorities; // GrantedAuthority 로 추상화된 사용자 권한 반환
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return false;
}
}
UserDetailsServiceImpl
package com.sparta.schedule.security;
import com.sparta.schedule.entity.User;
import com.sparta.schedule.exception.ErrorCode;
import com.sparta.schedule.repository.UserRepository;
import lombok.RequiredArgsConstructor;
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;
@Service // custom 하고 Bean 으로 등록 후 사용 가능
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UsernameNotFoundException(ErrorCode.NOT_FOUND_USER.getMessage())); // 사용자가 DB 에 없으면 예외처리
return new UserDetailsImpl(user, user.getEmail()); // 사용자 정보를 UserDetails 로 반환
}
}
UserRepository
package com.sparta.schedule.repository;
import com.sparta.schedule.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface UserRepository extends JpaRepository<User,Long> {
Optional<User> findByKakaoId(Long id);
Optional<User> findByEmail(String email);
}
ScheduleRepository
package com.sparta.schedule.repository;
import com.sparta.schedule.entity.CalendarDate;
import com.sparta.schedule.entity.Schedule;
import com.sparta.schedule.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
import java.util.Optional;
public interface ScheduleRepository extends JpaRepository<Schedule, Long> {
List<Schedule> findAllByCalendarDate(CalendarDate calendarDate);
// Optional<Schedule> findByIdAndDate(Long id, String date);
Optional<Schedule> findByIdAndUser(Long id, User user);
Optional<Schedule> findByComplete(boolean complete); // 이 부분입니다.
}
UserService
package com.sparta.schedule.service;
import com.sparta.schedule.dto.response.MessageResponseDto;
import com.sparta.schedule.dto.request.UserRequestDto;
import com.sparta.schedule.dto.response.UserResponseDto;
import com.sparta.schedule.entity.User;
import com.sparta.schedule.entity.UserRoleEnum;
import com.sparta.schedule.exception.ApiException;
import com.sparta.schedule.exception.ErrorCode;
import com.sparta.schedule.jwt.JwtUtil;
import com.sparta.schedule.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
// 회원가입
@Transactional
public ResponseEntity<MessageResponseDto> signup(UserRequestDto userRequestDto) {
String email = userRequestDto.getEmail();
String password = passwordEncoder.encode(userRequestDto.getPassword());
String username = userRequestDto.getUsername();
// 회원 중복 확인
Optional<User> found = userRepository.findByEmail(email);
if(found.isPresent()){
throw new ApiException(ErrorCode.DUPLICATED_USERNAME);
}
userRepository.save(User.user_service(username,password,email, UserRoleEnum.USER));
return ResponseEntity.ok(MessageResponseDto.User_ServiceCode(HttpStatus.OK, "회원가입 성공"));
}
// 중복 회원 검사 (버튼)
public ResponseEntity<MessageResponseDto>idCheck(UserRequestDto userRequestDto){
String email = userRequestDto.getEmail();
Optional<User> users = userRepository.findByEmail(email);
// 중복 회원 일 경우
if(users.isPresent()){
throw new IllegalArgumentException("중복 회원입니다.");
}
// 중복 회원이 아닐 경우 아이디 사용 가능
return ResponseEntity.ok(MessageResponseDto.User_ServiceCode(HttpStatus.OK,"아이디 사용가능합니다."));
}
@Transactional
public ResponseEntity<UserResponseDto> login(UserRequestDto userRequestDto) {
String email = userRequestDto.getEmail();
String password = userRequestDto.getPassword();
// 사용자 확인 및 비밀번호 확인
Optional<User> user = userRepository.findByEmail(email);
if(user.isEmpty() || !passwordEncoder.matches(password, user.get().getPassword())){
throw new ApiException(ErrorCode.NOT_MATCHING_INFO);
}
HttpHeaders headers = new HttpHeaders();
headers.set(JwtUtil.AUTHORIZATION_HEADER, jwtUtil.createToken(user.get().getEmail(), user.get().getRole()));
return ResponseEntity.ok()
.headers(headers)
.body(UserResponseDto.User_ServiceCode(HttpStatus.OK, "로그인 성공", user.get().getUsername()));
// header에 들어갈 JWT 세팅 body에다가 넣는거
// String jwtUtil2 = JwtUtil.AUTHORIZATION_HEADER+" "+ jwtUtil.createToken(user.get().getEmail(), user.get().getRole());
//
// return MessageResponseDto.builder()
// .code(HttpStatus.OK.value())
// .msg("성공")
// .jwtUtil(jwtUtil2)
// .build();
}
}
ScheduleService
@Transactional
public ResponseEntity<CompleteResponseDto> patchComplete(Long id,
CompleteRequestDto completeRequestDto,
UserDetailsImpl userDetails) {
Schedule schedule = scheduleRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("해당 일정이 없습니다.")
);
Schedule check = scheduleRepository.findByComplete(!completeRequestDto.isComplete()).orElseThrow(
() -> new IllegalArgumentException("완료가 되지 않았습니다.")
);
if (check.isComplete()) {
throw new IllegalStateException("이미 완료된 스케쥴입니다.");
}
schedule.updateCompleteStatus(completeRequestDto);
return ResponseEntity.ok().body(
CompleteResponseDto.User_ServiceCode(HttpStatus.OK, true, "스케쥴 완료")
);
}
@Transactional
public ResponseEntity<CompleteResponseDto> cancelComplete(Long id,
CompleteRequestDto completeRequestDto,
UserDetailsImpl userDetails) {
Schedule schedule = scheduleRepository.findById(id).orElseThrow(
() -> new IllegalArgumentException("해당 일정이 없습니다.")
);
Schedule check = scheduleRepository.findByComplete(!completeRequestDto.isComplete()).orElseThrow(
() -> new IllegalArgumentException("완료가 되지 않았습니다.")
);
if (!check.isComplete()) {
throw new IllegalStateException("완료가 취소된 스케쥴입니다.");
}
schedule.updateCompleteStatus(completeRequestDto);
return ResponseEntity.ok().body(
CompleteResponseDto.User_ServiceCode(HttpStatus.OK, false, "완료 취소")
);
}
private void foundUser(Long id, UserDetailsImpl userDetails) {
Optional<Schedule> found = scheduleRepository.findByIdAndUser(id, userDetails.getUser());
if (found.isEmpty() && userDetails.getUser().getRole() == UserRoleEnum.USER) {
throw new IllegalArgumentException("작성자가 일치하지 않습니다.");
}
}
KakaoService
package com.sparta.schedule.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sparta.schedule.dto.KakaoUserInfoDto;
import com.sparta.schedule.entity.User;
import com.sparta.schedule.entity.UserRoleEnum;
import com.sparta.schedule.jwt.JwtUtil;
import com.sparta.schedule.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;
@Slf4j
@Service
@RequiredArgsConstructor
public class KakaoService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final JwtUtil jwtUtil;
public String kakaoLogin(String code, HttpServletResponse response) throws JsonProcessingException {
// 1. "인가 코드"로 "액세스 토큰" 요청
String accessToken = getToken(code);
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
KakaoUserInfoDto kakaoUserInfo = getKakaoUserInfo(accessToken);
// 3. 필요시에 회원가입
User kakaoUser = registerKakaoUserIfNeeded(kakaoUserInfo);
// 4. JWT 토큰 반환
String createToken = jwtUtil.createToken(kakaoUser.getEmail(), kakaoUser.getRole());
response.addHeader(JwtUtil.AUTHORIZATION_HEADER, createToken);
return createToken;
}
// 1. "인가 코드"로 "액세스 토큰" 요청
private String getToken(String code) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP Body 생성
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", "94c5891ab6cec1f5eddede64f8358dd9");
body.add("redirect_uri", "http://15.164.158.158:8080/user/kakao/login");
body.add("code", code);
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoTokenRequest =
new HttpEntity<>(body, headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kauth.kakao.com/oauth/token",
HttpMethod.POST,
kakaoTokenRequest,
String.class
);
// HTTP 응답 (JSON) -> 액세스 토큰 파싱
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
return jsonNode.get("access_token").asText();
}
// ======================================================================================
// 2. 토큰으로 카카오 API 호출 : "액세스 토큰"으로 "카카오 사용자 정보" 가져오기
private KakaoUserInfoDto getKakaoUserInfo(String accessToken) throws JsonProcessingException {
// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
// HTTP 요청 보내기
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers);
RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
"https://kapi.kakao.com/v2/user/me",
HttpMethod.POST,
kakaoUserInfoRequest,
String.class
);
String responseBody = response.getBody();
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
Long id = jsonNode.get("id").asLong();
String nickname = jsonNode.get("properties")
.get("nickname").asText();
String email = jsonNode.get("kakao_account")
.get("email").asText();
log.info("카카오 사용자 정보: " + id + ", " + nickname + ", " + email);
return new KakaoUserInfoDto(id, nickname, email);
}
// ===============================================================================
// 3. 필요시에 회원가입
private User registerKakaoUserIfNeeded(KakaoUserInfoDto kakaoUserInfo) {
// DB 에 중복된 Kakao Id 가 있는지 확인
Long kakaoId = kakaoUserInfo.getId();
User kakaoUser = userRepository.findByKakaoId(kakaoId)
.orElse(null);
if (kakaoUser == null) {
// 카카오 사용자 email 동일한 email 가진 회원이 있는지 확인
String kakaoEmail = kakaoUserInfo.getEmail();
User sameEmailUser = userRepository.findByEmail(kakaoEmail).orElse(null);
if (sameEmailUser != null) {
kakaoUser = sameEmailUser;
// 기존 회원정보에 카카오 Id 추가
kakaoUser = kakaoUser.kakaoIdUpdate(kakaoId);
} else {
// 신규 회원가입
// password: random UUID
String password = UUID.randomUUID().toString();
String encodedPassword = passwordEncoder.encode(password);
// email: kakao email
String email = kakaoUserInfo.getEmail();
kakaoUser = new User(kakaoUserInfo.getNickname(),email, encodedPassword, kakaoId, UserRoleEnum.USER);
}
userRepository.save(kakaoUser);
}
return kakaoUser;
}
}
'개발일지' 카테고리의 다른 글
03.05 TIL (S3 이미지 File 변환 오류) (0) | 2023.03.05 |
---|---|
03.05 버킷설정 (0) | 2023.03.05 |
02.28 TIL (0) | 2023.02.28 |
02.27 Mini_Project 서버 및 로그인, 회원가입 기능 구현(CORS 문제 해결) (0) | 2023.02.27 |
02.25 CORS (0) | 2023.02.25 |