Hello

67. 카카오 로그인 서비스 구현 완료

by 볼빵빵오춘기

UserController

kakaoCallback() 작성하였던것을 실행시켜보면 아래와 같은 결과가 나오는 것을 확인가능하다.

아래 결과로 나온 정보를 Object에 담을 것이다.

 

OAuthToken

model 패키지 하위에 OAuthToken.java 생성한다.

package com.cos.blog.model;

import lombok.Data;

@Data
public class OAuthToken {
    private String access_token;
    private String token_type;
    private String refresh_token;
    private String scope;
    private int expires_in;
    private int refresh_token_expires_in;
}

 

UserController

  • return "카카오 토큰 요청 완료 : 토큰 요청에 대한 응답 : " + response; 을 Object에 담자!
  • 담을 때 Gson, Json Simple, ObjectMapper 여러 라이브러리가 있는데 우리는 ObjectMapper를 사용한다.
    (ObjectMapper를 사용이유는 쉽게 되어있고 기본적으로 내장되어있기 때문에 사용한다.)
// Gson, Json Simple, ObjectMapper
ObjectMapper objectMapper = new ObjectMapper();
OAuthToken oAuthToken = null;
try{
    oAuthToken = objectMapper.readValue(response.getBody(),OAuthToken.class);
}catch (JsonMappingException e){
    e.printStackTrace();
}catch (JsonProcessingException e){
    e.printStackTrace();
}

System.out.println("카카오 엑세스 토큰 : "+ oAuthToken.getAccess_token());

 


  • 토큰을 통한 사용자 정보 조회
  • RestTemplate rt = new RestTemplate(); ~ // response2 작성하는부분 까지 복사하여 이름 바꿔서 넣어준다.
  • HttpHeader 와 HttpBody를 하나의 오브젝트에 담을 때 params는 오버로딩 되므로 지운다.
  • 결과 복사해놓는다.
      // rt2
RestTemplate rt2 = new RestTemplate();

// HttpHeader 오브젝트 생성
HttpHeaders headers2 = new HttpHeaders();
headers2.add("Authorization","Bearer "+oAuthToken.getAccess_token());
headers2.add("Content-type","application/x-www-form-urlencoded;charset=utf-8");

System.out.println("headers2 : "+ headers2);

// HttpHeader 와 HttpBody를 하나의 오브젝트에 담기
HttpEntity<MultiValueMap<String,String>> kakaoProfileRequest =
        new HttpEntity<>(headers2); // params는 알아서 오버로딩 되므로 지움.

// Http 요청하기 - Post방식으로 - 그리고 response 변수의 응답 받음
ResponseEntity<String> response2 = rt2.exchange(
        "https://kapi.kakao.com/v2/user/me",
        HttpMethod.POST,
        kakaoProfileRequest,
        String.class
);

System.out.println("headers2 : "+ response2);
return response2.getBody();

 

jsonchema2pojo 이용

  • 해당 사이트는 받은 값을 통해 getter,setter를 만들어준다.
  • 복사한 부분 붙여놓고 오른쪽 radio와 checkbox를 체크를 한 후 밑에 preview 버튼을 누른다.
  • preview 에 나온 부분 복사한다.

KakaoProfile

jsonchema2pojo에서 preview 에 나온 부분 복사한 부분 붙여놓고 수정한다.

(복사한 부분은 외부클래스로 나와 내부 클래스로 보는게 눈에 편하므로 메인클래스내에 나머지 클래스를 내부클래스로 변경하였다.)

 

package com.cos.blog.model;

import lombok.Data;

@Data
public class KakaoProfile {
    public Long id;
    public String connected_at;
    public Properties properties;
    public KakaoAccount kakao_account;

    @Data
    public static class KakaoAccount {
        public Boolean profile_nickname_needs_agreement;
        public Profile profile;
        public Boolean has_email;
        public Boolean email_needs_agreement;
        public Boolean is_email_valid;
        public Boolean is_email_verified;
        public String email;
    }

    @Data
    public static class Profile {
        public String nickname;
    }

    @Data
    public static class Properties {
        public String nickname;
    }
}

 

UserController

// objectMapper2
ObjectMapper objectMapper2 = new ObjectMapper();
KakaoProfile kakaoProfile = null;
try{
    kakaoProfile = objectMapper2.readValue(response2.getBody(),KakaoProfile.class);
}catch (JsonMappingException e){
    e.printStackTrace();
}catch (JsonProcessingException e){
    e.printStackTrace();
}

System.out.println("카카오 아이디(번호)" + kakaoProfile.getId());
System.out.println("카카오 이메일" + kakaoProfile.getKakao_account().getEmail());

  • 비밀번호 UUID → cosKey로 나중에 변경하였다. 
  • cosKey는 비밀번호로써 카카오 로그인을 하면 DB에 비밀번호를 따로 입력 값이 없다. 
  • 따라서 임의로  쓰레기비밀번호를 넣어놓는다. 
