[Springboot] 카카오로그인 (JWT 토큰 발급)

이수영·2021년 12월 6일
3
post-thumbnail

✍🏻 카카오 로그인

  • 나는 카카오 메시지 api 를 사용하기 위해 카카오 로그인 api를 사용한다
  • 카카오로그인을 통해서 토큰을 발급받아야함

📌 Spring Security 로그인의 동작원리

  • 기본적으로 필터기반으로 동작
  • 클라이언트의 Http Request가 특정 Filter 조건에 부합하면 Filter 동작한다. 로그인도 이와 같이 Spring Security의 UsernamePasswordAuthenticationFilter 라는 필터에 의해 동작한다.

📌 OAuth

사용자가 애플리케이션에게 모든 권한을 넘기지 않고 사용자 대신 서비스를 이용할 수 있게 해주는 HTTP 기반의 보안 프로토콜

📌 OAuth2 인증과정

1. 인증코드 요청

  • 웹 등록 정보로 카카오 로그인 페이지를 띄움
  • 카카오 로그인 완료 시 연동화면이 나오고 동의롤 통해 카카오계정과 웹을 연결
  • 로그인에 성공하면 콜백페이지(redirect url)가 자동 리다이렉트 되고 우리는 url에서 인가코드를 가져오면 된다
    => 카카오가 대신 로그인을 통해 회원을 검증해주고 Authorization code를 발급해서 redirect url 로 돌려주게됨

2. 위에서 받은 인증코드를 이용해 access_token 요청

3. 위 과정에서 받은 access_token 을 이용해 resource 접근

  • 나의 서비스는 access_token 으로 카카오서버에 사용자 정보를 요청하고 값을 가져올 수 있으면 JWT를 발급시켜준다

📌 front에서 카카오로그인 요청

//카카오 api 키 등록
Kakao.init('150169fab72c4abb733d5ccf61c4ac70');
 function kakao_login() {
        Kakao.Auth.login({
            success: function(authObj) {
                $.ajax({
                    type: 'POST',
                    url: `/login/kakao`,
                    contentType: "application/json",
                    data: JSON.stringify({'token':authObj['access_token']}),
                    success: function (response) {
                        localStorage.setItem("token", response['token']);
                        localStorage.setItem("username", response['username']);
                        location.href = '/';
                    }
                })
            },
            fail: function(err) {
                alert(JSON.stringify(err))
            },
        })
    }
  • /login/kakao 로 ajax post 요청
  • 요청 성공시 받아온 token 정보를 저장 (token, username)
  • 로그인 성공 시 token 정보 저장하고 home 으로!

📌 Controller

@PostMapping(value = "/login/kakao")
    public ResponseEntity<?> createAuthenticationTokenByKakao(@RequestBody SocialLoginDto socialLoginDto) throws Exception {
        //api 인증을 통해 얻어온 code값 받아오기
        String username = userService.kakaoLogin(socialLoginDto.getToken());
        final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        
        final String token = jwtTokenUtil.generateToken(userDetails);
        return ResponseEntity.ok(new JwtResponse(token, userDetails.getUsername()));
    }
  • 받아온 토큰으로 카카오로부터 사용자 정보를 얻는다.
  • 얻은 사용자 정보를 바탕으로 토큰을 생성
  • 클라이언트에게 username이 나왔는지 반환해준다

📌 카카오로부터 사용자 정보 얻어서 DB에 저장

 public String kakaoLogin(String token) {
        // 카카오 OAuth2 를 통해 카카오 사용자 정보 조회
        KakaoUserInfo userInfo = kakaoOAuth2.getUserInfo(token);
        Long kakaoId = userInfo.getId();
        String nickname = userInfo.getNickname();
        String email = userInfo.getEmail();
     
        // 카카오 로그인 토큰은 email 과 password 로 만들어줌 
        String username = email;
        // 패스워드 = 카카오 Id + ADMIN TOKEN
        String password = kakaoId + ADMIN_TOKEN;

        // DB 에 중복된 Kakao Id 가 있는지 확인
        User kakaoUser = userRepository.findByKakaoId(kakaoId)
                .orElse(null);

        // 카카오 정보로 회원가입
        if (kakaoUser == null) {
            // 패스워드 인코딩
            String encodedPassword = passwordEncoder.encode(password);
            // ROLE = 사용자


            kakaoUser = new User(username, encodedPassword, nickname,  kakaoId);
            userRepository.save(kakaoUser);
        }

        // 로그인 처리
        Authentication kakaoUsernamePassword = new UsernamePasswordAuthenticationToken(username, password);
        //  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 로 진행됨

        Authentication authentication = authenticationManager.authenticate(kakaoUsernamePassword);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        return username;
    }
  • 원래 카카오 username을 바탕으로 토큰을 만들려고 했으나 username의 경우 중복 가능성이 존재하기 때문에 이메일을 바탕으로 토큰을 생성한다
  • 카카오 계정이름은 nickname으로 저장 , 카카오 이메일은 DB 내 username으로 저장

✔ 기억하기

Authentication kakaoUsernamePassword = new UsernamePasswordAuthenticationToken(username, password);

이 코드는 아래의 코드로 진행된다

 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("로그인 오류"));

        return new UserDetailsImpl(user);
    }
  • 내가 처음에 났던 오류는 토큰의 username을 이메일로 했을 때 실행이 안되는 오류였다
  • 위 코드에서 저장되어 있는 user 정보인지 확인하는 것에 username이 쓰인다
  • 하지만 나는 username을 사용자 계정이름 (nickname)로 해놓았기 때문에 오류가 발생했던 것이다
  • kakaoUser = new User(nickname, encodedPassword, username, kakaoId); 을
    kakaoUser = new User(username, encodedPassword, nickname, kakaoId);
    로 바꿨더니 이메일로도 토큰을 생성할 수 있게 되었다.
profile
Hongik Univ 💻

1개의 댓글

comment-user-thumbnail
2023년 3월 20일

안녕하세요 수영님.
DB저장 하는부분 좀 더 알아보고싶은데 깃허브 코드 공유 가능할까요 ??

답글 달기