源碼已上傳GitHub: https://github.com/shirayner/DingTalk_Demo
一、本節要點
1.免登授權的流程
(1)簽名校驗
(2)獲取code,並傳到后台
(3)根據code獲取userid
(4)根據userid獲取用戶信息,(此處可進行相應業務處理)
(5)將用戶信息傳到前端,前端拿到用戶信息,並做相應處理
2.計算簽名信息(signature)
2.1 待簽名參數
ticket | jsapi_ticket |
nonceStr | 隨機字符串,隨機生成 |
timeStamp | 時間戳 |
url | 當前網頁的URL,不包含#及其后面部分 |
2.2簽名流程
(1)字典序
將所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式 (即 key1=value1&key2=value2…)拼接成字符串string1
如:String string1= "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "×tamp=" + timeStamp + "&url=" + url;
(2)SHA-1簽名,得到 signature

/** * @desc : 3.生成簽名的函數 * * @param ticket jsticket * @param nonceStr 隨機串,自己定義 * @param timeStamp 生成簽名用的時間戳 * @param url 需要進行免登鑒權的頁面地址,也就是執行dd.config的頁面地址 * @return * @throws Exception String */ public static String getSign(String jsTicket, String nonceStr, Long timeStamp, String url) throws Exception { String plainTex = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "×tamp=" + timeStamp + "&url=" + url; System.out.println(plainTex); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(plainTex.getBytes("UTF-8")); return byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { throw new Exception(e.getMessage()); } catch (UnsupportedEncodingException e) { throw new Exception(e.getMessage()); } } //將bytes類型的數據轉化為16進制類型 private static String byteToHex(byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", new Object[] { Byte.valueOf(b) }); } String result = formatter.toString(); formatter.close(); return result; }
3.簽名校驗的流程
3.1 后端准備好前端校驗參數
后台方法:getConfig(HttpServletRequest)

