본문 바로가기

개발/BACK

[SpringFramework] JAVA 카카오Developers를 이용한 로그인 예제 (REST API) (2)

728x90

지난 포스팅에 이어 카카오 로그인 구현을 마무리 하겠다

 

지난포스팅 내용

https://hdhdeveloper.tistory.com/46

 

[SpringFramework] JAVA 카카오Developers를 이용한 로그인 기능 만들기 REST API (1)

이번 포스팅에서 다뤄볼 것은 카카오에서 제공하는 연계 API를 이용하여 카카오 계정으로 로그인 하는 기능을 구현해 볼 것이다. 먼저 해야하는 것은 카카오 디벨로퍼 계정이 있어야한다. develope

hdhdeveloper.tistory.com

 

 

 


 

이 페이지에서 로그인 버튼을 누르면 리다이렉트 되는 컨트롤러를 한번 살펴보자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @RequestMapping(value = "/kakao_callback", method = RequestMethod.GET)
    public String redirectkakao(@RequestParam String code, HttpSession session) throws IOException {
            System.out.println(code);
            
            //접속토큰 get
            String kakaoToken = kakaoService.getReturnAccessToken(code);
            
            //접속자 정보 get
            Map<String,Object> result = kakaoService.getUserInfo(kakaoToken);
            System.out.println("컨트롤러 출력"+result.get("nickname")+result.get("profile_image"));
            SessionConfigVO configVO =new SessionConfigVO();
            configVO.setUser_name((String)result.get("nickname"));
            configVO.setProfile_img((String)result.get("profile_image"));
            
            session.setAttribute("sessionConfigVO", configVO);
            /*로그아웃 처리 시, 사용할 토큰 값*/
            session.setAttribute("kakaoToken", kakaoToken);
        return "redirect:/";
    }
cs

주석을 매우 심플하게 달아놓았다.

 

먼저 redirectkakao 메소드 파라미터 중, String code 값을 @RequestParam을 이용해 필수적으로 받게 되어 있다.

 

넘어온 code값을 찍어보면 로그인 버튼을 눌렀을 때 임시 토큰 값이 넘어온다.

 

그 토큰 값을 내가 구현한 서비스의 getReturnAccessToken 메소드에 넣어준다.

 

해당 메소드의 역할은 카카오 로그인 토큰을 얻어내는 역할을 한다.

 

해당 서비스를 보자

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Override
    public String getReturnAccessToken(String code) {
         String access_token = "";
         String refresh_token = "";
         String reqURL = "https://kauth.kakao.com/oauth/token";
        
        try {
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            
             //HttpURLConnection 설정 값 셋팅
             conn.setRequestMethod("POST");
             conn.setDoOutput(true);
             
             
             // buffer 스트림 객체 값 셋팅 후 요청
             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream()));
             StringBuilder sb = new StringBuilder();
             sb.append("grant_type=authorization_code");
             sb.append("&client_id=[내 애플리케이션 Key 값]");  //앱 KEY VALUE
             sb.append("&redirect_uri=http://localhost:8080/kakao_callback"); // 앱 CALLBACK 경로
             sb.append("&code=" + code);
             bw.write(sb.toString());
             bw.flush();
             
             //  RETURN 값 result 변수에 저장
             BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
             String br_line = "";
             String result = "";
 
             while ((br_line = br.readLine()) != null) {
                 result += br_line;
             }
 
             JsonParser parser = new JsonParser();
             JsonElement element = parser.parse(result);
 
             
             // 토큰 값 저장 및 리턴
             access_token = element.getAsJsonObject().get("access_token").getAsString();
             refresh_token = element.getAsJsonObject().get("refresh_token").getAsString();
 
             br.close();
             bw.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
 
         return access_token;
     }
cs

String access_token = "";

String refresh_token = "";

연계문서를 보면 토큰 변수 명이 저렇게 되어있는데 나도 동일하게 선언해주었다.

 

BufferedReader의 readLine 함수로 while문을 열어서 변수 result에 값을 담아준다.

 

여기서 Json형식으로 데이터를 넘겨주기 때문에, JsonParser 클래스를 사용했는데 해당 클래스는 

