LeanMessage 移動開發 SDK 是由 LeanCloud 提供的,專為 iOS、Android 和 WindowsPhone? 等客戶端程序提供應用內聊天的 API 和服務,并且也提供了 JavaScript API,方便開發者打通網頁和客戶端應用,給最終用戶提供統一的使用體驗。使用 LeanMessage API,您可以極快地以最少工作量讓您的移動應用支持實時聊天,得到一種如微信一般的溝通體驗。
開始之前
出于本文的目的,我假設您已經非常熟悉使用 JSON、Android 和 Eclipse 進行移動應用編程的基本概念。在您繼續閱讀本文之前,請訪問 leancloud.cn 并創建您的應用程序。只需遵循注冊頁面中的簡單指令即可。
本文介紹了包含單聊、群聊、歷史記錄和應用鑒權的核心 API 類。您將學習如何簡單進行用戶間一對一單聊,以及如何創建群組讓多用戶進行群聊,還有如何通過簽名來對聊天通道進行控制,以保護應用和用戶的隱私。示例均構建于 LeanMessage SDK for Android 之上(請參閱Android開發指南)。
基本概念和類
聊天參與者 Peer
在 LeanMessage 實時消息世界中,每一個參與者(一般而言是「人」)都是一個 Peer。Peer 擁有一個在應用內唯一標識自己的 ID(稱為 PeerID,字符串類型,長度不超過 50 字節,具體數值由應用自身確定),系統中的每一條消息都來自于一個 Peer,發送到一個或多個 Peer。并且,LeanMessage 的消息服務允許一個 Peer 在多個不同設備上登錄,也允許一個設備上同時登錄多個 Peer,究竟該如何使用,由應用根據使用場景自己選擇。
這里要注意的是,PeerID 是由應用開發者自己賦值的,LeanMessage 本身并沒有任何強制要求,所以:
- 實時消息系統是可以和用戶賬戶系統解耦合的,應用開發者不需要把除了 PeerID 以外的任何信息告訴 LeanMessage;
- LeanMessage 在消息轉發的時候是按照 PeerID 來唯一定位的,因此如果應用自身支持同一賬戶的多點登錄,那么 LeanMessage 就會把消息通知到所有終端;
- 匿名聊天/非匿名聊天這都是應用層自己決定的,如果應用自身能為匿名用戶指定一個唯一的 ID,那么這個用戶參與到聊天系統里來,是完全沒有問題的。
為了防止騷擾,一個 Peer 需要先關注(watch)了對方才能給對方發送消息;因為 LeanMessage 提供了更細粒度的權限控制,應用開發者可以在關注(watch)動作上增加簽名來保證安全性。這一點后面會進行詳細說明。
實時消息 AVMessage
在 LeanMessage 中所有的消息都是 AVMessage 的實例,AVMessage 只支持文本,并且長度不能超過 5KB。消息分為暫態(transient)和持久消息兩種類型。所有持久消息都會在 LeanMessage 云端保存,所以用戶離線之后也可以得到通知和接收,而暫態消息并不會離線保存,適合開發者用來進行協議控制。
AVMessage 的定義如清單 1 所示:
public class AVMessage implements Parcelable {
private List<String> toPeerIds; // 消息接收方的 PeerID,支持一個或多個
String groupId; // 消息所屬群組的ID,對于普通一對一聊天消息而言,此值為空
String message; // 消息體
long timestamp; // 消息發送時間戳
boolean isTransient; // 是否是暫態消息
String fromPeerId; // 消息發送方的 PeerID
public AVMessage();
public AVMessage(String message);
public AVMessage(String message, List<String> toPeerIds, boolean isTransient);
public AVMessage(String message, boolean isTransient);
}
LeanMessage 為所有歷史消息都提供了存儲和查詢的功能,存儲時間則根據開發者的類型有所不同。
聊天會話 Session
每一個 Peer 通過開啟(open)一個會話(Session)而加入實時消息服務,Peer 可以在一個會話中關注(watch)一個或多個 Peer,當被關注者上下線時,會收到通知。Peer 在開啟會話后只能向自己關注的其他 Peers 發送消息,但可以收到任何 Peer 發來的消息,也就是說單向關注時,消息可以順利地由關注者發往被關注者。
Session 有如下幾種狀態:
- opened。 Session 被正常打開,此時可以進行正常的通信;
- pause。 網絡異常(譬如 wifi 斷開,3G/2G 切換,iOS 應用進入后臺,等),Session 進入暫停狀態,當網絡恢復時,Session 會自動重連;
- resume。 應用轉入前臺,會話重新建立起來(此狀態只在 iOS 設備上有效)
- closed。 Session 結束,僅在顯示調用了 Session.close 方法時才會發生。用戶注銷實時通信服務,不再能夠接收到消息或推送通知;
Session 上可以進行的操作有:
- open 以一個 Peer ID 打開 Session
- watch 關注一組 Peer ID,關注后可以收到這個 Peer 的上下線通知,發送消息
- unwatch 取消對一組 Peer ID 的關注
- sendMessage 給一組 Peer ID 發送消息
- queryOnlinePeer 查找當前在線的 Peers
- getHistoryMessageQuery 查找歷史消息
- setSignatureFactory 設置簽名類(為了保證安全性,后面會講述)
- close 注銷服務,關閉 Session
一對一的文本聊天
明白了這三個概念之后,我們就可以開始進入實際聊天環節了。
首先我們需要在 application 的 onCreate 函數中進行 LeanCloud 最基本的初始化:
@Override
public void onCreate() {
super.onCreate();
AVOSCloud.initialize(this, "pleaseReplaceWithYourAppId", "pleaseReplaceWithYourAppKey");
}
接下來我們來看一下怎么樣進行一對一的基本聊天。首先,我們需要開啟一個會話(Session),示例代碼如清單 2 所示:
SessionManager session = SessionManager.getInstance(selfId);//獲取SessionManager實例,以便于后續的操作。這里的 selfId 可以是用戶的實際 id,也可以是其他唯一的字符串,譬如「Tom」。
List<String> watchedIds = new LinkedList<String>();
session.open(watchedIds); //打開Session,同時關注一些 PeerID。此時沒有關注對象
注意!
一般而言,會話的開啟是在用戶登錄之后的 RootActivity 中進行的。對于支持匿名聊天的應用,也可以在 Application 啟動的時候進行。千萬不要在一個臨時或短命的 Activity 中開啟聊天會話。上面代碼中 SessionManager 也是 Session 的子類,所以可以直接調用 Session 的方法。
接下來,我們開始跟「Bob」這個用戶進行聊天。為了給他發送消息,我們先要關注(watch)他,代碼如下:
List<String> peerIds = new LinkedList<String>();
peerIds.add("Bob");
session.watchPeers(peerIds);
之后我們給「Bob」發送一條消息:
List<String> peerIds = new LinkedList<String>();
peerIds.add("Bob");
session.sendMessage(new AVMessage("嗨,你好,我是 Tom", peers, false));
好了,這樣一條消息就發送過去了。但是問題來了,對于「Bob」而言,他怎么才能收到別人發給他的消息呢?
上面對于 Session 的所有操作都是異步的。與一般 Android 異步方法調用不同,LeanMessage SDK 的異步并不是通過 Callback 或者類似 RsyncTask 的機制實現的,而是通過繼承 AVMessageReceiver 這一 BoardcastReceiver,實現以下方法來處理來自服務器端的響應的。AVMessageReceiver 接口定義如清單 3 所示:
/**
* 當服務器成功與客戶端打開session時產生本次回調
*/
public abstract void onSessionOpen(Context context, Session session);
/**
* 在 session 暫停時調用,一般都是由網絡連接丟失導致的隱性調用
*/
public abstract void onSessionPaused(Context context, Session session);
/**
* Session 恢復時,一般都是網絡連接恢復以后的
* 這個時候你可以處理一些由于網絡異常導致的失敗消息
*/
public abstract void onSessionResumed(Context context, Session session);
/**
* 從某個Peer接收到消息時,會收到一次調用
*/
public abstract void onMessage(Context context, Session session,
AVMessage msg);
/**
* 服務器反饋消息已經成功發送時,會收到調用
*/
public abstract void onMessageSent(Context context, Session session,
AVMessage msg);
/**
* 在消息發送失敗時,產生的調用 在這里你可以保存一下發送失敗的消息以便未來重發
*/
public abstract void onMessageFailure(Context context, Session session,
AVMessage msg);
/**
* 當關注的一些peers上線時,產生的調用
*/
public abstract void onStatusOnline(Context context, Session session,
List<String> peerIds);
/**
* 當關注的一些peers下線時,產生的調用
*/
public abstract void onStatusOffline(Context context, Session session,
List<String> peerIds);
/**
* 當與服務器發生交互的過程中的任何錯誤,都在這里被返回
*/
public abstract void onError(Context context, Session session, Throwable e);
從上面接口的定義中,我們可以看到,要接收到別人發過來的消息,只需要響應 onMessage() 方法即可。代碼示例如清單 4 所示:
public class CustomeMsgReceiver extends AVMessageReceiver {
@Override
public void onMessage(final Context context, Session session, AVMessage avMsg) {
Logger.d("onMessage "+avMsg.getMessage());
// 進行上層邏輯處理,譬如 UI 展示,或者消息提醒。
}
}
// 在 AndroidManifest.xml 文件中聲明這一 BoardcastReceiver。
<receiver android:name=".receiver.CustomeMsgReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="com.avoscloud.session.action" />
</intent-filter>
</receiver>
支持富媒體的聊天消息
上面的代碼演示了如何發送文本信息,但是現在的交互方式已經越來越多樣化,圖片、語音、視頻已是非常普遍的媒體類型。而從 AVMessage 的定義來看,只支持不超過 5KB 大小的文本,那么 LeanMessage 又如何能支持富媒體的聊天消息呢?
記得 LeanStorage 中的 AVFile 嗎?
AVFile 是 LeanStorage 提供的非結構化數據存儲解決方案,可以讓你的應用程序將二進制文件存儲到云端服務器中,并且自動提供 CDN 加速服務,能帶給用戶更迅捷的下載體驗。比如常見的文件類型圖像文件、影像文件、音樂文件和任何其他二進制數據都可以使用。具體說明可以參見Android 開發文檔。
對于圖片、語音、視頻這類較大的非結構化數據,存儲到云端文件系統之后,在消息中發送 url 已是業界慣例,并且 LeanMessage 中 AVMessage 類的定義并沒有規定消息體是什么類型,所以我們可以充分利用這一擴展空間,結合 AVFile 來發送、接收富媒體的聊天消息。實現方法如清單 5 所示:
AVFile file = AVFile.withAbsoluteLocalPath("test.jpg", Environment.getExternalStorageDirectory() + "/test.jpg");
file.saveInBackground(new SaveCallback() {
// override
public void done(AVException ex) {
if (null != ex) {
// error
} else {
// construct message body under json format.
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("type", "image");
params.put("context", "嗨,你好,我是 Tom");
params.put("attachment", file.getUrl());
List<String> peerIds = new LinkedList<String>();
peerIds.add("Bob");
session.sendMessage(new AVMessage(JSON.toJSONString(params), peers, false));
}
}
});
新版本的 LeanMessage SDK 會支持富媒體消息,避免讓每個開發者都重復做類似的工作。
群組聊天
在聊天的需求里,還有一個很重要的場景,就是群組聊天。從前面 AVMessage 的定義我們可以猜到,LeanMessage 應該是支持群聊的,那實際上該如何實現呢?下面我們一步一步來嘗試一下。
基本概念
與普通的單聊相比,群聊增加了如下兩個基本概念:
- 群組 AVGroup
AVGroup 代表一個聊天群組,可以對應到實際的多人聊天、聊天群、聊天室等,每個 AVGroup 有一個唯一的 ID(groupID,由 LeanMessage 云端分配),其定義如清單 6 所示:
public class AVGroup implements Group {
String roomId;
String selfId;
Session session;
}
一個 Peer 加入群后向群發送的消息可以被所有群成員收到。當有新成員加入或者既有成員退出時,所有群成員都會得到通知。AVGroup 上可以進行的操作有:
public interface Group{
public void join();
public void sendMessage(AVMessage msg);
public void kickMember(List<String> peerIds);
public void inviteMember(List<String> peerIds);
public void quit();
public String getGroupId();
public String getSelfId();
public AVHistoryMessageQuery getHistoryMessageQuery();
}
- 群組消息接受器 AVGroupMessageReceiver
與 AVMessageReceiver 類似,AVGroupMessageReceiver 主要用來處理群組操作的結果。其詳細定義如清單 7 所示:
/**
*在加入聊天室成功后被調用 如果join時,沒有帶groupId,您可以在返回的group中間獲取groupId
*/
@Override
public abstract void onJoined(Context context, Group group);
/**
* 當你被別人邀請進入某個聊天室以后
*
* @param group
* @param byPeerId
* 這個人邀請了你
*/
@Override
public abstract void onInvited(Context context, Group group, String byPeerId);
/**
* 當你被別人踢出聊天室以后
*
* @param group
* @param byPeerId
* 是他踢了你
*/
@Override
public abstract void onKicked(Context context, Group group, String byPeerId);
/**
* 處理消息發送成功事件
*/
@Override
public abstract void onMessageSent(Context context, Group group,
AVMessage message);
/**
* 用來處理消息發送失敗事件
* 可以緩存起來,事后重發
*/
@Override
public abstract void onMessageFailure(Context context, Group group,
AVMessage message);
/**
* 收到消息以后被調用
* 一般通過這個接口來處理和接受來自Group的消息
* @param context
* @param group
* @param message
* @param fromPeerId
* 發消息者
*/
@Override
public abstract void onMessage(Context context, Group group,
AVMessage message);
/**
* 處理退出成功事件
*/
@Override
public abstract void onQuit(Context context, Group group);
/**
* 處理Group操作被拒絕的時間
* @param context
* @param group
* @param op 這里可能存在的操作有 "join","invite","kick"
* @param targetIds
* 一般來說是指被操作的對象,在join操作中間就是指groupId本身,
* invite和kick中則指被邀請或者被踢除的peerIds
*/
@Override
public abstract void onReject(Context context, Group group, String op,
List<String> targetIds);
/**
* 處理新用戶加入事件
*/
@Override
public abstract void onMemberJoin(Context context, Group group,
List<String> joinedPeerIds);
/**
* 處理用戶退出事件
*/
@Override
public abstract void onMemberLeft(Context context, Group group,
List<String> leftPeerIds);
/**
* 處理所有Group相關的異常
*/
@Override
public abstract void onError(Context context, Group group, Throwable e);
加入聊天室
由于整個實時通信功能都是建立在 Session 的基礎上,所以我們要加入一個聊天室也需要建立在一個已經打開的 Session 上。 已經打開一個 Session 以后,可以通過以下操作來加入一個 Group:
Group group = SessionManager.getInstance(selfId).getGroup();//準備新建一個聊天室
//Group group = SessionManager.getInstance(selfId).getGroup(groupId); 加入一個已經存在的聊天室
group.join(); // LeanMessage 云端會判斷 groupId 是否存在,如果不存在就新建一個 Group,否則加入已有 Group
加入成功之后 AVGroupMessageReceiver 子類中的 onJoined 方法就會被調用。
往聊天室發送消息
發送消息非常簡單,通過如下代碼就可以向特定聊天室發送消息了:
Group group = SessionManager.getInstance(selfId).getGroup(groupId);
group.sendMessage(new AVMessage("hello world"));
發送成功之后,AVGroupMessageReceiver 子類中的 onMessageSent 方法會被調用,反之則 onMessageFailure 方法會被調用。
接收聊天室消息
接收一個聊天室的消息,與接收單聊的消息一樣,需要開發者實現 AVGroupMessageReceiver 接口,并在 AndroidManifest.xml 中注冊即可,如代碼清單 8 所示:
public class CustomeGroupMsgReceiver extends AVGroupMessageReceiver {
...
@Override
public void onMessage(final Context context, Group group, AVMessage avMsg) {
Logger.d("onMessage "+avMsg.getMessage());
// 進行上層邏輯處理,譬如 UI 展示,或者消息提醒。
}
...
}
// 在 AndroidManifest.xml 文件中聲明這一 BoardcastReceiver。
<receiver android:name=".receiver.CustomeGroupMsgReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="com.avoscloud.session.action" />
</intent-filter>
</receiver>
查詢聊天室成員
在加入一個聊天室之后,我們第一步就是看看有哪些人在這個群組里面。LeanMessage 和 LeanStorage 是結合在一起的,通過使用 LeanStorage 的數據存儲功能,來保存一個聊天室的基本信息(表名:AVOSRealtimeGroups),在 LeanStorage 應用管理平臺的數據中心,我們可以看到 AVOSRealtimeGroups 的所有字段。
LeanStorage 的數據中心
LeanStorage 也是 LeanCloud 平臺的核心服務之一,提供了應用內數據和文件數據的存儲功能。對于應用內數據,LeanStorage 支持 schema free 的存儲,開發者不需要事先定義數據的模式,只要符合 JSON 格式的 Object 都可以自由存儲到 LeanStorage 云端。同時,LeanStorage 也提供一個 Web 版的數據管理界面,可以非常方便地增、刪、改、查任何數據。
當然,在我們知道一個聊天室的 groupId 的時候,也可以在代碼中,通過 AVObject 的 fetch 接口來查看這個聊天室的組員情況,代碼如清單 9 所示:
AVObject groupObject = AVObject.createWithoutData("AVOSRealtimeGroups",groupId);
groupObject.fetch();//如果您在UI進程中,請使用異步方法調用
List groupMembers= groupObject.getList("m");
謹防系統線程阻塞!
回想一下,在移動應用程序中,長時間的操作(如網絡、文件或長的計算)不應該在主系統線程上完成。相反,應在一個單獨的工作線程中執行它們。阻塞系統線程會對應用程序的用戶界面的響應能力產生負面影響,有可能導致強行關閉您的應用程序。
成員管理
在查詢到聊天室成員之后,可以讓用戶邀請一些自己的朋友加入,作為管理員也可以剔除一些「可怕」的成員。代碼如清單 10 所示:
Group group = SessionManager.getInstance(selfId).getGroup(groupId);
List<String> toInvite = Arrays.asList("peerId1","peerId2","peerId3");
group.inviteMember(toInvite);
List<String> toKickOff = Arrays.asList("badBoy1","badBoy2");
group.kickMembers(toKickOff);
邀請成功以后,通知的流程是這樣的:
操作者(管理員) 被邀請者 其他人
1,發出請求 inviteMember
2,AVGroupMessageReceiver.onInvited
3, AVGroupMessageReceiver.onJoined
4,AVGroupMessageReceiver.onMemberJoin AVGroupMessageReceiver.onMemberJoin
相應地,踢人的流程如下:
操作者(管理員) 被踢者 其他人
1,發出請求 kickMember
2,AVGroupMessageReceiver.onKicked
3, AVGroupMessageReceiver.onQuit
4,AVGroupMessageReceiver.onMemberLeft AVGroupMessageReceiver.onMemberLeft
查詢歷史消息
LeanMessage 會將非暫態消息自動保存在云端,之后開發者可以通過 AVHistoryMessageQuery 這個對象來進行查詢。AVHistoryMessageQuery 定義如清單 11 所示:
public class AVHistoryMessageQuery {
int limit;
String convid;
String from;
long timestamp;
/**
* 設置查詢返回集合的大小
* 默認100,最大1000
*/
public void setLimit(int limit);
/**
* 設定聊天的發起人是誰
*/
public void setFrom(String from);
/**
* 設置查詢從哪個時間開始的聊天記錄
*/
public void setTimestamp(long timestamp);
/**
* 指定聊天記錄查詢條件中,聊天發送的對象條件
*/
public void setPeerIds(List<String> peerIds);
/**
* 同步方法查詢聊天記錄
* 請確保在一個異步方法中調用此方法,否則會出現UI線程中的網絡請求而導致的UI卡死
*/
public List<AVHistoryMessage> find() throws AVException;
/**
* 異步方法查詢聊天記錄
*/
public void findInBackground(HistoryMessageCallback callback);
/**
* 此接口為異步查詢聊天記錄的回調類
*/
public static interface HistoryMessageCallback;
}
通過 AVHistoryMessageQuery 查詢得到的結果是 AVHistoryMessage,該類的定義如清單 12 所示:
public class AVHistoryMessage extends AVMessage {
/**
* 查看是否屬于聊天室聊天記錄
*/
public boolean isRoom();
/**
* 查看聊天記錄所在的conversation Id,對于 Group 來說等于 GroupID,對于單聊來說,是內部生成的一個值。
*/
public String getConvid();
}
聊天記錄的查詢的基本方法跟 AVQuery 類似但是略有不同。 針對 Session 的聊天記錄和聊天室 Group 的聊天記錄查詢略有不同,但是基本流程是一樣(代碼清單 12):
String selfId = "Tom";
SessionManager sm = SessionManager.getInstance(selfId);
List<String> peers = new ArrayList<String>();
peers.add(selfId);
peers.add("Bob");
AVHistroyMessageQuery sessionHistoryQuery = sm.getHistroyMessageQuery();
sessionHistoryQuery.setLimit(100); //設置查詢結果大小
sessionHistoryQuery.setPeerIds(peers); // 設置單聊的參與方,多個參與者之間是「與」的關系
sessionHistoryQuery.setTimestamp(1413184345686); //查詢時間片1413184345686以前的聊天記錄
sessionHistoryQuery.findInBackground(new HistoryMessageCallback() {
@Override
public void done(List<AVHistoryMessage> messages, AVException error) {
System.out.println(messages.size());
}
});//查詢session里的聊天記錄
Group group = sm.getGroup("140a534fd092809500e6d651e73400c7");
AVHistroyMessageQuery groupHistoryQuery = group.getHistoryMessageQuery();//獲取AVHistoryMessageQuery對象來查詢聊天室的聊天記錄
groupHistoryQuery.findInBackground(new HistoryMessageCallback(){
@Override
public void done(List<AVHistoryMessage> messages,AVException error){
for(AVHistoryMessage msg:messages){
System.out.println(msg.getMessage());
}
}
})
上面第一個查詢會拿到「Tom」和「Bob」在特定時間點以前的 100 條聊天記錄;第二個查詢會拿到特定聊天室的所有聊天記錄(如果總數不超過 1000 條的話)。
一覽查看所有聊天室
查看所有聊天室的方法和查看單個聊天室成員的方法類似,都是直接通過 AVQuery 或者 AVObject 來遍歷 AVOSRealtimeGroups 表實現的,這里不再贅述。
聊天記錄和安全
前面實現了單聊、群聊、富媒體聊天諸多功能,但是開發者可能已經發現了,這都是直接調用 LeanMessage SDK 來實現的,對于我們開發者來說,能控制的東西很少,在安全性上會存在一些擔心。譬如:萬一不懷好意的人破解了我的 appId 和 appKey,是不是就可以在我的聊天社區里面為所欲為?
為了滿足開發者對權限和認證的要求,LeanMessage 還設計了操作簽名的機制。我們可以在 LeanCloud 應用控制臺、設置、應用選項中強制啟用簽名(強烈推薦這樣做)。啟用后,所有的 Session open 和 watch 行為都需要驗證簽名,這樣開發者就可以對用戶登錄以及他可以關注哪些人,進而可以給哪些人發消息進行充分的控制。
簽名采用 Hmac-sha1 算法,輸出字節流的十六進制字符串 (hex dump),簽名的消息格式如下:
app_id:peer_id:watch_peer_ids:timestamp:nonce
其中:
- app_id 是你的應用 ID
- peer_id 是打開此 Session 的 Peer ID
- watch_peer_ids 是 open 或 watch 請求中關注的 peer ids,升序排序后以: 分隔
- timestamp 是當前的 UTC 時間距離 unix epoch 的秒數
- nonce 為隨機字符串
在群組操作中,LeanMessage 對加群、邀請和踢出群這三個動作也允許加入簽名,它的簽名格式是:
app_id:peer_id:group_id:group_peer_ids:timestamp:nonce:action
其中:
- app_id, peer_id, timestamp 和 nonce 同上
- group_id 是此次行為關聯的群組 ID,對于創建群尚沒有 id 的情況,group_id 是空字符串
- group_peer_ids 是: 分隔的升序排序的 peer id,即邀請和踢出的 peer_id;對加入群的情況,這里是空字符串
- action 是此次行為的動作,三種行為分別對應常量 join, invite 和 kick
簽名的 key 是應用的 master key。開發者可以實現自己的 SignatureFactory,調用遠程的服務器的簽名接口獲得簽名。如果沒有自己的服務器,可以直接在 LeanCloud 的云代碼上通過 Web Hosting 動態接口實現自己的簽名接口。在移動應用中直接做簽名是非常危險的,它可能導致你的 master key 泄漏。
LeanCloud 的 appKey 分類
在 LeanCloud 平臺上申請了應用之后,LeanCloud 會分配給我們兩個 key:一個 appKey,一個 master Key。其中 appKey 可以執行一些普通的操作,并且受到 LeanCloud 平臺安全設置的限制,類似于操作系統中的普通 User 賬號,所以可以直接用在客戶端;master Key 則擁有所有權限,類似于操作系統中的 Root/Administrator 賬號,所以請妥善保管。
好,有了簽名機制之后,我們究竟該如何使用呢?我們只需要實現自己的 SignatureFactory,然后在開啟 session 的時候,把這個 signatureFactory 傳進去即可。示例代碼如清單 13 所示:
// Signature 定義如下,由 LeanMessage 提供
public class Signature {
private String signature;
private long timestamp;
private String nonce;
private List<String> signedPeerIds;
// getter / setter for properties
......
}
// customise signature factory,由開發者實現
public MySignatureFactory implements SignatureFactory {
@override
public Signature createSignature(String selfId, List<String> watchIds) {
// call remote server for correct signature.
}
@override
public Signature createGroupSignature(String groupId, String selfId, List<String> targetPeerIds,
String action) {
// call remote server for correct group signature.
}
}
// open session with signature factory.
SignatureFactory signatureFacatory = new MySignatureFactory();
SessionManager sm = SessionManager.getInstance(selfId);
sm.setSignatureFactory(signatureFactory);
sm.open(selfId);
設定了 SignatureFactory 之后,對于需要鑒權的操作,LeanMessage SDK 與服務器端通訊的時候都會帶上應用自己生成的 Signature 信息,這樣 LeanMessage 服務器端就會使用 app 的 masterKey 來驗證信息的有效性,保證聊天渠道的安全。
結束語
LeanMessage 是一個非常穩定可靠的聊天服務平臺,提供的功能也足以滿足我們應用開發者的需求。這里我通過介紹 LeanMessage API 來實現應用內的單聊、群聊、富媒體消息等基本功能,但是 LeanMessage 還支持更多高階功能,譬如 Super Peer、敏感詞過濾、消息實時監聽等等,有興趣的朋友可以繼續探索。