카카오소셜로그인 401에러

문의 시, 사용하시는 개발환경과 디벨로퍼스 앱ID를 알려주세요.


앱아이디 : 964154

package com.semi.main.kakao;

import java.io.IOException;

import javax.servlet.http.HttpSession;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;

import com.semi.main.member.MemberDTO;

@Controller
@RequestMapping("/kakao/*")
public class KakaoController {

    @Autowired
    private KakaoService kakaoService; 
    
    
 // 로그인: 인가 코드 및 토큰 발급
 	@RequestMapping(value = "callback", method = RequestMethod.GET)
 	public String getLogin(@RequestParam("code") String code, HttpSession session, Model model) throws Exception{
        
         // 1. header 생성
         HttpHeaders httpHeaders = new HttpHeaders();
         httpHeaders.add(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=utf-8");

         // 2. body 생성
         MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
         params.add("grant_type", "authorization_code"); //고정값
         params.add("client_id", "d5aa1769209cd0342601e6c69c838176");
         params.add("redirect_uri", "http://localhost:82/kakao/callback"); //등록한 redirect uri
         params.add("code", code); 

         // 3. header + body
         HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<MultiValueMap<String, String>>(params, httpHeaders);
         
         // 4. http 요청하기
         RestTemplate restTemplate = new RestTemplate();
         ResponseEntity<String> response = restTemplate.exchange(
                 "https://kauth.kakao.com/oauth/token",
                 HttpMethod.POST,
                 httpEntity,
                 String.class     
         );
         System.out.println("1111");
         JSONParser jsonParser = new JSONParser();
         JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody());

         String accessToken  = (String) jsonObj.get("access_token");
         
         // 발급받은 토큰 정보로 유저 정보를 얻어옴
         MemberDTO memberDTO = getUserInfoWithToken(accessToken);
                 
         MemberDTO memberDTO2 = kakaoService.getEmailCheck(memberDTO);
         System.out.println(memberDTO2);
         
         if(memberDTO2 != null) {
 			//4. 세션에 저장
 			System.out.println("기존 정보 있음");
 			session.setAttribute("member", memberDTO2);				
 			return "/member/login";
 		}else {
 			System.out.println("기존 정보 없음");
 			session.setAttribute("kakaoMember", memberDTO);
 			return "/member/signUp";
 		}			
 	
 	}

 	private MemberDTO getUserInfoWithToken(String accessToken) throws ParseException {
 		
         //HttpHeader 생성
         HttpHeaders headers = new HttpHeaders();
         headers.add("Authorization", "Bearer " + accessToken);
         headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
         
         //HttpHeader 담기
         RestTemplate rt = new RestTemplate();
         HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(headers);
         ResponseEntity<String> response = rt.exchange(
                 "https://kapi.kakao.com/v2/user/me",
                 HttpMethod.POST,
                 httpEntity,
                 String.class
         );
         
         //Response 데이터 파싱
         JSONParser jsonParser = new JSONParser();
         JSONObject jsonObj    = (JSONObject) jsonParser.parse(response.getBody());
         JSONObject account = (JSONObject) jsonObj.get("kakao_account");
         
         String email = String.valueOf(account.get("email"));
         
         MemberDTO memberDTO = new MemberDTO();
         memberDTO.setEmail(email);
         
         return memberDTO;
 	}
}

입 예외 보고

메시지 Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 401 Unauthorized

설명 서버가, 해당 요청을 충족시키지 못하게 하는 예기치 않은 조건을 맞닥뜨렸습니다.

예외

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

근본 원인 (root cause)

org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
	org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
	org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667)
	org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620)
	org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580)
	org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:498)
	com.semi.main.kakao.KakaoController.getLogin(KakaoController.java:54)
	java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	java.base/java.lang.reflect.Method.invoke(Method.java:566)
	org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
	org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
	org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

비고 근본 원인(root cause)의 풀 스택 트레이스를, 서버 로그들에서 확인할 수 있습니다.

해당컨트롤러 사용하고 있는데 소셜로그인시 동의후 로그인누르면 401에러가 발생합니다. 무엇이 문제인지 알수잇을까요 감이안잡힙니다 리다이렉트주소도 알맞게 설정되어있는데 안되는이유를 모르겠습니다.

안녕하세요.

client_secret이 활성화된 상태이지만, 접근토큰 발급 시 해당 값이 전달되고 있지 않아 KOE010 오류가 발생하였습니다.
아래 FAQ를 참고 부탁드립니다.

답변감사합니다

비활성화 후 소셜로그인버튼 클릭시

HTTP 상태 500 – 내부 서버 오류


타입 예외 보고

메시지 Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 400 Bad Request

설명 서버가, 해당 요청을 충족시키지 못하게 하는 예기치 않은 조건을 맞닥뜨렸습니다.

예외

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException: 400 Bad Request org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) javax.servlet.http.HttpServlet.service(HttpServlet.java:529) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) javax.servlet.http.HttpServlet.service(HttpServlet.java:623) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

근본 원인 (root cause)

org.springframework.web.client.HttpClientErrorException: 400 Bad Request org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580) org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:498) com.semi.main.kakao.KakaoController.getLogin(KakaoController.java:54) java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) java.base/java.lang.reflect.Method.invoke(Method.java:566) org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) javax.servlet.http.HttpServlet.service(HttpServlet.java:529) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) javax.servlet.http.HttpServlet.service(HttpServlet.java:623) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

비고 근본 원인(root cause)의 풀 스택 트레이스를, 서버 로그들에서 확인할 수 있습니다.


