| 
 | 
 
 
 楼主 |
发表于 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> 
 
 |   
 
 
 
 |