源码已上传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. Java Web开发笔记

    问题: 读取资源文件问题 servletContext.getRealPath() servletContext.getResouce().getPath() setvletContext.getRe ...

  2. 【Python + Mysql】之用pymysql库连接Mysql数据库并进行增删改查操作

    用pip下载pymysql并引用 具体请参考文章: <Python之MySQL数据库增删改查操作> <python3.6 使用 pymysql 连接 Mysql 数据库及 简单的增删 ...

  3. 嵌入式驱动开发之sensor---"VIP0 PortA", "VIP0 PortB", "VIP1 PortA", "VIP1 PortB",dvo0(vout1) dvo1(vout0)

    (1)vip 简介 (2)vip 电路图 (3)vip 更换采集相机输入 (4)vip 驱动 ---------------------author:pkf --------------------- ...

  4. Linux5_环境变量

    1.总结背景 在linux系统下,下载并安装了应用程序,很有可能在键入它的名称时出现“command not found”的提示内容. 每次都到安装目标文件夹内,找到可执行文件来进行操作就太繁琐了.这 ...

  5. Python中使用__new__实现单例模式并解析

    阅读文章前请先阅读 Python中类方法.__new__方法和__init__方法解析 单例模式是一个经典设计模式,简要的说,一个类的单例模式就是它只能被实例化一次,实例变量在第一次实例化时就已经固定 ...

  6. 利用solr6.5,tomcat9.0和centos7.0的搭建

    第一步:去官网下载所需的软件包, jdk1.8   wget http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff ...

  7. 【BZOJ3563/3569】DZY Loves Chinese II 线性基神题

    [BZOJ3563/3569]DZY Loves Chinese II Description 神校XJ之学霸兮,Dzy皇考曰JC. 摄提贞于孟陬兮,惟庚寅Dzy以降. 纷Dzy既有此内美兮,又重之以 ...

  8. OLTP和OLAP

    1 OLTP和OLAP online transaction processing,联机事务处理.业务类系统主要供基层人员使用,进行一线业务操作,通常被称为联机事务处理. online analyti ...

  9. images have the “stationarity” property, which implies that features that are useful in one region are also likely to be useful for other regions.

    Convolutional networks may include local or global pooling layers[clarification needed], which combi ...

  10. 深入ConcurrentHashMap二

    深入ConcurrentHashMap一,已经介绍了主要的ConcurrentHashMap的结构,Segment组成,HashEntry的组成以及包含ConcurrentHashMap的创建. 这篇 ...