들어가며
대부분의 회사나 조직은 직원과 고객 데이터베이스를 가지고 있습니다. 매뉴얼웍스를 도입하면 일부 데이터베이스를 이중으로 관리해야 하는 불편함에 직면합니다. 이 문제를 해결하기 위해서 매뉴얼웍스는 OAuth 2.0으로 사용자를 관리하는 기능을 제공합니다.
OAuth 2.0이란?
OAuth 2.0은 여러 애플리케이션이 안전하게 인증 및 권한을 제어할 수 있도록 해주는 오픈 프로토콜입니다. OAuth를 구성하는 주요 요소는 다음과 같습니다.
- 인증 서버(Authorization Server)
로그인과 같은 사용자 인증을 처리하는 서버입니다. 직원이나 고객 데이터베이스에 접근할 수 있는 웹 사이트에 OAuth에 맞춰 필요한 기능을 추가해야 합니다.
- 클라이언트(매뉴얼웍스)
인증 서버로 로그인한 후에 사용할 수 있는 서비스를 말합니다. 매뉴얼웍스가 이에 해당합니다.
- 웹 브라우저
웹 브라우저 리다이렉트로 인증 서버와 매뉴얼웍스를 연결합니다.
OAuth에서는 인증 서버와 자원 서버(Resource Server)를 분리해서 설명합니다. 자원 서버는 사용자 프로파일 등을 제공하는 역할을 하는데 매뉴얼웍스에 OAuth를 적용할 때 인증 서버와 자원 서버를 분리할 필요가 없기 때문에 인증과 자원 제공을 모두 인증 서버에서 처리하는 것으로 가정합니다.
매뉴얼웍스에 OAuth 2.0을 적용하면 다음과 같이 사용자 인증을 처리합니다.
1
웹 브라우저에서 매뉴얼웍스를 호출합니다.
외부에 공개한 문서에는 바로 접근할 수 있습니다.
2
인증 과정을 거치지 않았다면 매뉴얼웍스는 웹 브라우저를 이용해 인증 서버로 리다이렉트합니다. 이 때 쿼리 문자열로 다음 파라미터를 전달합니다.
redirect_uri
인증이 성공한 후 웹 브라우저 리다이렉트로 이동할 매뉴얼웍스 주소
state
중복 호출을 방지하기 위한 장치입니다. 인증이 성공한 후 이 값을 쿼리 문자열로 다시 보냅니다.
3
인증 서버에서 인증에 성공하면 매뉴얼웍스로부터 받은 redirect_uri
으로 리다이렉트합니다. 이 때 쿼리 문자열로 code
와 state
를 전달합니다.
보안을 위해서 미리 정해진 주소(http://127.0.0.1:1975/r/oauth/auth)로 리다이렉트할 수도 있습니다.
4
매뉴얼웍스는 code
를 파라미터로 인증 서버에 토큰을 요청합니다.
웹 브라우저를 거치지 않고 매뉴얼웍스가 인증 서버를 직접 호출합니다.
5
인증 서버에서 받은 토큰을 파라미터로 매뉴얼웍스는 인증 서버에 사용자 프로파일 정보를 요청합니다.
웹 브라우저를 거치지 않고 매뉴얼웍스가 인증 서버를 직접 호출합니다.
토큰 대신에 바로 사용자 프로파일 정보를 요청하고 받는 것이 나아 보입니다. 하지만 OAuth는 인증뿐만 아니라 다양한 서비스나 자원에 접근할 수 있는 프레임워크입니다. 예를 들어 주소록이나 사진 목록과 같은 것들을 요청하는데 사용할 수 있습니다. 따라서 토큰을 가져오는 것과 서비스(사용자 프로파일 정보)를 요청하는 것이 분리되어 있습니다.
매뉴얼웍스 OAuth URL 설정
사용자를 인증하는 인증 서버 URL을 매뉴얼웍스에 설정합니다.
1
<환경 설정 | API> 메뉴로 이동합니다. 1<API 변경> 링크를 클릭합니다.
2
OAuth 서버 URL을 모두 입력한 후 저장합니다.
실제 OAuth 인증 주소를 입력합니다. 서버 네트워크 보안을 위해서 HTTPS를 사용하는 것을 권장합니다.
인증 서버 구현하기
매뉴얼웍스에 설정한 OAuth URL 기능을 구현합니다. 구현해야 하는 URL은 세 개입니다.
직원 또는 고객 데이터베이스에 접근할 수 있는 기존 웹 사이트(애플리케이션)에 이 기능을 추가합니다.
- OAuth 서버 URL
사용자를 인증하는 URL입니다. 사용자가 로그인하지 않았다면 로그인 페이지로 이동시킵니다. 사용자가 인증에 성공하면 웹 브라우저 리다이렉트로 인증 코드를 매뉴얼웍스로 전달합니다.
- OAuth 서버 토큰 URL
인증 서버로 받은 인증 코드로 매뉴얼웍스가 접근 토큰을 가져오는 URL입니다. 이 때는 웹 브라우저를 거치지 않고 매뉴얼웍스가 인증 서버를 직접 호출합니다.
- OAuth 서버 사용자 프로파일 URL
인증 서버로 받은 인증 토큰으로 매뉴얼웍스가 사용자 정보를 가져오는 URL입니다. 이 때는 웹 브라우저를 거치지 않고 매뉴얼웍스가 인증 서버를 직접 호출합니다.
OAuth 서버 URL 구현
매뉴얼웍스는 웹 브라우저 리다이렉트로 다음 파라미터와 함께 인증 서버를 호출합니다.
redirect_uri
인증이 성공한 후 웹 브라우저 리다이렉트로 이동할 매뉴얼웍스 주소
state
중복 호출을 방지하기 위한 장치입니다. 인증이 성공한 후 이 값을 쿼리 문자열로 다시 보냅니다.
사용자가 인정 서버 로그인에 성공하면 redirect_uri
로 리다이렉트합니다. 이 때 다음을 쿼리 문자열로 함께 보내야 합니다.
code
코드 문자열입니다. 특별한 포멧은 없습니다.
state
매뉴얼웍스로부터 받은 state 값을 그대로 전달합니다.
구현할 때 다음을 참고합니다.
보안을 위해서 미리 정해진 주소(http://127.0.0.1:1975/r/oauth/auth)로 리다이렉트할 수도 있습니다.
state
값으로 중복 호출 및 코드 노출에 따른 보안 문제를 방지할 수 있습니다. OAuth 서버를 구현할 때 같은state
값으로 들어오는 중복 요청을 걸러내는 것을 권장합니다.
OAuth 서버 토큰 URL 구현
매뉴얼웍스는 인증 서버로 다음 파라미터를 보냅니다.
code
앞 단계에서 받은 코드 값입니다.
인증 서버는 JSON 형식으로 결과를 반환해야 합니다.
Content-Type: application/json; charset=UTF-8
JSON 형식은 다음과 같습니다.
{ "access_token": "접근 토큰", "expires_in": 60 }
expires_in
은 접근 토큰 유효 기간으로 초를 단위로 합니다.
OAuth 서버 사용자 프로파일 URL 구현
매뉴얼웍스는 인증 서버로 다음 파라미터를 보냅니다.
access_token
앞 단계에서 받은 토큰 값입니다.
인증 서버는 토큰 값에 맞는 사용자 정보를 매뉴얼웍스로 반환해야 합니다. 이 때 지켜야하는 형식은 다음과 같습니다.
Content-Type: application/json; charset=UTF-8
다음을 반환합니다.
{ "id": "사용자 아이디", "name": "사용자 이름", "email": "사용자 이메일 주소", "locale": "ko", "roles": "사용자 권한", "groups": "사용자가 속한 그룹" }
locale
은 매뉴얼웍스 3.0.21 버전부터 지원합니다. locale
에는 en
또는 ko
를 설정할 수 있습니다.
roles
에 세미콜론을 구분자로 여러 개를 설정할 수 있습니다. 설정할 수 있는 권한은 다음과 같습니다.
admin
edtior
writer
reader
roles에 세미콜론을 구분자로 여러 개를 설정할 수 있습니다.
"roles": "admin;writer"
writer
를 설정했다면 reader
는 설정할 필요가 없습니다.
매뉴얼웍스에서 권한을 설정하려면 roles
에 3rabbitz
를 입력합니다.
"roles": "3rabbitz"
groups
에 세미콜론을 구분자로 여러 “그룹 아이디”를 설정할 수 있습니다. “그룹 아이디”에 대한 설명은 그룹 관리를 참고합니다.
"groups": "marketing;support"
매뉴얼웍스에서 그룹을 설정하려면 groups
에 3rabbitz
를 입력합니다.
"groups": "3rabbitz"
그룹 이름 동기화하기
매뉴얼웍스 4.0.2 버전부터 지원합니다.
콘솔에서 다음 명령어를 실행합니다.
set-preference -name SUPPORT_OAUTH_GROUP_SYNC -value true
groups
속성을 문자열이 아닌 객체 배열로 처리합니다.
{ "id": "사용자 아이디", "name": "사용자 이름", "email": "사용자 이메일 주소", "locale": "ko", "roles": "admin;editor;writer", "groups": [ {"id": "group_a", "name": "그룹 A"}, {"id": "group_b", "name": "그룹 B"} ] }
name
속성이 없으면 id
속성을 그룹 이름으로 사용합니다.
JSP 구현 예제
프로그래밍 언어와 개발 환경에 따라서 인증 서버를 구현하는 방법이 달라집니다. 인증 서버를 구현할 때 참고할 수 있는 자바와 JSP로 구현한 예제입니다.
OAuthQueue.java
코드와 토큰과 사용자 아이디를 연결시켜주는 자바 클래스입니다.
package com.threerabbitz.oauth; import java.util.LinkedList; import java.util.UUID; public class OAuthQueue { 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; } 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
클래스를 선언합니다.
57java.util.UUID
클래스로 코드와 토큰 값을 만듭니다.
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사용자가 로그인했는지를 체크합니다. 개발 환경에 맞게 고칩니다.
12code
와 state
를 쿼리 문자열에 넣어서 라다이렉트합니다.
15로그인 이후에 이동할 페이지를 설정합니다. 개발 환경에 맞게 고칩니다.
16로그인 페이지로 이동합니다. 개발 환경에 맞게 고칩니다.
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("}"); } %>
1Content-Type은 application/json; charset=UTF-8
입니다.
11expires_in
속성으로 유효 기간을 설정합니다. 단위는 초입니다.
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" %> <% 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("}"); } %>
1Content-Type은 application/json; charset=UTF-8
입니다.
9사용자 아이디로 사용자 정보를 찾습니다. 실제 환경에 맞게 고칩니다.
OAuth 적용에 따라 알아야 하는 사항
로그아웃
매뉴얼웍스에서 로그아웃을 해도 자동으로 다시 로그인합니다. OAuth 서버에는 여전히 로그인되어 있기 때문입니다. 이를 방지하려면 다음 순서로 로그아웃 이후에 이동할 경로를 설정합니다.
콘솔을 실행합니다.
다음 명령어를 입력합니다.
set-preference -name LOGOUT_REDIRECT_PATH -value https://www.3rabbitz.com
이 옵션에 로그아웃 이후에 이동할 URL을 설정합니다. 일반적으로 이 URL에서 OAuth 서버 로그아웃을 처리합니다.
기타
매뉴얼웍스에서 만든 사용자는 OAuth가 아닌 http://127.0.0.1:1975/r/signon/login에서 로그인해야 합니다.
OAuth로 처음 로그인할 때 매뉴얼웍스에 자동으로 사용자가 만들어집니다. 라이선스를 초과하면 roles
속성과 상관없이 글 읽기 권한만 있는 사용자로 만들어 집니다.
OAuth로 로그인한 사용자는 내 프로파일 정보를 바꿀 수 없습니다.