public static String getConfig(HttpServletRequest request){ //1.准備好參與簽名的字段 /* *以http://localhost/test.do?a=b&c=d為例 *request.getRequestURL的結果是http://localhost/test.do *request.getQueryString的返回值是a=b&c=d */ String urlString = request.getRequestURL().toString(); String queryString = request.getQueryString(); String queryStringEncode = null; String url; if (queryString != null) { queryStringEncode = URLDecoder.decode(queryString); url = urlString + "?" + queryStringEncode; } else { url = urlString; } String nonceStr=UUID.randomUUID().toString(); //隨機數 long timeStamp = System.currentTimeMillis() / 1000; //時間戳參數 String signedUrl = url; String accessToken = null; String ticket = null; String signature = null; //簽名 //2.進行簽名,獲取signature try { accessToken=AuthHelper.getAccessToken(Env.CORP_ID, Env.CORP_SECRET); ticket=AuthHelper.getJsapiTicket(accessToken); signature=getSign(ticket,nonceStr,timeStamp,signedUrl); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("accessToken:"+accessToken); System.out.println("ticket:"+ticket); System.out.println("nonceStr:"+nonceStr); System.out.println("timeStamp:"+timeStamp); System.out.println("signedUrl:"+signedUrl); System.out.println("signature:"+signature); System.out.println("agentId:"+Env.AGENTID); System.out.println("corpId:"+Env.CORP_ID); String configValue = "{jsticket:'" + ticket + "',signature:'" + signature + "',nonceStr:'" + nonceStr + "',timeStamp:'" + timeStamp + "',corpId:'" + Env.CORP_ID + "',agentId:'" + Env.AGENTID + "'}"; System.out.println(configValue); return configValue; }
3.2 前端接收后台參數
在前端調用后端方法,獲取dd.config所需的校驗參數:‘url’,‘nonceStr’,‘agentId’,‘timeStamp’,‘corpId’,‘signature’。

<script type="text/javascript"> var _config =<%=com.ray.dingtalk.auth.AuthHelper.getConfig(request)%>; </script>
3.3 執行前端 dd.config ,進行簽名校驗
dd.config 用接收到的 nonceStr、agentId、timeStamp、corpId這四個參數去釘釘官方后端計算出一個簽名(signature ), 並將這個簽名與我們后端所計算的signature來進行比對,若一致,則校驗通過。若不一致,則是我們后端計算簽名的時候出錯了。此時可根據錯誤消息提示去進行調試。

dd.config({ agentId : _config.agentId, corpId : _config.corpId, timeStamp : _config.timeStamp, nonceStr : _config.nonceStr, signature : _config.signature, jsApiList : [ //需要調用的借口列表 'runtime.info', 'biz.contact.choose', //選擇用戶接口 'device.notification.confirm', 'device.notification.alert', //confirm,alert,prompt都是彈出小窗口的接口 'device.notification.prompt', 'biz.ding.post', 'biz.util.openLink' ] });
3.4 異常:js加載順序有誤所引起的 前端什么信息都不提示
出現這個原因,可能是自己js出錯了。我的原因是js加載順序有誤。
請注意這幾個js的加載順序: _config,jquery-3.2.1.min.js 必須在auth.js之前加載

<script src="js/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="http://g.alicdn.com/dingding/open-develop/1.6.9/dingtalk.js"></script> <script type="text/javascript"> var _config =<%=com.ray.dingtalk.auth.AuthHelper.getConfig(request)%>; </script> <script type="text/javascript" src="js/auth.js"></script>
4. 將code送往后端:ajax
簽名校驗成功之后,即dd.config校驗成功之后,會執行dd.ready函數,這時我們就可以使用釘釘的jsapi了。
簽名校驗成功后,我們就可以調用獲取免登授權碼(CODE)的jsapi,來獲取code,然后通過ajax方式將這個code傳到后台userInfoServlet

/**獲取免登授權碼 CODE * */ dd.runtime.permission.requestAuthCode({ corpId : _config.corpId, onSuccess : function(info) { //成功獲得code值,code值在info中 alert('authcode: ' + info.code); /* *$.ajax的是用來使得當前js頁面和后台服務器交互的方法 *參數url:是需要交互的后台服務器處理代碼,userInfoServlet *參數type:指定和后台交互的方法,因為后台servlet代碼中處理Get和post的doGet和doPost *data:負責傳遞請求參數 *其中success方法和error方法是回調函數,分別表示成功交互后和交互失敗情況下處理的方法 */ $.ajax({ type : "POST", url : "http://p65s3p.natappfree.cc/DingTalk_Demo/userInfoServlet", data : { code : info.code }, success : function(data, status, xhr) { alert(data); var userInfo = JSON.parse(data); document.getElementById("userName").innerHTML = userInfo.name; document.getElementById("userId").innerHTML = userInfo.userid; // 圖片 if(info.avatar.length != 0){ var img = document.getElementById("userImg"); img.src = info.avatar; img.height = '200'; img.width = '200'; } }, error : function(xhr, errorType, error) { logger.e("yinyien:" + _config.corpId); alert(errorType + ', ' + error); } }); }, onFail : function(err) { //獲得code值失敗 alert('fail: ' + JSON.stringify(err)); } });
5.根據code獲取userid

private static final String GET_USERINFO_BYCODE_URL="https://oapi.dingtalk.com/user/getuserinfo?access_token=ACCESSTOKEN&code=CODE"; /** 5.根據免登授權碼Code查詢免登用戶userId * @desc :釘釘服務器返回的用戶信息為: * userid 員工在企業內的UserID * deviceId 手機設備號,由釘釘在安裝時隨機產生 * is_sys 是否是管理員 * sys_level 級別,0:非管理員 1:超級管理員(主管理員) 2:普通管理員(子管理員) 100:老板 * * @param accessToken * @param code * @throws Exception void */ public JSONObject getUserInfo(String accessToken,String code) throws Exception { //1.獲取請求url String url=GET_USERINFO_BYCODE_URL.replace("ACCESSTOKEN", accessToken).replace("CODE", code); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); System.out.println("jsonObject:"+jsonObject.toString()); //3.解析結果,獲取User if (null != jsonObject) { //4.請求成功,則返回jsonObject if (0==jsonObject.getInteger("errcode")) { return jsonObject; } //5.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return null; }
6.根據userid獲取用戶信息

private static final String GET_USER_URL="https://oapi.dingtalk.com/user/get?access_token=ACCESSTOKEN&userid=USERID"; /** 2.根據userid獲取成員詳情 * @desc :獲取成員詳情 * 參考文檔: https://open-doc.dingtalk.com/docs/doc.htm?spm=0.0.0.0.jjSfQQ&treeId=371&articleId=106816&docType=1#s0 * @param accessToken * @param userId void * @throws Exception */ public JSONObject getUser(String accessToken, String userId) throws Exception { //1.獲取請求url String url=GET_USER_URL.replace("ACCESSTOKEN", accessToken).replace("USERID", userId); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); System.out.println("jsonObject:"+jsonObject.toString()); //3.解析結果,獲取User if (null != jsonObject) { //4.請求成功,則返回jsonObject if (0==jsonObject.getInteger("errcode")) { return jsonObject; } //5.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return null; }
7.將用戶信息傳到前端
注意:傳輸格式為json

//3.通過userid換取用戶信息 JSONObject jsonObject=us.getUser(accessToken, userId); result=JSON.toJSON(jsonObject); PrintWriter out = response.getWriter(); out.print(result); out.close(); out = null;
8.前端接收用戶信息后做相應處理
jsp中代碼:

<div align="center"> <img id="userImg" alt="頭像" src=""> </div> <div align="center"> <span>UserName:</span> <div id="userName" style="display: inline-block"></div> </div> <div align="center"> <span>UserId:</span> <div id="userId" style="display: inline-block"></div> </div>
js中代碼:發送code的ajax調用成功后

success : function(data, status, xhr) { alert(data); //接收后端發送過來的用戶信息 var userInfo = JSON.parse(data); //收到用戶信息后所做的處理 document.getElementById("userName").innerHTML = userInfo.name; document.getElementById("userId").innerHTML = userInfo.userid; // 圖片 if(info.avatar.length != 0){ var img = document.getElementById("userImg"); img.src = info.avatar; img.height = '200'; img.width = '200'; } },
二、代碼實現
1.釘釘參數配置——Env.java
將Env.java中的配置修改成你自己的

package com.ray.dingtalk.config; /**@desc : 企業應用接入時的常量定義 * * @author: shirayner * @date : 2017年9月27日 下午4:57:36 */ public class Env { /** * 企業應用接入秘鑰相關 */ public static final String CORP_ID = "ding6d4828968696691535c2f4657eb6378f"; public static final String CORP_SECRET = "ZigmkCY4VcsGUhLIzmfxOmP0ElJbGI5uBhn-2mPelovnjPcA6e4LrjpYXQQw89Q4"; public static final String SSO_Secret = "YgIGtCHmcwAmOuKsAo_lgqJJiOwyez2G6vBvhCf1zwR6kZ5DGMJsxOcUgK5p1C"; public static final String AGENTID = "128838526"; /** * DING API地址 */ public static final String OAPI_HOST = "https://oapi.dingtalk.com"; /** * 企業應用后台地址,用戶管理后台免登使用 */ public static final String OA_BACKGROUND_URL = ""; /** * 企業通訊回調加密Token,注冊事件回調接口時需要傳遞給釘釘服務器 */ public static final String TOKEN = ""; public static final String ENCODING_AES_KEY = ""; }
2.Http請求工具類——HttpHelper.java
主要包括發送GET請求和POST請求

package com.ray.dingtalk.util; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import javax.servlet.http.HttpServletRequest; import org.apache.http.HttpEntity; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.util.EntityUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ray.dingtalk.auth.AuthHelper; import com.ray.dingtalk.config.Env; /** * HTTP請求封裝,建議直接使用sdk的API */ public class HttpHelper { /** * @desc :1.發起GET請求 * * @param url * @return JSONObject * @throws Exception */ public static JSONObject httpGet(String url) throws Exception { //1.創建httpClient CloseableHttpClient httpClient = HttpClients.createDefault(); //2.生成一個請求 HttpGet httpGet = new HttpGet(url); //3.配置請求的屬性 RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(2000).setConnectTimeout(2000).build(); httpGet.setConfig(requestConfig); //4.發起請求,獲取響應信息 CloseableHttpResponse response = null; try { response = httpClient.execute(httpGet, new BasicHttpContext()); //如果返回結果的code不等於200,說明出錯了 if (response.getStatusLine().getStatusCode() != 200) { System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode() + ", url=" + url); return null; } //5.解析請求結果 HttpEntity entity = response.getEntity(); //reponse返回的數據在entity中 if (entity != null) { String resultStr = EntityUtils.toString(entity, "utf-8"); //將數據轉化為string格式 JSONObject result = JSON.parseObject(resultStr); //將String轉換為 JSONObject if (result.getInteger("errcode") == 0) { return result; } else { System.out.println("request url=" + url + ",return value="); System.out.println(resultStr); int errCode = result.getInteger("errcode"); String errMsg = result.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } } catch (IOException e) { System.out.println("request url=" + url + ", exception, msg=" + e.getMessage()); e.printStackTrace(); } finally { if (response != null) try { response.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } /** 2.發起POST請求 * @desc : * * @param url * @param data * @return * @throws Exception JSONObject */ public static JSONObject httpPost(String url, Object data) throws Exception { HttpPost httpPost = new HttpPost(url); CloseableHttpResponse response = null; CloseableHttpClient httpClient = HttpClients.createDefault(); RequestConfig requestConfig = RequestConfig.custom(). setSocketTimeout(2000).setConnectTimeout(2000).build(); httpPost.setConfig(requestConfig); httpPost.addHeader("Content-Type", "application/json"); try { StringEntity requestEntity = new StringEntity(JSON.toJSONString(data), "utf-8"); httpPost.setEntity(requestEntity); response = httpClient.execute(httpPost, new BasicHttpContext()); if (response.getStatusLine().getStatusCode() != 200) { System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode() + ", url=" + url); return null; } HttpEntity entity = response.getEntity(); if (entity != null) { String resultStr = EntityUtils.toString(entity, "utf-8"); JSONObject result = JSON.parseObject(resultStr); if (result.getInteger("errcode") == 0) { result.remove("errcode"); result.remove("errmsg"); return result; } else { System.out.println("request url=" + url + ",return value="); System.out.println(resultStr); int errCode = result.getInteger("errcode"); String errMsg = result.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } } catch (IOException e) { System.out.println("request url=" + url + ", exception, msg=" + e.getMessage()); e.printStackTrace(); } finally { if (response != null) try { response.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } }
3.釘釘相關接口權限的獲取工具類——AuthHelper.java
主要包括:AccessToken、JsapiTicket、以及簽名校驗的工具類

package com.ray.dingtalk.auth; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Formatter; import java.util.HashMap; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSONObject; import com.ray.dingtalk.config.Env; import com.ray.dingtalk.util.HttpHelper; /** * 釘釘相關配置參數的獲取工具類 * @desc : AccessToken和jsticket的獲取封裝 * * @author: shirayner * @date : 2017年9月27日 下午5:00:25 */ public class AuthHelper { //private static Logger log = LoggerFactory.getLogger(AuthHelper.class); //獲取access_token的接口地址,有效期為7200秒 private static final String GET_ACCESSTOKEN_URL="https://oapi.dingtalk.com/gettoken?corpid=CORPID&corpsecret=CORPSECRET"; //獲取getJsapiTicket的接口地址,有效期為7200秒 private static final String GET_JSAPITICKET_URL="https://oapi.dingtalk.com/get_jsapi_ticket?access_token=ACCESSTOKE"; /** 1.獲取access_token * @desc : * * @param corpId * @param corpSecret * @return * @throws Exception String */ public static String getAccessToken(String corpId,String corpSecret) throws Exception { //1.獲取請求url String url=GET_ACCESSTOKEN_URL.replace("CORPID", corpId).replace("CORPSECRET", corpSecret); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); //3.解析結果,獲取accessToken String accessToken=""; if (null != jsonObject) { accessToken=jsonObject.getString("access_token"); //4.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return accessToken; } /** * 2、獲取JSTicket, 用於js的簽名計算 * 正常的情況下,jsapi_ticket的有效期為7200秒,所以開發者需要在某個地方設計一個定時器,定期去更新jsapi_ticket * @throws Exception */ public static String getJsapiTicket(String accessToken) throws Exception { //1.獲取請求url String url=GET_JSAPITICKET_URL.replace("ACCESSTOKE", accessToken); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); //3.解析結果,獲取ticket String ticket=""; if (null != jsonObject) { ticket=jsonObject.getString("ticket"); //4.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return ticket; } /** * @desc : 3.生成簽名的函數 * * @param ticket jsticket * @param nonceStr 隨機串,自己定義 * @param timeStamp 生成簽名用的時間戳 * @param url 需要進行免登鑒權的頁面地址,也就是執行dd.config的頁面地址 * @return * @throws Exception String */ public static String getSign(String jsTicket, String nonceStr, Long timeStamp, String url) throws Exception { String plainTex = "jsapi_ticket=" + jsTicket + "&noncestr=" + nonceStr + "×tamp=" + timeStamp + "&url=" + url; System.out.println(plainTex); try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(plainTex.getBytes("UTF-8")); return byteToHex(crypt.digest()); } catch (NoSuchAlgorithmException e) { throw new Exception(e.getMessage()); } catch (UnsupportedEncodingException e) { throw new Exception(e.getMessage()); } } //將bytes類型的數據轉化為16進制類型 private static String byteToHex(byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", new Object[] { Byte.valueOf(b) }); } String result = formatter.toString(); formatter.close(); return result; } /** * @desc :獲取前端jsapi需要的配置參數(已棄用,請用getConfig(HttpServletRequest)) * * @param request request:在釘釘中點擊微應用圖標跳轉的url地址 * @return Map<String,Object> 將需要的參數存入map,並返回 */ public static Map<String, Object> getDDConfig(HttpServletRequest request){ Map<String, Object> configMap = new HashMap<String, Object>(); //1.准備好參與簽名的字段 /* *以http://localhost/test.do?a=b&c=d為例 *request.getRequestURL的結果是http://localhost/test.do *request.getQueryString的返回值是a=b&c=d */ String urlString = request.getRequestURL().toString(); String queryString = request.getQueryString(); String queryStringEncode = null; String url; if (queryString != null) { queryStringEncode = URLDecoder.decode(queryString); url = urlString + "?" + queryStringEncode; } else { url = urlString; } String nonceStr=UUID.randomUUID().toString(); //隨機數 long timeStamp = System.currentTimeMillis() / 1000; //時間戳參數 String signedUrl = url; String accessToken = null; String ticket = null; String signature = null; //簽名 //2.進行簽名,獲取signature try { accessToken=AuthHelper.getAccessToken(Env.CORP_ID, Env.CORP_SECRET); ticket=AuthHelper.getJsapiTicket(accessToken); signature=getSign(ticket,nonceStr,timeStamp,signedUrl); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("accessToken:"+accessToken); System.out.println("ticket:"+ticket); System.out.println("nonceStr:"+nonceStr); System.out.println("timeStamp:"+timeStamp); System.out.println("signedUrl:"+signedUrl); System.out.println("signature:"+signature); System.out.println("agentId:"+Env.AGENTID); System.out.println("corpId:"+Env.CORP_ID); //3.將配置參數存入Map configMap.put("agentId", Env.AGENTID); configMap.put("corpId", Env.CORP_ID); configMap.put("timeStamp", timeStamp); configMap.put("nonceStr", nonceStr); configMap.put("signature", signature); return configMap; } public static String getConfig(HttpServletRequest request){ //1.准備好參與簽名的字段 /* *以http://localhost/test.do?a=b&c=d為例 *request.getRequestURL的結果是http://localhost/test.do *request.getQueryString的返回值是a=b&c=d */ String urlString = request.getRequestURL().toString(); String queryString = request.getQueryString(); String queryStringEncode = null; String url; if (queryString != null) { queryStringEncode = URLDecoder.decode(queryString); url = urlString + "?" + queryStringEncode; } else { url = urlString; } String nonceStr=UUID.randomUUID().toString(); //隨機數 long timeStamp = System.currentTimeMillis() / 1000; //時間戳參數 String signedUrl = url; String accessToken = null; String ticket = null; String signature = null; //簽名 //2.進行簽名,獲取signature try { accessToken=AuthHelper.getAccessToken(Env.CORP_ID, Env.CORP_SECRET); ticket=AuthHelper.getJsapiTicket(accessToken); signature=getSign(ticket,nonceStr,timeStamp,signedUrl); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("accessToken:"+accessToken); System.out.println("ticket:"+ticket); System.out.println("nonceStr:"+nonceStr); System.out.println("timeStamp:"+timeStamp); System.out.println("signedUrl:"+signedUrl); System.out.println("signature:"+signature); System.out.println("agentId:"+Env.AGENTID); System.out.println("corpId:"+Env.CORP_ID); String configValue = "{jsticket:'" + ticket + "',signature:'" + signature + "',nonceStr:'" + nonceStr + "',timeStamp:'" + timeStamp + "',corpId:'" + Env.CORP_ID + "',agentId:'" + Env.AGENTID + "'}"; System.out.println(configValue); return configValue; } }
4.用戶業務類——UserService.java

package com.ray.dingtalk.service.contact; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ray.dingtalk.model.contact.User; import com.ray.dingtalk.util.HttpHelper; /**@desc : * * @author: shirayner * @date : 2017年9月28日 上午9:53:51 */ public class UserService { private static final String CREATE_USER_URL="https://oapi.dingtalk.com/user/create?access_token=ACCESSTOKEN"; private static final String GET_USER_URL="https://oapi.dingtalk.com/user/get?access_token=ACCESSTOKEN&userid=USERID"; private static final String GET_DEPARTMENTUSER_URL="https://oapi.dingtalk.com/user/simplelist?access_token=ACCESSTOKEN&department_id=DEPARTMENTID"; private static final String GET_DEPARTMENTUSERDETAIL_URL="https://oapi.dingtalk.com/user/list?access_token=ACCESSTOKEN&department_id=DEPARTMENTID"; private static final String GET_USERINFO_BYCODE_URL="https://oapi.dingtalk.com/user/getuserinfo?access_token=ACCESSTOKEN&code=CODE"; /**1.創建用戶 * @desc : * * @param accessToken * @param user * @return * @throws Exception String */ public String createUser(String accessToken,User user) throws Exception { //1.准備POST請求參數 Object data=JSON.toJSON(user); System.out.println(data); //2.獲取請求url String url=CREATE_USER_URL.replace("ACCESSTOKEN", accessToken); //3.發起POST請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpPost(url, data); System.out.println("jsonObject:"+jsonObject.toString()); //4.解析結果,獲取UserId String userId=""; if (null != jsonObject) { userId=jsonObject.getString("userid"); //5.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return userId; } /** 2.根據userid獲取成員詳情 * @desc :獲取成員詳情 * 參考文檔: https://open-doc.dingtalk.com/docs/doc.htm?spm=0.0.0.0.jjSfQQ&treeId=371&articleId=106816&docType=1#s0 * @param accessToken * @param userId void * @throws Exception */ public JSONObject getUser(String accessToken, String userId) throws Exception { //1.獲取請求url String url=GET_USER_URL.replace("ACCESSTOKEN", accessToken).replace("USERID", userId); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); System.out.println("jsonObject:"+jsonObject.toString()); //3.解析結果,獲取User if (null != jsonObject) { //4.請求成功,則返回jsonObject if (0==jsonObject.getInteger("errcode")) { return jsonObject; } //5.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return null; } /** 3.獲取部門成員 * @desc : * * @param accessToken * @param departmentId * @throws Exception void */ public void getDepartmentUser(String accessToken, String departmentId) throws Exception { //1.獲取請求url String url=GET_DEPARTMENTUSER_URL.replace("ACCESSTOKEN", accessToken).replace("DEPARTMENTID", departmentId); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); System.out.println("jsonObject:"+jsonObject.toString()); //3.解析結果,獲取User if (null != jsonObject) { //4.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } } /** 4.獲取部門成員(詳情) * @desc : * * @param accessToken * @param departmentId * @throws Exception void */ public void getDepartmentUserDetail(String accessToken, String departmentId) throws Exception { //1.獲取請求url String url=GET_DEPARTMENTUSERDETAIL_URL.replace("ACCESSTOKEN", accessToken).replace("DEPARTMENTID", departmentId); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); System.out.println("jsonObject:"+jsonObject.toString()); //3.解析結果,獲取User if (null != jsonObject) { //4.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } } /** 5.根據免登授權碼Code查詢免登用戶userId * @desc :釘釘服務器返回的用戶信息為: * userid 員工在企業內的UserID * deviceId 手機設備號,由釘釘在安裝時隨機產生 * is_sys 是否是管理員 * sys_level 級別,0:非管理員 1:超級管理員(主管理員) 2:普通管理員(子管理員) 100:老板 * * @param accessToken * @param code * @throws Exception void */ public JSONObject getUserInfo(String accessToken,String code) throws Exception { //1.獲取請求url String url=GET_USERINFO_BYCODE_URL.replace("ACCESSTOKEN", accessToken).replace("CODE", code); //2.發起GET請求,獲取返回結果 JSONObject jsonObject=HttpHelper.httpGet(url); System.out.println("jsonObject:"+jsonObject.toString()); //3.解析結果,獲取User if (null != jsonObject) { //4.請求成功,則返回jsonObject if (0==jsonObject.getInteger("errcode")) { return jsonObject; } //5.錯誤消息處理 if (0 != jsonObject.getInteger("errcode")) { int errCode = jsonObject.getInteger("errcode"); String errMsg = jsonObject.getString("errmsg"); throw new Exception("error code:"+errCode+", error message:"+errMsg); } } return null; } }
5.Servlet——UserInfoServlet
(1)UserInfoServlet.java

package com.ray.dingtalk.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ray.dingtalk.auth.AuthHelper; import com.ray.dingtalk.config.Env; import com.ray.dingtalk.service.contact.UserService; /**身份認證Servlet:免登 * * * Servlet implementation class AuthServlet */ @WebServlet("/UserInfoServlet") public class UserInfoServlet extends HttpServlet { private static final long serialVersionUID = 1L; public UserInfoServlet() { super(); // TODO Auto-generated constructor stub } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.getWriter().append("Served at: ").append(request.getContextPath()); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //1.將請求、響應的編碼均設置為UTF-8(防止中文亂碼) request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); //1.獲取code String code = request.getParameter("code"); System.out.println("code:"+code); Object result=null; try { //2.通過CODE換取身份userid String accessToken = AuthHelper.getAccessToken(Env.CORP_ID, Env.CORP_SECRET); UserService us = new UserService(); String userId=us.getUserInfo(accessToken, code).getString("userid"); //3.通過userid換取用戶信息 JSONObject jsonObject=us.getUser(accessToken, userId); result=JSON.toJSON(jsonObject); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } PrintWriter out = response.getWriter(); out.print(result); out.close(); out = null; } }
(2)web.xml

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:web="http://java.sun.com/xml/ns/javaee" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <servlet-name>userInfoServlet</servlet-name> <servlet-class> com.ray.dingtalk.servlet.UserInfoServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>userInfoServlet</servlet-name> <url-pattern>/userInfoServlet</url-pattern> </servlet-mapping> </web-app>
6.前端代碼
(1)IDAuthentication.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>身份認證</title> <script src="js/jquery-3.2.1.min.js"></script> <script type="text/javascript" src="http://g.alicdn.com/dingding/open-develop/1.6.9/dingtalk.js"></script> <script type="text/javascript"> var _config =<%=com.ray.dingtalk.auth.AuthHelper.getConfig(request)%>; </script> <script type="text/javascript" src="js/auth.js"></script> </head> <body> <div align="center"> <img id="userImg" alt="頭像" src=""> </div> <div align="center"> <span>UserName:</span> <div id="userName" style="display: inline-block"></div> </div> <div align="center"> <span>UserId:</span> <div id="userId" style="display: inline-block"></div> </div> <div align="center"> <span class="desc">是否驗證成功</span> <button class="btn btn_primary" id="yanzheng">ceshi</button> </div> <div align="center"> <span class="desc">測試按鈕</span> <button class="btn btn_primary" id="ceshi">ceshi</button> </div> </body> </html>
(2)auth.js

dd.config({ agentId : _config.agentId, corpId : _config.corpId, timeStamp : _config.timeStamp, nonceStr : _config.nonceStr, signature : _config.signature, jsApiList : [ //需要調用的借口列表 'runtime.info', 'biz.contact.choose', //選擇用戶接口 'device.notification.confirm', 'device.notification.alert', //confirm,alert,prompt都是彈出小窗口的接口 'device.notification.prompt', 'biz.ding.post', 'biz.util.openLink' ] }); dd.ready(function() { document.getElementById("yanzheng").innerHTML = "驗證成功"; document.querySelector('#ceshi').onclick = function () { alert("ceshiaaa"); }; /* 1.獲取容器信息 *獲取容器信息,返回值為ability:版本號,也就是返回容器版本 *用來表示這個版本的jsapi的能力,來決定是否使用jsapi */ dd.runtime.info({ onSuccess : function(info) { logger.e('runtime info: ' + JSON.stringify(info)); }, onFail : function(err) { logger.e('fail: ' + JSON.stringify(err)); } }); /**獲取免登授權碼 CODE * */ dd.runtime.permission.requestAuthCode({ corpId : _config.corpId, onSuccess : function(info) { //成功獲得code值,code值在info中 alert('authcode: ' + info.code); /* *$.ajax的是用來使得當前js頁面和后台服務器交互的方法 *參數url:是需要交互的后台服務器處理代碼,userInfoServlet *參數type:指定和后台交互的方法,因為后台servlet代碼中處理Get和post的doGet和doPost *data:負責傳遞請求參數 *其中success方法和error方法是回調函數,分別表示成功交互后和交互失敗情況下處理的方法 */ $.ajax({ type : "POST", url : "http://p65s3p.natappfree.cc/DingTalk_Demo/userInfoServlet", data : { code : info.code }, success : function(data, status, xhr) { alert(data); //接收后端發送過來的用戶信息 var userInfo = JSON.parse(data); //收到用戶信息后所做的處理 document.getElementById("userName").innerHTML = userInfo.name; document.getElementById("userId").innerHTML = userInfo.userid; // 圖片 if(info.avatar.length != 0){ var img = document.getElementById("userImg"); img.src = info.avatar; img.height = '200'; img.width = '200'; } }, error : function(xhr, errorType, error) { logger.e("yinyien:" + _config.corpId); alert(errorType + ', ' + error); } }); }, onFail : function(err) { //獲得code值失敗 alert('fail: ' + JSON.stringify(err)); } }); }); //在dd.config函數驗證失敗時執行 dd.error dd.error(function(err) { //驗證失敗 alert("進入到error中"); document.getElementById("userName").innerHTML = "驗證出錯"; alert('dd error: ' + JSON.stringify(err)); });