源码已上传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 + "&timestamp=" + 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 + "&timestamp=" + 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 + "&timestamp=" + 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));
});

Java钉钉开发_02_免登授权(身份验证)的更多相关文章

  1. Java钉钉开发_02_免登授权(身份验证)(附源码)

    源码已上传GitHub: https://github.com/shirayner/DingTalk_Demo 一.本节要点 1.免登授权的流程 (1)签名校验 (2)获取code,并传到后台 (3) ...

  2. 钉钉开发入门,微应用识别用户身份,获取用户免登授权码code,获取用户userid,获取用户详细信息

    最近有个需求,在钉钉内,点击微应用,获取用户身份,根据获取到的用户身份去企业内部的用户中心做校验,校验通过,相关子系统直接登陆; 就是在获取这个用户身份的时候,网上的资料七零八落的,找的人烦躁的很,所 ...

  3. 用java实现“钉钉微应用,免登进入某H5系统首页“功能”

    一.前言 哈哈,这是我的第一篇博客. 先说一下这个小功能的具体场景: 用户登录钉钉app,点击微应用,获取当前用户的信息,与H5系统的数据库的用户信息对比,如果存在该用户,则点击后直接进入H5系统的首 ...

  4. 企业微信开发免登授权时提示scope不能为空,错误代码1001

    企业免登授权提示scope不能为空1001 原因是我们是单页面应用url自带#/在微信里面认为#号后面的参数不被识别 后端开发人员把参数放到跳转 URL地址前面,正确形式是 https://open. ...

  5. Java企业微信开发_02_通讯录同步

    一.本节要点 1.获取通讯录密钥 获取方式: 登录企业微信—>管理工具—>通讯录同步助手—>开启“API接口同步”  ; 开启后,即可看到通讯录密钥,也可设置通讯录API的权限:读取 ...

  6. 钉钉开发第三方H5微应用入门详细教程[ISV][免登流程][授权码][HTTP回调推送][识别用户身份][获取用户信息]

    转载请注明原文地址:https://www.cnblogs.com/applerosa/p/11509512.html (by lnexin@aliyun.com 世间草木) 此教程注意点: 适用于第 ...

  7. js api 实现钉钉免登

    js api 实现钉钉免登,用于从钉钉微应用跳转到企业内部的oa,erp等,我刚刚实施完了我公司的这个功能,钉钉用起来还不错. 1 js api 实现钉钉免登,页面配置. <title>利 ...

  8. 钉钉企业内部H5微应用开发

    企业内部H5微应用开发 分为 服务端API和前端API的开发,主要涉及到进入应用免登流程和JSAPI鉴权. JSAPI鉴权开发步骤: 1.创建H5微应用 登入钉钉开放平台(https://open-d ...

  9. 钉钉微应用接入钉钉免登陆配置记录。NET实现

    在这里记录一下我配置的钉钉接入微应用遇到的坑.搞了我几天天才调通.头皮发麻,现在梳理一下,以免别人也入坑. 1.钉钉接入主要要获取钉钉企业员工的ID,然后去自己的应用的数据库里进行匹配然后实现免登陆的 ...

随机推荐

  1. Mysql CAST()函数

      (1).CAST()函数的参数是一个表达式,它包括用AS关键字分隔的源值和目标数据类型.以下例子用于将文本字符串'12'转换为整型: SELECT CAST('12' AS int) (2).返回 ...

  2. CardView的具体使用方法(转)

    转载自:CardView的具体使用方法  因为学习做此记录方便查找使用 今天主要是CardView的用法,CardView是在安卓5.0提出的卡片式控件.首先介绍一下它的配置. 在gradle文件下添 ...

  3. 浅谈Java数据结构和算法

    今天的突然看集合底层的时候发现了好多算法和数据结构.再次就比较一下和汇总一下. 数据结构分类:线性结构和非线性结构 问题一: 什么是线性和非线性: 我个人的理解是:数据结构中线性结构指的是数据元素之间 ...

  4. C语言基础知识【判断】

    C 判断1.判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的).C 语言把任何非零和非空的值假定为 true,把零或 null ...

  5. 读书笔记-HBase in Action-第三部分应用-(1)OpenTSDB

    OpenTSDB是基于HBase的开源监控系统,能够支持上万规模集群监控和上亿数据点採集. 当中TSDB代表Time Series Database,OpenTSDB在时间序列数据的存储和查询上都做了 ...

  6. [Sdoi2013]直径(树的直径)

    //36分 #include<cstdio> #include<cstdlib> #include<cstring> #include<ctime> # ...

  7. [转]Html position(static、relative、absolute、fixed)

    转自:http://blog.csdn.net/topviewers/article/details/21644305 讲解不错,转载备忘. position的四个属性值: 1.relative2.a ...

  8. POJ 2965 The Pilots Brothers' refrigerator【枚举+dfs】

    题目:http://poj.org/problem?id=2965 来源:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=26732#pro ...

  9. vue 指令系统的使用

    所谓指令系统,大家可以联想咱们的cmd命令行工具,只要我输入一条正确的指令,系统就开始干活了. 在vue中,指令系统,设置一些命令之后,来操作我们的数据属性,并展示到我们的DOM上. OK,接下来我们 ...

  10. Android异步载入全解析之使用AsyncTask

    Android异步载入全解析之使用AsyncTask 概述 既然前面提到了多线程,就不得不提到线程池,通过线程池,不仅能够对并发线程进行管理.更能够提高他们运行的效率.优化整个App.当然我们能够自己 ...