|  | 
 
 
 楼主|
发表于 2019-9-18 17:08:17
|
显示全部楼层 
| 本帖最后由 chaohai 于 2019-9-19 08:38 编辑 
 自己研究出来了,
 第一步,钉钉新建H5应用
 其中首页应用首页地址:http://ip地址/ddlogin
 第二步:复制一下index.jsp命名为ddindex.jsp 修改jsp文件添加脚本如下
 
 <script type="text/javascript" src="https://g.alicdn.com/dingding/open-develop/1.6.9/dingtalk.js">
 </script>
 <script type="text/javascript">
 var _config = <%= com.jeecg.dingding.AuthHelper.getConfig(request) %>;
 
 /***************************开始****************************/
 /**
 * _config 这个参数是在前台的H5文件中我定义的,它的值是通过调用步骤6中封装好的参数来获得的
 */
 /*
 我们需要明白的一点是,所有的这些文件都是放在企业应用的服务器后台,和钉钉网站没有半毛钱的关系
 并且钉钉的jsapi中唯一的作用就是提供了对config的验证和获得code值
 对于其他值得获取,如access_token,ticket,sign,username,userid都是自己在后台写java代码通过get或者post方式向
 钉钉开发平台请求得来的,并不是从jsapi中的接口得来的
 */
 dd.config({                                                //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',     //confirm,alert,prompt都是弹出小窗口的接口
 'device.notification.alert',
 'device.notification.prompt',
 'biz.util.openLink' ]
 });
 
 
 /*
 *在dd.config()验证通过的情况下,就会执行ready()函数,
 *dd.ready参数为回调函数,在环境准备就绪时触发,jsapi的调用需要保证在
 *该回调函数触发后调用,否则无效,所以你会发现所有对jsapi接口的调用都会在
 *ready的回调函数里面
 */
 dd.ready(function() {
 
 /*
 *获取容器信息,返回值为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));
 }
 });
 
 /*
 *获得免登授权码,需要的参数为corpid,也就是企业的ID
 *成功调用时返回onSuccess,返回值在function的参数info中,具体操作可以在function中实现
 *返回失败时调用onFail
 */
 dd.runtime.permission.requestAuthCode({
 corpId : _config.corpId,
 onSuccess : function(info) {                                                   //成功获得code值,code值在info中
 //alert('authcode: ' + info.code);
 /*
 *$.ajax的是用来使得当前js页面和后台服务器交互的方法
 *参数url:是需要交互的后台服务器处理代码,这里的userinfo对应WEB-INF -> classes文件中的UserInfoServlet处理程序
 *参数type:指定和后台交互的方法,因为后台servlet代码中处理Get和post的doGet和doPost
 *原本需要传输的参数可以用data来存储的,格式为data:{"code":info.code,"corpid":_config.corpid}
 *其中success方法和error方法是回调函数,分别表示成功交互后和交互失败情况下处理的方法
 */
 $.ajax({
 url : 'loginController.do?checkuserbydd',//根据零时授权code获取用户相关数据并与当前系统用户进行匹配,存在调用登录接口,不存在退出
 type : 'GET',
 data:{"code":info.code,"corpid":_config.corpId},
 success : function(data, status, xhr) {
 var result = JSON.parse(data);
 if(!result.success){
 alert(result.msg);
 }
 window.location.href="loginController.do?login";
 },
 error : function(xhr, errorType, error) {
 logger.e("公司ID:" + _config.corpId);
 alert(errorType + ', ' + error);
 window.location.href="loginController.do?login";
 }
 });
 },
 onFail : function(err) {
 alert('登录失败: ' + JSON.stringify(err));
 window.location.href="loginController.do?login";
 }
 });
 });
 
 
 
 /*
 *在dd.config函数验证没有通过下执行这个函数
 */
 dd.error(function(err) {
 alert('登录失败:' + JSON.stringify(err));
 window.location.href="loginController.do?login";
 });
 </script>
 第三步:
 新增后台钉钉接口相关处理方法
 package com.jeecg.dingding;
 
 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.ClientProtocolException;
 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.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
 import com.alibaba.fastjson.JSONObject;
 
 public class AuthHelper {
 
 
 
 /*
 * params:
 *                 url:需要Get请求的网址
 *
 * return:
 *                 返回请求时网页相应的数据,用json存储
 */
 public static JSONObject httpGet(String url){
 //创建httpClient
 CloseableHttpClient httpClient=HttpClients.createDefault();
 
 HttpGet httpGet=new HttpGet(url);                             //生成一个请求
 RequestConfig requestConfig = RequestConfig.custom().         //配置请求的一些属性
 setSocketTimeout(2000).setConnectTimeout(2000).build();
 httpGet.setConfig(requestConfig);                             //为请求设置属性
 
 CloseableHttpResponse response=null;
 
 try {
 response=httpClient.execute(httpGet);
 
 //如果返回结果的code不等于200,说明出错了
 if (response.getStatusLine().getStatusCode() != 200) {
 System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()+ ", url=" + url);
 return null;
 }
 
 HttpEntity entity = response.getEntity();                 //reponse返回的数据在entity中
 
 if(entity!=null){
 String resultStr=EntityUtils.toString(entity,"utf-8");//将数据转化为string格式
 
 JSONObject result=JSONObject.parseObject(resultStr);  //将结果转化为json格式
 if(result.getInteger("errcode")==0){                  //如果返回值得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("ErrorCode:"+errCode+"ErrorMsg"+errMsg);
 }
 }
 } catch (ClientProtocolException e) {
 // TODO Auto-generated catch block
 System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
 e.printStackTrace();
 } catch (Exception e) {
 // TODO Auto-generated catch block
 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;
 }
 
 
 /*
 * params:
 *                 url:需要Post请求的网址
 *
 * return:
 *                 返回请求时网页相应的数据,用json存储
 */
 public static JSONObject httpPost(String url){
 //创建httpClient
 CloseableHttpClient httpClient=HttpClients.createDefault();
 
 HttpPost httpost=new HttpPost(url);                             //生成一个请求
 RequestConfig requestConfig = RequestConfig.custom().         //配置请求的一些属性
 setSocketTimeout(2000).setConnectTimeout(2000).build();
 httpost.setConfig(requestConfig);                             //为请求设置属性
 
 CloseableHttpResponse response=null;
 
 try {
 response=httpClient.execute(httpost);
 
 //如果返回结果的code不等于200,说明出错了
 if (response.getStatusLine().getStatusCode() != 200) {
 System.out.println("request url failed, http code=" + response.getStatusLine().getStatusCode()+ ", url=" + url);
 return null;
 }
 
 HttpEntity entity = response.getEntity();                 //reponse返回的数据在entity中
 
 if(entity!=null){
 String resultStr=EntityUtils.toString(entity,"utf-8");//将数据转化为string格式
 
 JSONObject result=JSONObject.parseObject(resultStr);  //将结果转化为json格式
 if(result.getInteger("errcode")==0){                  //如果返回值得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("ErrorCode:"+errCode+"ErrorMsg"+errMsg);
 }
 }
 } catch (ClientProtocolException e) {
 // TODO Auto-generated catch block
 System.out.println("request url=" + url + ", exception, msg=" + e.getMessage());
 e.printStackTrace();
 } catch (Exception e) {
 // TODO Auto-generated catch block
 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;
 }
 
 
 
 //然后是调用httpGet方法获得access_token的代码实现:
 public static String getAccess_Token(String corpid,String corpsecret){
 String url="https://oapi.dingtalk.com/gettoken?"+"corpid="+corpid+"&corpsecret="+corpsecret;
 
 JSONObject res=httpGet(url);                      //将httpGet方法封装在HttpHelper类中
 String access_token="";
 if(res!=null){
 access_token=res.getString("access_token");
 }
 else{
 new Exception("Cannot resolve field access_token from oapi resonpse");
 }
 return access_token;
 
 }
 
 
 /*
 * 向网页请求ticket值,用Get方式请求网页
 * param:
 *         access_token:上面得到的access_token值
 *
 * return:
 *         返回值是ticket
 */
 public static String getTicket(String access_token){
 String url="https://oapi.dingtalk.com/get_jsapi_ticket?"+
 "access_token="+access_token;
 
 JSONObject res=httpGet(url);                                //步骤3中有httpGet的定义,只是封装在HttpHelper类中
 String ticket="";
 if(res!=null){
 ticket=res.getString("ticket");
 }
 else{
 new Exception("Cannot resolve field ticket from oapi resonpse");
 }
 return ticket;
 
 }
 
 
 /*
 * 生成签名的函数
 * params:
 *         ticket:签名数据
 *         nonceStr:签名用的随机字符串,从properties文件中读取
 *         timeStamp:生成签名用的时间戳
 *         url:当前请求的URL地址
 */
 public static String getSign(String ticket, String nonceStr, long timeStamp, String url) throws Exception {
 String plain = "jsapi_ticket=" + ticket + "&noncestr=" + nonceStr + "×tamp=" + String.valueOf(timeStamp)
 + "&url=" + url;
 try {
 MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
 sha1.reset();
 sha1.update(plain.getBytes("UTF-8"));
 return bytesToHex(sha1.digest());
 } catch (NoSuchAlgorithmException e) {
 throw new Exception(e.getMessage());
 } catch (UnsupportedEncodingException e) {
 throw new Exception(e.getMessage());
 }
 }
 
 private static String bytesToHex(byte[] hash) {
 Formatter formatter = new Formatter();
 for (byte b : hash) {
 formatter.format("%02x", b);
 }
 String result = formatter.toString();
 formatter.close();
 return result;
 }
 
 
 /*
 * 将所有需要传送到前端的参数进行打包,在前端会调用jsapi提供的dd.config接口进行签名的验证
 *params:
 *        request:在钉钉中点击微应用图标跳转的url地址
 *return:
 *        将需要的参数打包好,按json格式打包
 */
 public static String getConfig(HttpServletRequest request){
 /*
 *以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 url=null;
 if(queryString!=null){
 url=urlString+queryString;
 }
 else{
 url=urlString;
 }
 
 String corpId=Env.CORP_ID;        //一些比较重要的不变得参数本人存储在properties文件中
 String corpSecret=Env.CORP_SECRET;
 String nonceStr="abcdefg";
 String agentId =Env.AGENT_ID;  //agentid参数
 long timeStamp = System.currentTimeMillis() / 1000;     //时间戳参数
 String signedUrl = url;                                 //请求链接的参数,这个链接主要用来生成signatrue,并不需要传到前端
 String accessToken = null;                              //token参数
 String ticket = null;                                   //ticket参数
 String signature = null;                                //签名参数
 
 try {
 
 accessToken=getAccess_Token(corpId,corpSecret);
 ticket=getTicket(accessToken);
 signature=getSign(ticket,nonceStr,timeStamp,signedUrl);
 
 } catch (Exception e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
 }
 
 return "{jsticket:'" + ticket + "',signature:'" + signature + "',nonceStr:'" + nonceStr + "',timeStamp:'"
 + timeStamp + "',corpId:'" + corpId + "',agentId:'" + agentId+ "'}";
 
 }
 
 
 /*
 * 通过零时授权码向网页请求用户信息值,用Get方式请求网页
 * param:
 *         access_token:上面得到的access_token值
 *
 * return:
 *         返回值是userid
 */
 public static String getUserInfo(String access_token,String code){
 String url="https://oapi.dingtalk.com/user/getuserinfo?"+
 "access_token="+access_token+"&code="+code;
 
 JSONObject res=httpGet(url);                                //步骤3中有httpGet的定义,只是封装在HttpHelper类中
 String userid="";
 if(res!=null){
 userid=res.getString("userid");
 }
 else{
 new Exception("在钉钉系统未获取到对应的用户");
 }
 return userid;
 
 }
 
 }
 相关参数配置类
 package com.jeecg.dingding;
 
 
 
 public class Env {
 
 public static final String OAPI_HOST = "https://oapi.dingtalk.com";
 public static final String OA_BACKGROUND_URL = "";
 public static final String CORP_ID = "******自己的CORPID****";
 public static final String AGENT_ID="******自己的AGENTID****";
 public static final String CORP_SECRET = "******自己的CORP_SECRET ****";
 public static final String SSO_Secret = "";
 
 
 public static String suiteTicket;
 public static String authCode;
 public static String suiteToken;
 
 public static final String CREATE_SUITE_KEY = "suite4xxxxxxxxxxxxxxx";
 public static final String SUITE_KEY = "";
 public static final String SUITE_SECRET = "";
 public static final String TOKEN = "";
 public static final String ENCODING_AES_KEY = "";
 
 }
 
 第四步:
 
 loginController类增加方法checkuserByDD  校验用户
 /**
 * 校验钉钉登录用户
 *
 * @param user
 * @param req
 * @return
 */
 @RequestMapping(params = "checkuserbydd")
 @ResponseBody
 public AjaxJson checkuserByDD(HttpServletRequest req) {
 HttpSession session = req.getSession();
 AjaxJson j = new AjaxJson();
 //语言选择
 if (req.getParameter("langCode")!=null) {
 req.getSession().setAttribute("lang", req.getParameter("langCode"));
 }
 String code=req.getParameter("code");
 String corp_id=req.getParameter("corpid");
 String accessToken=AuthHelper.getAccess_Token(corp_id,Env.CORP_SECRET);
 String userid=AuthHelper.getUserInfo(accessToken, code);
 List<TSUser>        users=userService.findHql("from TSUser where 1 = 1 AND userName = ?", "admin");
 if(users.size()>0&&users!=null){
 TSUser user=users.get(0);
 //用户登录验证逻辑
 TSUser u = userService.checkUserExits(user);
 if (u == null) {
 u = userService.findUniqueByProperty(TSUser.class, "email", user.getUserName());
 
 if(u == null || !u.getPassword().equals(PasswordUtil.encrypt(u.getUserName(),user.getPassword(), PasswordUtil.getStaticSalt()))){
 
 j.setMsg(mutiLangService.getLang("common.username.or.password.error"));
 j.setSuccess(false);
 return j;
 }
 }
 if (u != null && u.getStatus() != 0) {
 
 if(u.getDeleteFlag()==1){
 j.setMsg(mutiLangService.getLang("common.username.or.password.error"));
 j.setSuccess(false);
 return j;
 }
 
 if("2".equals(u.getUserType())){
 j.setMsg(mutiLangService.getLang("common.user.interfaceUser"));
 j.setSuccess(false);
 return j;
 }
 
 
 // 处理用户有多个组织机构的情况,以弹出框的形式让用户选择
 Map<String, Object> attrMap = new HashMap<String, Object>();
 j.setAttributes(attrMap);
 
 String orgId = req.getParameter("orgId");
 if (oConvertUtils.isEmpty(orgId)) {
 // 没有传组织机构参数,则获取当前用户的组织机构
 Long orgNum = systemService.getCountForJdbcParam("select count(1) from t_s_user_org where user_id = ?",u.getId());
 if (orgNum > 1) {
 attrMap.put("orgNum", orgNum);
 attrMap.put("user", u);
 } else {
 Map<String, Object> userOrgMap = systemService.findOneForJdbc("select org_id as orgId from t_s_user_org where user_id=?", u.getId());
 userService.saveLoginUserInfo(req, u, (String) userOrgMap.get("orgId"));
 }
 } else {
 attrMap.put("orgNum", 1);
 userService.saveLoginUserInfo(req, u, orgId);
 }
 } else {
 //用户锁定提醒
 j.setMsg(mutiLangService.getLang("common.lock.user"));
 j.setSuccess(false);
 }
 }else{
 j.setMsg("你没有权限登录内部报表");
 j.setSuccess(false);
 }
 
 
 return j;
 }
 第五步:修改web.xml增加如下代码
 <servlet>
 <servlet-name>ddlogin</servlet-name>
 <jsp-file>/ddindex.jsp</jsp-file>
 </servlet>
 <servlet-mapping>
 <servlet-name>ddlogin</servlet-name>
 <url-pattern>/ddlogin</url-pattern>
 </servlet-mapping>
 
 
 | 
 |