// 카카오계정으로 로그인한
System.out.println("블로그 서버 유저네임 : " + kakaoProfile.getKakao_account().getEmail()+"_"+kakaoProfile.getId());
System.out.println("블로그 서버 이메일 : " + kakaoProfile.getKakao_account().getEmail());
UUID garbagePassword = UUID.randomUUID();
System.out.println("블로그 서버 패스워드 : " + garbagePassword);
// 카카오계정으로 로그인한
System.out.println("블로그 서버 유저네임 : " + kakaoProfile.getKakao_account().getEmail()+"_"+kakaoProfile.getId());
System.out.println("블로그 서버 이메일 : " + kakaoProfile.getKakao_account().getEmail());
System.out.println("블로그 서버 패스워드 : " + cosKey);

 


@Autowired
private UserService userService;
User kakaoUser = User.builder()
  .username(kakaoProfile.getKakao_account().getEmail()+"_"+kakaoProfile.getId())
  .password(garbagePassword.toString())
  .email(kakaoProfile.getKakao_account().getEmail())
  .build();

garbagePassword.toString() → cosKey 로 변경하였다.

해당 정보를 가져와서 가입완료를 시키면된다.

User kakaoUser = User.builder()
  .username(kakaoProfile.getKakao_account().getEmail()+"_"+kakaoProfile.getId())
  .password(cosKey)
  .email(kakaoProfile.getKakao_account().getEmail())
  .build();

 


가입완료를 하더라도 이미 가입된 자인지 아닌지 확인해서 가입을 시킨다.

// 가입자 혹은 비가입자 체크 해서 처리
User originUser = userService.회원찾기(kakaoUser.getUsername());
if(originUser==null) {
    System.out.println("기존 회원이 아니므로 자동 회원가입을 진행합니다.");
    userService.회원가입(kakaoUser);
}
// 가입자 혹은 비가입자 체크 해서 처리
User originUser = userService.회원찾기(kakaoUser.getUsername());
if(originUser.getUsername()==null) {
    System.out.println("기존 회원이 아니므로 자동 회원가입을 진행합니다.");
    userService.회원가입(kakaoUser);
}

 

UserService

@Transactional(readOnly = true)
public User 회원찾기(String username){
    User user = userRepository.findByUsername(username).orElseGet(()->{
        return new User();
    });
    return user;
}

 

UserController

@Autowired
private AuthenticationManager authenticationManager;
System.out.println("자동 로그인 진행합니다.");
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(kakaoUser.getUsername(),cosKey));
SecurityContextHolder.getContext().setAuthentication(authentication);

System.out.println("자동 로그인 완료");
return "redirect:/";

 

application.properties

cos.key 추가한다.

oauth 회원가입이 할 때 가입자들의 비밀번호이다. 따라서 절대 노출이되면 안된다.

cos.key=cos1234

 

UserController

@Value("${cos.key}")
private String cosKey;

 

여기까지 문제점은..

카카오 로그인 만들었다.

이 때, 여기서 문제점은 아이디를 조합해서 만들었지만 누군가에 DB에 침입하여 아이디와 비밀로 해두었던 내 cosKey 를 알고 로그인을 할수있다.

추가로 그 비밀번호를 변경한다면 해당 유저는 OAUTH 로그인 불가다.

 

User

private String oauth; // 소셜 로그인인지 일반 로그인인지 확인하기 위함.

 

UserController

User kakaoUser = User.builder()
      .username(kakaoProfile.getKakao_account().getEmail()+"_"+kakaoProfile.getId())
      .password(cosKey)
      .email(kakaoProfile.getKakao_account().getEmail())
      .oauth("kakao")
      .build();

 

updateForm.jsp

OAuth로 로그인 했을 시 비밀번호,email 변경 수정 불가하도록 변경하였다.

<c:if test="${empty principal.user.oauth}">
    <div class="form-group">
      <label for="password">Password:</label>
      <input type="password" class="form-control" id="password" placeholder="Enter password" name="password">
    </div>
</c:if>

 

UserService

OAuth로 로그인 했을 시 비밀번호,email 변경 수정 불가하도록 변경하였다.

// Validate 체크 => oauth 필드에 값이 없으면 수정 가능
if(persistance.getOauth() == null || persistance.getOauth().equals("")){
    String rawPassword = user.getPassword();
    String encPassword = encoder.encode(rawPassword);
    persistance.setPassword(encPassword);
    persistance.setEmail(user.getEmail());
}

블로그의 정보

Hello 춘기's world

볼빵빵오춘기

활동하기