釘釘開發入門,微應用識別用戶身份,獲取用戶免登授權碼code,獲取用戶userid,獲取用戶詳細信息
最近有個需求,在釘釘內,點擊微應用,獲取用戶身份,根據獲取到的用戶身份去企業內部的用戶中心做校驗,校驗通過,相關子系統直接登陸;
就是在獲取這個用戶身份的時候,網上的資料七零八落的,找的人煩躁的很,所以自己記錄一下;
實現這個要求,有好幾種方式,使用ISV方式相對來說比較簡單一點,獲取的到的信息雖然沒有其他方式那麽全,但是也包含了百分之七八十的信息,少了角色信息之類的;
效果:(demo的GIT地址在文末)
說說步驟:
1.去OA 控制臺創建一個微應用: https://oa.dingtalk.com
這個首頁跳轉地址,信任了以後,就可以直接使用js-sdk來獲取用戶code等相關信息,最方便的一種.
如果是別的頁面,使用js-sdk 需要進行dd.config的初始化,這個初始化裏面,包含了相關的權限校驗.
2.應用創建完了以後,會生成一個agentID,
如果僅僅只是為了獲取當前點擊用戶的信息,並且獲取的位置是在這個首頁地址的js裏面,則大可以不用管這個信息,但是,如果需要更加復雜的操作,就需要獲取這個ID,獲取方法在創建完了以後,右上角的小三角下拉,有個設置,點進去就能看到
另外,關於js-sdk的需要鑒權的api信息查詢地址:jsapi列表(是否需要dd.config校驗)
列表裏面不需要的接口調用,都不需要進行dd.config()
3.獲取釘釘開發的corpID和corpSecert
進入釘釘開發者平臺獲取:http://open-dev.dingtalk.com
一般獲取上面的足夠.web sso免登可能需要下面的SSOSecert;
4.準備工作做完
現在我們有以下信息:
corpID:
corpSecert:
agentID:
url:這個url就是你需要獲取用戶code的那個頁面url
當然如果只是簡單的獲取用戶信息,不需要進行dd.config的話,可以不用管agrntID和url
5.進入開發(這裏只是做獲取當前用戶信息的示例)
(1).前端頁面引入 dingtalk.js
(2).在頁面添加 獲取code 的 js 代碼,
(3).將獲取的 code 發送到後臺處理
(4). 後臺先根據corpID,corpSecert獲取一個accessToken(這個token是獲取其他信息的一個關鍵key) https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.QmfTwJ&treeId=385&articleId=104980&docType=1#s2
後臺根據 code 和 accessToken 獲取 userinfo , 這個獲取到的是一個簡單的用戶信息,包括userid,時候管理員等 https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.QmfTwJ&treeId=385&articleId=104969&docType=1#s0
後臺根據上一步返回的簡單的,包含userid的信息,拿到userid
後臺根據userid 和accessToken 獲取用戶的詳細信息 https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.QmfTwJ&treeId=385&articleId=106816&docType=1#s0
(5).返回給前臺顯示,或者進行後續開發
貼一貼這個流程中關鍵一點的代碼:
前端頁面在引入js 後,或有一個dd的全局變量,這個就是js-sdk,如果需要權限校驗的,就要放在最前邊
關於免登授權碼 code 的獲取 : https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.xwmxzP&treeId=171&articleId=107553&docType=1
dd.ready(function() { dd.runtime.permission.requestAuthCode({ corpId : "這裏是你的corpID", onSuccess : function(result) { var code = result.code; alert(code);
//將code 發往後臺處理 }, onFail : function(err) { alert(‘出錯了, ‘ + err); } }); });
後臺處理部分:
AuthHelper.java 文末提供
在接收到授權碼以後:
String accessToken = AuthHelper.getAccessToken(CORP_ID, CORP_SECRET);
String user = AuthHelper.getUserInfo(code, accessToken);
當返回正確的時候,這個user 裏面結果大致是這樣的:
{ "errcode": 0, "errmsg": "ok", "userid": "USERID", "deviceId":"DEVICEID", "is_sys": true, "sys_level": 0|1|2 }
然後根據裏面的userid,獲取詳細的用戶信息:
String userall = AuthHelper.getUser(userid, accessToken);
返貨正確的話,這個userall裏面的結果大致是:(具體查看釘釘開發文檔)
{ "errcode": 0, "unionid": "PiiiPyQqBNBii0HnCJ3zljcuAiEiE", "openId": "PiiiPyQqBNBii0HnCJ3zljcuAiEiE", "roles": [{ "id": 23003585, "name": "財務", "groupName": "職務" }], "remark": "備註", "userid": "04232334556237185", "isLeaderInDepts": "{1:false}", "isBoss": false, "hiredDate": 1520265600000, "isSenior": false, "tel": "010-88996533", "department": [1,2], "workPlace": "北京市朝陽區", "email": "[email protected]", "orderInDepts": "{1:71738366882504}", "dingId": "$:LWCP_v1:$aTPvVHhhsCMtDZRQ1xbYGg==", "mobile": "15901516821", "errmsg": "ok", "active": false, "avatar": "dingtalk.com/abc.jpg", "isAdmin": false, "isHide": false, "jobnumber": "001", "name": "測試名字", "extattr": {}, "stateCode": "86", "position": "總監" }
然後簡單的獲取信息到此結束;
註意的是:
如果需要更多的操作,就需要在前端頁面進行dd.config的初始化,這個裏面的所需要的sign,可以在後臺根據相關信息生成,是必不可少的,生成規則見AuthHelper.java(其他工具類見文末的 git 地址)
import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import cn.jlhd.util.HttpHelper; import cn.jlhd.util.JsonUtil; import cn.jlhd.util.ReturnUtil; /** * * 1.獲取accessToken * 2.獲取jsapi中的ticket * 3.生成jsapiz中的鑒權sign * 4.根據傳入的臨時code獲取用戶的基本信息,入userinfo * 5.根據userid獲取詳細用戶信息 * * @author lnexin * */ public class AuthHelper { // 釘釘api相關 static String TOKEN_URL = "https://oapi.dingtalk.com/gettoken"; static String TICKET_URL = "https://oapi.dingtalk.com/get_jsapi_ticket"; static String USER_INFO_URL = "https://oapi.dingtalk.com/user/getuserinfo"; static String USER_ALL_URL = "https://oapi.dingtalk.com/user/get"; // 調整到1小時50分鐘 public static final long cacheTime = 1000 * 60 * 55 * 2; private static String ACCESS_TOKEN = null; private static String JSAPI_TICKET = null; private static long LAST_TIME = 0; /** * * @param corpId * @param corpSecert * @return 與釘釘服務器請求生成的accessToken */ public static String getAccessToken(String corpId, String corpSecert) { long curTime = System.currentTimeMillis(); long differ = curTime - LAST_TIME; if (ACCESS_TOKEN != null && differ < cacheTime) return ACCESS_TOKEN; ACCESS_TOKEN = requestAccessToken(corpId, corpSecert); LAST_TIME = curTime; return ACCESS_TOKEN; } /** * * @param accessToken * * @see getAccess_Token(String corpId, String corpSecert) 生成的access_token * @return 一個用於js鑒權的ticket */ public static String getJsapiTicket(String accessToken) { long curTime = System.currentTimeMillis(); long differ = curTime - LAST_TIME; if (JSAPI_TICKET != null && differ < cacheTime) { return JSAPI_TICKET; } JSAPI_TICKET = requestJsapiTicket(accessToken); return JSAPI_TICKET; } /** * 根據傳入的相關參數生成sign * * @param ticket * @param nonceStr * @param timeStamp * @param url * @return */ public static String sign(String ticket, String nonceStr, long timeStamp, String url) { StringBuffer plain = new StringBuffer(); plain.append("jsapi_ticket=").append(ticket); plain.append("&noncestr=").append(nonceStr); plain.append("×tamp=").append(String.valueOf(timeStamp)); plain.append("&url=").append(url); MessageDigest sha; try { sha = MessageDigest.getInstance("SHA-1"); sha.reset(); sha.update(plain.toString().getBytes("UTF-8")); return bytesToHex(sha.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } private static String requestAccessToken(String corpId, String corpSecert) { StringBuffer url = new StringBuffer(TOKEN_URL); url.append("?corpid=").append(corpId); url.append("&corpsecret=").append(corpSecert); String result = null; try { result = HttpHelper.sendGet(url.toString()); } catch (IOException e) { result = ReturnUtil.result("-1", "請求accessTokenc出錯!corpid:" + corpId + ",corpsecert:" + corpSecert + "異常信息:" + e); } return JsonUtil.getJsonNode(result).get("access_token").asText(); } private static String requestJsapiTicket(String accessToken) { StringBuffer url = new StringBuffer(TICKET_URL); url.append("?access_token=").append(accessToken); String result = null; try { result = HttpHelper.sendGet(url.toString()); } catch (IOException e) { result = ReturnUtil.result("-1", "請求JsapiTicket出錯!accessToken:" + accessToken + "異常信息:" + e); } return JsonUtil.getJsonNode(result).get("ticket").asText(); } private static String bytesToHex(byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } /** * 獲取用戶信息 * * @param code * 用戶相應的臨時code * @param token * 根據相應corpid和corpsecret生成的access_token * @return 用戶ID等相關信息 */ public static String getUserInfo(String code, String accessToken) { StringBuffer url = new StringBuffer(USER_INFO_URL); url.append("?access_token=").append(accessToken); url.append("&code=").append(code); String result = null; try { result = HttpHelper.sendGet(url.toString()); } catch (IOException e) { result = ReturnUtil.result("-1", "請求User信息出錯!code:" + code + "異常信息:" + e); } return result; } /** * 獲取用戶詳細信息 * @param userid 在某個corpid下的唯一用戶userid * @param accessToken 據相應corpid和corpsecret生成的access_token * @return */ public static String getUser(String userid, String accessToken) { StringBuffer url = new StringBuffer(USER_ALL_URL); url.append("?access_token=").append(accessToken); url.append("&userid=").append(userid); String result = null; try { result = HttpHelper.sendGet(url.toString()); } catch (IOException e) { result = ReturnUtil.result("-1", "請求User信息出錯!userid:" + userid + "異常信息:" + e); } return result; } }
做了一個簡單demo獲取用戶信息:
關於釘釘的接口封裝GIT 地址: https://gitee.com/lne/DTalkApi
關於獲取信息的簡單demo地址:https://gitee.com/lne/dtalk_login_simple_demo
釘釘開發入門,微應用識別用戶身份,獲取用戶免登授權碼code,獲取用戶userid,獲取用戶詳細信息