Apache Tomcat/9.0.79

해당오류가 발생하기 시작했습니다 …

해당 버튼은
<a href=카카오계정 >

a태그를 이용해 작성되어있습니다…조언부탁드리겠습니다

이미 사용한 인가코드, 즉 유효하지 않은 인가코드를 사용하여 KOE320 오류가 발생하였습니다.
인가코드는 성공 여부와 상관없이 1번만 사용가능합니다.

다른이야기지만, 오류 확인 시, 스택 로그 외에 response body 값을 확인하시면 오류 코드와 원인에 대한 간략한 메시지를 확인해 보실수 있습니다.

답변감사합니다!! 제가 아직 모르는게많은데

@Controller
@RequestMapping(“/kakao/*”)
public class KakaoController {

@Autowired
private KakaoService kakaoService; 

// 로그인: 인가 코드 및 토큰 발급
@RequestMapping(value = “callback”, method = RequestMethod.GET)
public String getLogin(@RequestParam(“code”) String code, HttpSession session, Model model) throws Exception{

     // 1. header 생성
     HttpHeaders httpHeaders = new HttpHeaders();
     httpHeaders.add(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded;charset=utf-8");

     // 2. body 생성
     MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
     params.add("grant_type", "authorization_code"); //고정값
     params.add("client_id", "d5aa1769209cd0342601e6c69c838176");
     params.add("redirect_uri", "http://localhost:82/kakao/callback"); //등록한 redirect uri
     params.add("code", code); 

     // 3. header + body
     HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<MultiValueMap<String, String>>(params, httpHeaders);
     
     // 4. http 요청하기
     RestTemplate restTemplate = new RestTemplate();
     ResponseEntity<String> response = restTemplate.exchange(
             "https://kauth.kakao.com/oauth/token",
             HttpMethod.POST,
             httpEntity,
             String.class     
     );
     System.out.println("1111");
     JSONParser jsonParser = new JSONParser();
     JSONObject jsonObj = (JSONObject) jsonParser.parse(response.getBody());

     String accessToken  = (String) jsonObj.get("access_token");
     
     // 발급받은 토큰 정보로 유저 정보를 얻어옴
     MemberDTO memberDTO = getUserInfoWithToken(accessToken);
             
     MemberDTO memberDTO2 = kakaoService.getEmailCheck(memberDTO);
     System.out.println(memberDTO2);
     
     if(memberDTO2 != null) {
		//4. 세션에 저장
		System.out.println("기존 정보 있음");
		session.setAttribute("member", memberDTO2);				
		return "/member/login";
	}else {
		System.out.println("기존 정보 없음");
		session.setAttribute("kakaoMember", memberDTO);
		return "/member/kakaoSignUp";
	}			

}

private MemberDTO getUserInfoWithToken(String accessToken) throws ParseException {
	
     //HttpHeader 생성
     HttpHeaders headers = new HttpHeaders();
     headers.add("Authorization", "Bearer " + accessToken);
     headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
     
     //HttpHeader 담기
     RestTemplate rt = new RestTemplate();
     HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(headers);
     ResponseEntity<String> response = rt.exchange(
             "https://kapi.kakao.com/v2/user/me",
             HttpMethod.POST,
             httpEntity,
             String.class
     );
     
     //Response 데이터 파싱
     JSONParser jsonParser = new JSONParser();
     JSONObject jsonObj    = (JSONObject) jsonParser.parse(response.getBody());
     JSONObject account = (JSONObject) jsonObj.get("kakao_account");
     
     String email = String.valueOf(account.get("email"));
     
     MemberDTO memberDTO = new MemberDTO();
     memberDTO.setEmail(email);
     
     System.out.println(memberDTO);
     return memberDTO;
}

}

해당컨트롤러에서 회원중 일치하는 이메일값이없어서 카카오회원가입 페이지로 이동합니다 해당 회원가입페이지로 이동후 kakaoMember.email을 이메일입력창에 value값으로 미리넣은 후 회원가입을 진행하고싶은데 해당값이 null로 반환됩니다. 해당 컨트롤러 실행시 콘솔창에 찍힌 값들입니다 .

1111

com.semi.main.member.MemberDTO@263b9be2

null

기존 정보 없음

데이터를 파싱하면서 문제가 생기는거같은데 어느부분에서 문제가 생기는지 모르겠습니다…답변주시면 감사하겠습니다

카카오 로그인한 사용자의 이메일 정보가 제공되지 않기에 null로 확인되셨습니다.

카카오 로그인 시, 사용자가 이메일 제공 동의 항목에 동의하지 않은 경우
사용자 정보 가져오기 (/v2/user/me) 에서 해당 값을 획득할 수 없습니다.

앱 964154의 경우 이메일 항목이 이용 중 동의로 설정되어 있습니다.
일반적으로 이용 중 동의 항목은 서비스 이용 중, 필요 할 경우 사용자에게 추가로 동의 받기 위해 설정합니다.
따라서 카카오 로그인 시, 기본적으로 동의 항목에 표시되지 않고, 추가 항목 동의 받기 기능을 사용해만 동의 받을 수 있습니다.

좀 더 자세한 내용은 아래 동의 항목과 동의 단계관련 문서를 참고 부탁드립니다.

이해하기 | Kakao Developers 이해하기 - 동의 항목
설정하기 | Kakao Developers 설정하기 - 개인정보 동의 항목

덕분에 해결됐습니다 감사합니다!

1개의 좋아요