12. OAuth 2.0 으로사용자관리하기 12.1 들어가며 대부분의회사나조직은직원과고객데이터베이스를가지고있습니다. 쓰리래빗츠를도입하면 일부데이터베이스를이중으로관리해야하는불편함에직면합니다. 이문제를해결하기위해서 쓰리래빗츠는 OAuth 2.0 으로사용자를관리하는기능을제공합니다. 12.2 OAuth 2.0 이란? OAuth 2.0 은여러애플리케이션이안전하게인증및권한을제어할수있도록해주는오픈프로 토콜입니다. OAuth 를구성하는주요요소는다음과같습니다. 인증서버 (Authorization Server) 로그인과같은사용자인증을처리하는서버입니다. 직원이나고객데이터베이스에접근할수있는웹사이트에 OAuth에맞춰필요한기능을추가해야합니다. 클라이언트 ( 쓰리래빗츠 ) 인증서버로로그인한후에사용할수있는서비스를말합니다. 쓰리래빗츠가이에해당합니다. 1
2 OAuth 2.0 으로사용자관리하기 웹브라우저 웹브라우저리다이렉트로인증서버와쓰리래빗츠를연결합니다. OAuth에서는인증서버와자원서버 (Resource Server) 를분리해서설명합니다. 자원서버는사용자프로파일등을제공하는역활을하는데쓰리래빗츠에 OAuth를적용할때인증서버와자원서버를분리할필요가없기때문에인증과자원제공을모두인증서버에서처리하는것으로가정합니다. 쓰리래빗츠에 OAuth 2.0 을적용하면다음과같이사용자인증을처리합니다. 1 웹브라우저에서쓰리래빗츠를호출합니다. 외부에공개한문서에는바로접근할수있습니다. 2 인증과정을거치지않았다면쓰리래빗츠는인증서버로리다이렉트합니다. 이때쿼리문자 열로다음파라미터를전달합니다. redirect_uri 인증이성공한후웹브라우저리다이렉트로이동할쓰리래빗츠주소 state 중복호출을방지하기위한장치입니다. 인증이성공한후이값을쿼리문자열로다시보냅니다.
OAuth 2.0 이란? 3 3 인증서버에서인증에성공하면쓰리래빗츠로부터받은 redirect_uri 으로리다이렉트합니 다. 이때쿼리문자열로 code 와 state 를전달합니다. 보안을위해서미리정해진주소 (http://127.0.0.1:1975/r/oauth/auth) 로리다이렉 트할수도있습니다. 4 쓰리래빗츠는 code 를파라미터로인증서버에토큰을요청합니다.
4 OAuth 2.0 으로사용자관리하기 웹브라우저를거치지않고쓰리래빗츠이인증서버를직접호출합니다. 5 인증서버에서받은토큰을파라미터로쓰리래빗츠는인증서버에사용자프로파일정보를 요청합니다. 웹브라우저를거치지않고쓰리래빗츠가인증서버를직접호출합니다. 토큰대신에바로사용자프로파일정보를요청하고받는것이낫아보입니다. 하지만 OAut h 는인증뿐만아니라다양한서비스나자원에접근할수있는프레임워크입니다. 예를들어
쓰리래빗츠 OAuth URL 설정 5 주소록이나사진목록과같은것들을요청하는데사용할수있습니다. 따라서토큰을가져오 는것과서비스 ( 사용자프로파일정보 ) 를요청하는것이분리되어있습니다. 12.3 쓰리래빗츠 OAuth URL 설정 사용자를인증하는인증서버 URL 을쓰리래빗츠에설정합니다. 1 < 관리 환경설정 API> 메뉴로이동합니다. 1 <API 변경 > 링크를클릭합니다. 2 OAuth 서버 URL 을모두입력한후저장합니다.
6 OAuth 2.0 으로사용자관리하기 네트워크보안을위해서 HTTPS 를사용하는것을권장합니다. 12.4 인증서버구현하기 쓰리래빗츠에설정한 OAuth URL 기능을구현합니다. 구현해야하는 URL 은세개입니다. 직원또는고객데이터베이스에접근할수있는기존웹사이트 ( 애플리케이션 ) 에이기 능을추가합니다.
인증서버구현하기 7 OAuth 서버 URL 사용자를인증하는 URL입니다. 사용자가로그인하지않았다면로그인페이지로이동시킵니다. 사용자가인증에성공하면웹브라우저리다이렉트로인증코드를쓰리래빗츠로전달합니다. OAuth 서버토큰 URL 인증서버로받은인증코드로쓰리래빗츠가접근토큰을가져오는 URL입니다. 이때는웹브라우저를거치지않고쓰리래빗츠가인증서버를직접호출합니다. OAuth 서버사용자프로파일 URL 인증서버로받은인증토큰으로쓰리래빗츠가사용자정보를가져오는 URL입니다. 이때는웹브라우저를거치지않고쓰리래빗츠가인증서버를직접호출합니다. 12.4.1 OAuth 서버 URL 구현 쓰리래빗츠는웹브라우저리다이렉트로다음파라미터와함께인증서버를호출합니다. redirect_uri 인증이성공한후웹브라우저리다이렉트로이동할쓰리래빗츠주소 state 중복호출을방지하기위한장치입니다. 인증이성공한후이값을쿼리문자열로다시보냅니다. 사용자가인정서버로그인에성공하면 redirect_uri 로리다이렉트합니다. 이때다음을쿼리문 자열로함께보내야합니다. code 코드문자열입니다. 특별한포멧은없습니다. state 쓰리래빗츠로부터받은 state 값을그대로전달합니다. 구현할때다음을참고합니다. 보안을위해서미리정해진주소 (http://127.0.0.1:1975/r/oauth/auth) 로리다이렉트할수도
8 OAuth 2.0 으로사용자관리하기 있습니다. state 값으로중복호출및코드노출에따른보안문제를방지할수있습니다. OAuth 서버를 구현할때같은 state 값으로들어오는중복요청을걸러내는것을권장합니다. 12.4.2 OAuth 서버토큰 URL 구현 쓰리래빗츠는인증서버로다음파라미터를보냅니다. code 앞단계에서받은코드값입니다. 인증서버는 JSON 형식으로결과를반환해야합니다. Content-Type: application/json; charset=utf-8 JSON 형식은다음과같습니다. { "access_token": " 접근토큰 ", "expires_in": 60 expires_in 은접근토큰유효기간으로초를단위로합니다. 12.4.3 OAuth 서버사용자프로파일 URL 구현 쓰리래빗츠는인증서버로다음파라미터를보냅니다. access_token 앞단계에서받은토큰값입니다.
인증서버구현하기 9 인증서버는토큰값에맞는사용자정보를쓰리래빗츠로반환해야합니다. 이때지켜야하는형식 은다음과같습니다. Content-Type: application/json; charset=utf-8 다음을반환합니다. { "id": " 사용자아이디 ", "name": " 사용자이름 ", "email": " 사용자이메일주소 ", "roles": " 사용자권한 ", "groups": " 사용자가속한그룹 " roles 에세미콜론을구분자로여러개를설정할수있습니다. 설정할수있는권한은다음과같습 니다. admin edtior writer reader roles 에세미콜론을구분자로여러개를설정할수있습니다. "roles": "admin;writer" writer 를설정했다면 reader 는설정할필요가없습니다. 쓰리래빗츠에서권한을설정하려면 roles 에 3rabbitz 를입력합니다.
10 OAuth 2.0 으로사용자관리하기 "roles": "3rabbitz" groups 에세미콜론을구분자로여러그룹아이디를설정할수있습니다. 그룹아이디에대한설 명은 10. 그룹관리를참고합니다. "groups": "marketing;support" 쓰리래빗츠에서그룹을설정하려면 groups 에 3rabbitz 를입력합니다. "groups": "3rabbitz" 12.5 JSP 구현예제 프로그래밍언어와개발환경에따라서인증서버를구현하는방법이달라집니다. 인증서버를구 현할때참고할수있는자바와 JSP 로구현한예제입니다. 12.5.1 OAuthQueue.java 코드와토큰과사용자아이디를연결시켜주는자바클래스입니다. package com.threerabbitz.oauth; import java.util.linkedlist; import java.util.uuid; public class OAuthQueue {
JSP 구현예제 11 private static final int MAX_SIZE = 10000; private static OAuthQueue instance = new OAuthQueue(); public static OAuthQueue getinstance() { return instance; private LinkedList<OAuthInfo> queue = new LinkedList<OAuthInfo>(); public synchronized String getcode(string user) { if (user == null) { throw new IllegalArgumentException("user_cannot_be_null"); OAuthInfo result = new OAuthInfo(user); queue.add(result); if (queue.size() > MAX_SIZE) { queue.removefirst(); return result.code; public synchronized String gettoken(string code) { if (code == null) { throw new IllegalArgumentException("code_cannot_be_null"); for (int i = queue.size() - 1; i > -1; i--) { OAuthInfo info = queue.get(i); if (info.code.equals(code)) { return info.token; return null;
12 OAuth 2.0 으로사용자관리하기 public synchronized String getuser(string token) { if (token == null) { throw new IllegalArgumentException("token_cannot_be_null"); for (int i = queue.size() - 1; i > -1; i--) { OAuthInfo info = queue.get(i); if (info.token.equals(token)) { return info.user; return null; static private class OAuthInfo { String code = UUID.randomUUID().toString(); String token = UUID.randomUUID().toString(); String user; OAuthInfo(String user) { this.user = user; 8 인증데이터캐싱크기를 10,000개로제한합니다. 12 싱글톤패턴을적용했습니다. 56 코드, 토큰, 사용자아이디를담고있는 OAuthInfo 클래스를선언합니다. 57 java.util.uuid 클래스로코드와토큰값을만듭니다.
JSP 구현예제 13 12.5.2 oauth.jsp 로그인여부를체크하고웹브라우저리다이렉트로쓰리래빗츠에코드를전달하는 JSP 파일입니 다. <%@ page contenttype="text/html; charset=utf-8" pageencoding="utf-8" %> <%@ page import="com.threerabbitz.base.domain.user" %> <%@ page import="com.threerabbitz.oauth.oauthqueue" %> <% String redirecturi = request.getparameter("redirect_uri"); String state = request.getparameter("state"); User user = (User) session.getattribute("user"); if (user!= null) { String code = OAuthQueue.getInstance().getCode(user.getUserid()); response.sendredirect(redirecturi + "?code=" + code + "&state=" + state); else { String path = "/oauth.jsp?redirect_uri=" + redirecturi + "&state=" + state; session.setattribute("path", path); request.getrequestdispatcher("/login.jsp").forward(request, response); %> 10 사용자가로그인했는지를체크합니다. 개발환경에맞게고칩니다. 12 code와 state를쿼리문자열에넣어서라다이렉트합니다. 15 로그인이후에이동할페이지를설정합니다. 개발환경에맞게고칩니다. 16 로그인페이지로이동합니다. 개발환경에맞게고칩니다.
14 OAuth 2.0 으로사용자관리하기 12.5.3 oauth_token.jsp 코드 (code) 를받아토큰을반환하는 JSP 파일입니다. <%@ page contenttype="application/json; charset=utf-8" pageencoding="utf-8" %> <%@ page import="com.threerabbitz.oauth.oauthqueue" %> <% String code = request.getparameter("code"); String token = OAuthQueue.getInstance().getToken(code); if (token!= null) { out.print("{"); out.print("\"access_token\": \"" + token + "\","); out.print("\"expires_in\": 60"); out.print(""); %> 1 Content-Type 은 application/json; charset=utf-8 입니다. 11 expires_in 속성으로유효기간을설정합니다. 단위는초입니다. 12.5.4 oauth_user_profile.jsp 토큰 (access_token) 을받아사용자정보를반환하는 JSP 파일입니다. <%@ page contenttype="application/json; charset=utf-8" pageencoding="utf-8" %> <%@ page import="com.threerabbitz.base.domain.user" %> <%@ page import="com.threerabbitz.base.domain.users" %> <%@ page import="com.threerabbitz.oauth.oauthqueue" %>
OAuth 적용에따라알아야하는사항 15 <% String token = request.getparameter("access_token"); User user = Users.find(OAuthQueue.getInstance().getUser(token)); if (user!= null) { out.print("{"); out.print("\"id\": \"" + user.getuserid() + "\","); out.print("\"name\": \"" + user.getname() + "\","); out.print("\"email\": \"" + user.getemail() + "\","); out.print("\"roles\": \"3rabbitz\","); out.print("\"groups\": \"3rabbitz\""); out.print(""); %> 1 Content-Type 은 application/json; charset=utf-8 입니다. 9 사용자아이디로사용자정보를찾습니다. 실제환경에맞게고칩니다. 12.6 OAuth 적용에따라알아야하는사항 쓰리래빗츠에서만든사용자는 OAuth이아닌 http://127.0.0.1:1975/r/signon/login에서로그인해야합니다. OAuth로처음로그인할때쓰리래빗츠에자동으로사용자가만들어집니다. 라이선스를초과하면글읽기권한만있는사용자로만들어집니다. OAuth로로그인한사용자는내프로파일정보를바꿀수없습니다.