Gson 라이브러리를 적용시켜줘야 한다.

 

아래 Gson라이브러리를 pom.xml에 붙여넣어주자

 

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.2</version>
        </dependency>
cs

 

 

그 후, access_token 값을 JSON 형태의 값에서 파싱해와서 리턴해주면 해당 메소드의 역할은 끝난다.

 

그리고 상단의 컨트롤러 kakao_callback을 보면,  

//접속자 정보 get

            Map<String,Object> result = kakaoService.getUserInfo(kakaoToken);

 

또 하나의 메소드를 타게 된다.

해당 메소드는 구해온 토큰 값을 파라미터로 넣어서 로그인한 아이디의 정보를 얻어오는 역할이다.

 

나의 경우, 사진과 이름만 가져왔으므로, 해당 메소드의 소스는 아래와 같다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@Override
    public Map<String,Object> getUserInfo(String access_token) {
        Map<String,Object> resultMap =new HashMap<>();
        String reqURL = "https://kapi.kakao.com/v2/user/me";
         try {
             URL url = new URL(reqURL);
             HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             conn.setRequestMethod("GET");
 
            //요청에 필요한 Header에 포함될 내용
             conn.setRequestProperty("Authorization", "Bearer " + access_token);
 
             int responseCode = conn.getResponseCode();
             System.out.println("responseCode : " + responseCode);
 
             BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
 
             String br_line = "";
             String result = "";
 
             while ((br_line = br.readLine()) != null) {
                 result += br_line;
             }
            System.out.println("response:" + result);
 
             JsonParser parser = new JsonParser();
             JsonElement element = parser.parse(result);
 
             JsonObject properties = element.getAsJsonObject().get("properties").getAsJsonObject();
             JsonObject kakao_account = element.getAsJsonObject().get("kakao_account").getAsJsonObject();
 
             String nickname = properties.getAsJsonObject().get("nickname").getAsString();
             String profile_image = properties.getAsJsonObject().get("profile_image").getAsString();
             resultMap.put("nickname", nickname);
             resultMap.put("profile_image", profile_image);
             
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         return resultMap;
     }
cs

//요청에 필요한 Header에 포함될 내용

             conn.setRequestProperty("Authorization", "Bearer " + access_token);

 

가져온 토큰 값을 Header에 추가해주고 reqURL로 요청을 보내면 사용자의 정보를 리턴해준다고 연계 문서에 기록되어 있었다.

 

여기서도 동일하게 JSON형태로 넘어온 값을 각각 nickname 과 profile_image 변수에 담아준다.

 

넘겨야 할 값이 하나가 아니라서, Map을 사용해 데이터를 리턴해주는 것이 해당 메소드의 역할이다.

 

그리고 SessionConfigVO는 내가 session에 값을 저장할 때, VO를 통째로 담는걸 좋아하기 때문에, 

 

전에 구현했던 네이버 로그인 정보와,

자체적으로 가지고 있는 회원정보, 카카오 로그인 정보까지 동일한 VO를 구현해서 세션에 담아주고 있다.

 

 


 

            session.setAttribute("sessionConfigVO", configVO);

            /*로그아웃 처리 시, 사용할 토큰 값*/

            session.setAttribute("kakaoToken", kakaoToken);

 

세션에 SessionConfigVO 값도 담아주고, kakaoToken 값도 담아주고 있다.

kakaoToken 값은 왜 담아주냐면, 나는 로그아웃 기능을 만들 때, session의 SessionConfigVO만 날려주면 될 줄 알았는데,

 

연계문서에는 kakaoToken을 이용해서, 로그아웃 시, 토큰 값으로 로그아웃 메소드를 타라고 적혀있었다. (아닌가?)

그래서 로그아웃 시에 따로 토큰 값을 세션에서 가지고와서 넘겨줄 생각으로 세션에 담아주었다.

 

이렇게 세션에 담겨진 값을 가지고 / 를 리다이렉트 하게되면  / 라는 호출을 하는 곳에서 home.jsp를 호출하게 된다.

 

해당 jsp페이지에서 나는 세션값을 이렇게 처리하고 있다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    <c:if test="${sessionConfigVO ne null}">
                <ul style="height:30px;float:right;margin-bottom:20px;" class="fn-font">
                    <li><a style="color:#f97088;text-decoration:none;" class="" >${sessionConfigVO.user_name}' s come in</a></li>
                    <c:if test="${sessionConfigVO.naver_login eq true }">
                        <li>
                            <img src="../resources/images/naver_logo.png" style="width:30px;">
                            <a style="color:green;">NAVER 계정으로 접속중 </a>
                            <span id="count" class="badge bg-theme"></span>
                        </li>
                    </c:if>
                </ul>
                <div class="profile_div"><img src="<c:out value='${sessionConfigVO.profile_img}'/>" width=30 height=30/></div>
            </c:if>
            <c:if test='${sessionConfigVO eq null}'>
                <ul style="height:30px;float:right;margin-bottom:20px;" class="fn-font">
                    <li><a style="color:blue;" class="forget_login" onclick="fn_forgetID()">Forget ID/PASSWORD</a></li>
                </ul>    
            </c:if>
            <span id="recMs" onclick="openNav()" name="recMs" style="float:right;cursor:pointer;margin-right:10px;color:pink;"><img src="../resources/images/msgicon.png" id="messageImage" style="opacity :0.3;width:15px;"></span>
        </div>
        <header class="menu_header">
            <h2 id="title_bar" style="display:inline;cursor:pointer;" onclick="location.href='/'">Education Site Comm</h2>
            <nav>
                <ul class="menu_ul">
                    <c:if test="${sessionConfigVO eq null}">
                        <li><a href="#" onclick="fn_join_modal();">Join</a></li>
                        <li><a href="#" onclick="fn_login_modal();" id="login">Login</a></li>
                    </c:if>
                    <c:if test="${sessionConfigVO ne null}">
                        <li><a href="/login/logout_proc">Logout</a></li>
                        <li><a href="/update/update.do">Info</a></li>
                    </c:if>
                        <li><a href="">Propose</a></li>
                        <li><a href="">Advertise</a></li>
                </ul>
            </nav>
        </header>
cs

 

 

 

그리고 이제 실행을 해보자

오 상단에 내 사진과 이름이 잘 출력된다.

 

 

그리고 로그아웃 로직을 보자면 이렇다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    @RequestMapping(value="/login/logout_proc")
    public String logout(ModelMap modelMap, HttpSession session)throws IOException {
        if(SystemUtil.EmptyCheck((String)session.getAttribute("kakaoToken"))){
        }else {
            kakaoService.getLogout((String)session.getAttribute("kakaoToken"));
        }
        session.setAttribute("sessionConfigVO"null);
        HashMap<StringString> message = new HashMap<>();
        message.put("title""로그아웃");
        message.put("script""location.href='/'");
        message.put("msg""로그아웃 되었습니다");
        message.put("type","alert");
        modelMap.addAttribute("message",message);
        return "/comm/alert_message";
    }
cs

아까 session에 담아뒀던 카카오토큰 값은, 오직 카카오로 로그인 할때만 생기는 값이다.

그 값으로 카카오 로그인을 판별해 처리했다.

 

 

kakaoService.getLogout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    @Override
    public void getLogout(String access_token) {
        String reqURL ="https://kapi.kakao.com/v1/user/logout";
        try {
            URL url = new URL(reqURL);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            
            conn.setRequestProperty("Authorization""Bearer " + access_token);
            int responseCode = conn.getResponseCode();
            System.out.println("responseCode : " + responseCode);
 
            if(responseCode ==400)
                throw new RuntimeException("카카오 로그아웃 도중 오류 발생");
            
            
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            
            String br_line = "";
            String result = "";
            while ((br_line = br.readLine()) != null) {
                result += br_line;
            }
            System.out.println("결과");
            System.out.println(result);
        }catch(IOException e) {
            
        }
    }
cs

 

여기까지 구성을 하면 카카오 연동 로그인, 로그인 정보 가져오기, 로그아웃 기능을 모두 구현한 것이다.

 

728x90