카우치베이스 모바일을 사용한 사용자 지정 인증
Couchbase Mobile은 모든 클라우드에서 모든 모바일 장치로 데이터를 안전하게 관리하고 동기화하여 Couchbase를 에지까지 확장합니다. Couchbase Mobile은 임베디드 데이터베이스인 Couchbase Lite를 통해 JSON에 대한 SQL 및 전체 텍스트 검색, 내장된 피어 투 피어 동기화, 클라우드에서 에지까지 엔드투엔드 보안을 제공합니다.
또한 Couchbase Mobile은 웹을 통해 데이터에 액세스하고 동기화할 수 있는 보안 웹 게이트웨이인 동기화 게이트웨이를 제공하며 여러 인증 방법. 이러한 방법 중 하나는 앱 서버가 외부 시스템과의 인증을 처리하는 사용자 지정 인증입니다.
이 블로그 게시물에서는 앱 서버 코드와 모바일 앱 코드를 구현하는 방법의 예시를 통해 사용자 지정 인증 흐름을 안내합니다. An OpenLDAP 서버가 사용되었지만 이 흐름은 모든 외부 인증 제공업체에 적용됩니다.
예제 앱 서버는 간단한 node.js 애플리케이션이며 모바일 앱 코드 샘플은 카우치베이스 라이트 안드로이드 SDK.
전제 조건
이 블로그는 Couchbase Server, 동기화 게이트웨이 및 Couchbase Lite에 익숙하다고 가정합니다.
동기화 게이트웨이 2.5 및 Couchbase Server EE 6.x를 설치해야 합니다. 필요한 경우 다음 링크를 통해 빠르게 설치 및 실행할 수 있습니다:
이 블로그의 모든 코드는 다음 Git 리포지토리에서 사용할 수 있습니다: https://github.com/dugbonsai/sg-custom-auth
사용자 지정 인증 흐름
다음 다이어그램은 사용자 지정 인증의 예를 보여줍니다.
- 모바일 앱 사용자는 앱 서버에 의해 노출된 REST 인터페이스를 통해 자격 증명을 사용하여 로그인을 시도합니다.
- 앱 서버는 OpenLDAP 서버에 대해 사용자를 인증합니다.
- 사용자가 인증되지 않은 경우 로그인 UI로 돌아갑니다.
- 사용자가 인증되면 앱 서버는 REST 인터페이스를 통해 동기화 게이트웨이에서 사용자 정보를 가져옵니다.
- 동기화 게이트웨이에 사용자가 있는 경우 8단계로 건너뜁니다.
- 사용자가 동기화 게이트웨이에 존재하지 않는 경우 앱 서버는 REST 인터페이스를 통해 동기화 게이트웨이에 새 사용자를 만듭니다.
- 사용자가 동기화 게이트웨이에서 생성되지 않은 경우 앱 서버는 오류를 반환하고 사용자는 로그인 UI로 돌아갑니다.
- 사용자가 동기화 게이트웨이에서 생성된 경우, 앱 서버는 REST 인터페이스를 통해 동기화 게이트웨이에서 사용자에 대한 새 세션을 만듭니다.
- 동기화 게이트웨이에서 세션이 생성되지 않은 경우 앱 서버는 오류를 반환하고 사용자는 로그인 UI로 돌아갑니다.
- 세션이 동기화 게이트웨이에서 생성된 경우 앱 서버는 세션 ID를 반환합니다.
- 모바일 앱은 세션 ID를 저장하고 세션 ID를 사용하여 Couchbase Lite 리플리케이터를 생성합니다.
- 동기화 게이트웨이에서 세션이 만료되어 복제에 실패하면 사용자는 로그인 UI로 돌아갑니다.
OpenLDAP 구성
이미 LDAP 서버를 구성한 경우 이 섹션을 건너뛸 수 있습니다. LDAP 서버를 구성하지 않은 경우, 이 지침은 Ubuntu 18.04에서 OpenLDAP를 구성하는 방법을 안내합니다..
- LDIF 파일 생성(ldap_data.ldif)에 LDAP 데이터베이스를 채울 정보(LDIF 파일에 대한 자세한 내용을 보려면 여기를 클릭하세요.). 위의 OpenLDAP 구성 지침에는 LDAP 데이터베이스를 채우기 위한 LDIF 파일 생성 지침도 포함되어 있습니다. 이 예에서는 모바일 앱에서 인증할 때 모바일 사용자(13번째 줄부터 시작)가 사용됩니다.
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 |
dn: ou=사람,dc=예제,dc=com 객체 클래스: 조직 단위 ou: 사람 dn: ou=그룹,dc=예제,dc=com 객체 클래스: 조직 단위 ou: 그룹 dn: cn=부서,ou=그룹,dc=예제,dc=com 객체 클래스: posixGroup cn: 하위 그룹 gidNumber: 5000 dn: uid=모바일 사용자,ou=사람,dc=예제,dc=com 객체 클래스: inetOrgPerson 객체 클래스: posixAccount 객체 클래스: shadowAccount uid: 모바일 사용자 sn: 사용자 주어진 이름: 모바일 cn: 모바일 사용자 표시 이름: 모바일 사용자 uidNumber: 10000 gidNumber: 5000 사용자 비밀번호: 비밀번호 gecos: 모바일 사용자 로그인 셸: /bin/bash 홈디렉토리: /홈/모바일 사용자 |
- 초기 데이터를 OpenLDAP 데이터베이스에 추가합니다:
1 |
$ ldapadd -x -D cn=관리자,dc=예제,dc=com -W -f ldap_data.ldif |
- OpenLDAP 디렉토리에서 mobileuser를 검색하여 설정을 테스트합니다:
1 |
$ ldapsearch -x -LLL -b dc=예제,dc=com 'uid=모바일사용자' |
모바일 사용자에 대한 정보가 표시됩니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
dn: uid=모바일 사용자,ou=사람,dc=예제,dc=com 객체 클래스: inetOrgPerson 객체 클래스: posixAccount 객체 클래스: shadowAccount uid: 모바일 사용자 sn: 사용자 주어진 이름: 모바일 cn: 모바일 사용자 표시 이름: 모바일 사용자 uidNumber: 10000 gidNumber: 5000 gecos: 모바일 사용자 로그인 셸: /bin/bash 홈디렉토리: /홈/모바일 사용자 |
앱 서버 구현
앱 서버는 node.js 애플리케이션으로 구현됩니다. 전체 앱 서버 코드는 다음에서 확인할 수 있습니다. auth.js 를 다음 Git 리포지토리에 저장합니다: sg-custom-auth. 앱 서버는 여권-ldapauth 패키지 를 사용해야 합니다. 다음 코드 스니펫은 OpenLDAP 서버에 대한 인증 호출과 동기화 게이트웨이 REST API 호출을 강조 표시합니다.
앱 서버는 POST /로그인 호출을 처리해야 합니다. 이 엔드포인트는 요청 본문에 사용자 이름과 비밀번호로 저장된 사용자 자격 증명을 사용하여 모바일 앱에서 호출됩니다. 앱 서버는 이러한 자격 증명을 사용하여 OpenLDAP 서버에 대해 인증합니다.
searchBase의 값은 LDAP 서버에 정의된 도메인 구성 요소와 일치해야 합니다. 위에서 dc=example,dc=com을 사용했으므로 여기(12줄)에서도 동일한 값을 사용합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var express = require('express'), 여권 = require('passport'), bodyParser = require('body-parser'), LdapStrategy = require('passport-ldapauth'), curl = require('curl-request'); var 동기화 게이트웨이 엔드포인트 = 'http://:4985/'; var 앱 = express(); var OPTS = { 서버: { URL: 'ldap://:389', 검색베이스: 'DC=예제,DC=컴', // LDAP 디렉토리와 일치해야 함 검색 필터: '(uid={{사용자명}})' } }; 여권.사용(new LdapStrategy(OPTS)); 앱.사용(bodyParser.json()); 앱.사용(bodyParser.urlencoded({확장: false})); 앱.사용(여권.초기화()); 앱.post('/로그인', 여권.인증('ldapauth', {세션: false}), 함수(req, res) { // 사용자가 LDAP에 대해 성공적으로 인증되었습니다. } |
사용자가 인증되면 다음 단계가 실행됩니다.
동기화 게이트웨이에서 사용자 정보 가져오기
다음을 호출하여 동기화 게이트웨이에서 사용자 정보를 가져옵니다. GET /{db}/_user/{name}. 사용자가 동기화 게이트웨이에 있는 경우 응답 코드는 200입니다. 사용자가 동기화 게이트웨이에 존재하지 않는 경우 응답 코드는 404입니다.
1 2 3 4 5 6 7 8 9 10 |
getUser = new(curl); getUser.setHeaders(['콘텐츠 유형: 애플리케이션/json']) .get(동기화 게이트웨이 엔드포인트 + '/_user/' + req.body.사용자 이름) .다음(({statusCode, body, 헤더}) => { 만약 (statusCode == 404) { // 상태 == 404: 사용자가 존재하지 않습니다. } else 만약 (statusCode == 200) { // 상태 == 200: 사용자 존재 } }) |
동기화 게이트웨이에 사용자가 없는 경우 새 사용자를 만듭니다.
동기화 게이트웨이에서 다음을 호출하여 새 사용자를 만듭니다. POST /{db}/_user/. 동기화 게이트웨이에서 사용자가 성공적으로 생성된 경우 응답 코드는 201입니다.
1 2 3 4 5 6 7 8 9 10 11 |
postUser = new(curl); postUser.setHeaders(['콘텐츠 유형: 애플리케이션/json']) .setBody('{"name": "' + req.body.사용자 이름 + '","비밀번호": "' + req.body.비밀번호 + '"}') .post(동기화 게이트웨이 엔드포인트 + '/_user/') .다음(({statusCode, body, 헤더}) => { 만약 (statusCode == 201) { // 상태 == 201: 성공 } else { // 사용자를 만들 수 없습니다. } }) |
동기화 게이트웨이에서 사용자에 대한 세션 만들기
동기화 게이트웨이에서 사용자에 대한 세션을 만듭니다. POST /{db}/_session. 이 예에서는 세션이 30분 후에 만료됩니다. 동기화 게이트웨이에서 세션이 성공적으로 생성된 경우 응답 코드는 200입니다. JSON 응답에는 다음 값이 포함됩니다:
세션_id새 세션의 ID입니다. 모바일 앱에서 동기화 게이트웨이를 통한 인증에 사용됩니다.
만료: 세션이 만료될 때의 타임스탬프입니다. 모바일 앱에서는 사용되지 않습니다.
쿠키_이름: 세션 쿠키의 이름입니다. 모바일 앱에서는 사용되지 않습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
resBody = {statusCode: 200}; 포스트 세션 = new(curl); 포스트 세션.setHeaders(['콘텐츠 유형: 애플리케이션/json']) .setBody('{"name": "' + 요청.body.사용자 이름 + '","TTL": 1800}') .post(동기화 게이트웨이 엔드포인트 + '/_session') .다음(({statusCode, body, 헤더}) => { 만약 (statusCode == 200) { // 상태 == 200: 성공 resBody.세션_id = body.세션_id; resBody.만료 = body.만료; resBody.쿠키_이름 = body.쿠키_이름; // 클라이언트에 성공 응답 보내기 응답.보내기(JSON.문자열화(resBody)); } else { // 세션을 만들 수 없습니다. } }) |
인증 테스트
앱 서버에서 인증을 테스트하려면 다음 명령을 사용하여 시작하세요:
1 |
$ 노드 auth.js |
시작되면 다음과 같은 내용이 표시됩니다:
1 |
듣기 on 포트 8080 |
다음과 같이 간단한 curl 명령어(또는 Postman 등)를 사용하여 앱 서버를 테스트할 수 있습니다:
1 |
$ curl -d "사용자명=모바일사용자&비밀번호=비밀번호" -X POST http://:8080/login |
다음과 유사한 출력이 표시됩니다:
1 |
{"statusCode":200,"session_id":"c149012eaa8d0cf15b1b4110cf0a2fec259ef726","만료":"2019-08-01T13:47:56.311076773Z","쿠키_이름":"동기화 게이트웨이 세션"} |
사용자가 LDAP에 존재하지 않으면 다음과 같은 응답이 표시됩니다:
1 |
승인되지 않음 |
앱 서버의 출력은 다음과 같습니다:
1 2 3 4 5 |
모바일 사용자 has been 인증 와 함께 LDAP 만들기 사용자 모바일 사용자 in 동기화 게이트웨이 사용자 모바일 사용자 생성 in 동기화 게이트웨이 create 세션 에 대한 사용자 모바일 사용자 생성 세션 에 대한 모바일 사용자 |
모바일 앱 구현
남은 작업은 모바일 앱에서 앱 서버 및 동기화 게이트웨이로 적절한 호출을 하는 것뿐입니다. 모바일 앱은 발리 프레임워크 를 사용하여 앱 서버에 REST 호출을 수행합니다. 다음 코드 스니펫은 앱 서버에 대한 호출과 세션_id를 사용하여 인증하는 Couchbase Lite 코드를 강조 표시합니다.
모바일 사용자 인증
POST /login/을 호출하여 앱 서버로 사용자를 인증합니다.
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 |
JSONObject reqBody = new JSONObject(); reqBody.put("username", <사용자 제공 사용자 이름>); reqBody.put("비밀번호", <사용자 제공 비밀번호>); 문자열 URL = <앱 서버 호스트>:8080/로그인; 요청 대기열 대기열 = 발리.새로운 요청 대기열(<컨텍스트>); JsonRequest<JSONObject> jsonRequest = new JsonObjectRequest( 요청.방법.POST, URL, reqBody, new 응답.리스너<JSONObject>() { @오버라이드 public void onResponse(JSONObject 응답) { // 응답에서 세션 ID 가져오기 시도 { 문자열 세션ID = 응답.getString("session_id"); // 저장 세션_id } catch (JSONException je) { // 예외 처리 } } }, new 응답.오류 리스너() { @오버라이드 public void onErrorResponse(볼리 에러 오류) { // 인증 실패 } }); 대기열.추가(jsonRequest); |
원샷 복제 생성
저장된 세션_id(13줄)를 사용하여 원샷 복제를 생성하고 동기화 게이트웨이 세션이 만료된 경우 다시 인증합니다.
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 |
문자열 동기화 게이트웨이 엔드포인트 = "ws://:4984/{db}"; URI URL = null; 시도 { URL = new URI(mSyncGatewayEndpoint); } catch (URISyntaxException e) { e.프린트스택트레이스(); 반환; } 리플리케이터 구성 구성 = new 리플리케이터 구성(데이터베이스, new URL엔드포인트(URL)); 구성.setReplicatorType(리플리케이터 구성.리플리케이터 유형.PUSH_AND_PULL); 구성.setContinuous(false); 구성.setAuthenticator(new 세션 인증자(세션ID)); 리플리케이터 리플리케이터 = new 리플리케이터(구성); 리플리케이터.추가 변경 리스너(new 리플리케이터 변경 리스너() { @오버라이드 public void 변경됨(리플리케이터 변경 변경) { 카우치베이스 라이트 예외 오류 = 변경.getStatus().getError(); 만약 (오류 != null) { 만약 (오류.getCode() == 10401) { // 세션 만료; 재인증 } } ... } }); 리플리케이터.시작(); |
다음 단계
Couchbase를 처음 사용하는 경우 다음에서 제공되는 무료 온라인 교육을 활용하세요. https://learn.couchbase.com 를 클릭해 자세히 알아보세요.
자세히 보기 모바일 튜토리얼 에서 찾을 수 있습니다. 카우치베이스 튜토리얼 웹 페이지.