用java实现“钉钉微应用,免登进入某H5系统首页“功能”
一、前言
哈哈,这是我的第一篇博客。
先说一下这个小功能的具体场景:
用户登录钉钉app,点击微应用,获取当前用户的信息,与H5系统的数据库的用户信息对比,如果存在该用户,则点击后直接进入H5系统的首页,否则显示“您无权限”。
补充:又加了一个小需求,就是免登成功,会给该用户发条消息
我是参考钉钉开发文档实现的这个小功能,文档地址:https://ding-doc.dingtalk.com/doc#/serverapi2/clotub
二、准备工作
需要创建一个微应用:https://open-dev.dingtalk.com
1.是在企业内部开发中创建H5微应用,不是第三方企业应用中
企业内部开发:企业内部开发是指“开发企业内部应用”供企业内部的人员使用。企业可以选择由企业内部的开发者进行开发,或者由企业授权定制服务商进行开发。
第三方企业应用:第三方企业应用开发,是指开发者以钉钉、企业之外的第三方身份,基于钉钉的开放能力开发应用,并提供给钉钉上的其他组织使用。(哈哈,这个区别我是直接copy文档中的)
2.H5工程中,创建的一个前端页面ddNoLogin.html(哈哈,这个起名有点中文式英语,因为我没有找到合适的英文单词):
该页面是用来获取免登授权码,然后把该code传递给后台接口的。
3.填写公司服务器的公网IP
可以敲命令查看该ip:curl ifconfig.me
4.微应用创建好,会生成三个参数,
agentId、appKey、appSecret
外加一个corpId(钉钉开发者平台的首页中有显示),这四个参数值是固定的,后续开发需用
5.接口权限
高级权限-企业通讯录中的接口都需要给开通
6.最后发布应用
三、功能开发
哈哈,要开始敲代码了
1.获取免登授权码code
这里调用的是钉钉JS-API中的方法,本功能调用的方法是不需要鉴权的,如果需要鉴权,则还需先dd.config。
关于功能方法是否需要鉴权,可以去查看JSAPI总览
ddNoLogin.html:
<!DOCTYPE html> <html> <head> <title>微应用登陆</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1 user-scalable=0" /> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script> <script type="text/javascript" src="http://g.alicdn.com/dingding/open-develop/1.9.0/dingtalk.js"></script> </head> <body> <div id="ddNoLogin"></div> <script type="text/javascript"> dd.ready(function() { //1.获取免登授权码code dd.runtime.permission.requestAuthCode({ corpId : corpId , //企业id onSuccess : function(result) { var code = result.code; getUserInfo(code); //通过该code可以获取用户身份 }, onFail : function(err) { alert('出错了, ' + err); } }); }); function getUserInfo(code) { $.ajax({ type : "GET", url : "/xxx/noLogin?code=" + code, async : false, dataType : 'json', contentType : "application/json;charset=utf-8", success : (function(res) { if(res.code == "0000"){ window.location.href = '/#/xxxxx'; }else{ $('#ddNoLogin').html(res.msg); } }), }); } </script> </body> </html>
获取code的实现是需要用前端JS去写的
2.获取access_token
钉钉提供了开放的api后台接口,这里通过appkey和appsecret获取token,请求路径:https://oapi.dingtalk.com/gettoken?appkey=key&appsecret=secret
该token有效期是2个小时,有效期内重复获取,会返回相同结果,并自动续期,
故这里我的实现是:定时刷新token,每隔1小时50分钟去获取钉钉的token,并缓存到redis中(我把钉钉的相关配置都放入到application.yml中了)
DdTokenTask.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /** * 定时获取钉钉的token */ @Component @EnableScheduling public class DdTokenTask { @Autowired private JedisClient jedisClient; public static final long cacheTime = 1000 * 60 * 55 * 2 ; //1小时50分钟 @Value ( "${dtalk.tokenUrl}" ) private String tokenUrl; @Value ( "${dtalk.app.key}" ) private String appKey; @Value ( "${dtalk.app.secret}" ) private String appSecret; @Value ( "${dtalk.redisTokenKey}" ) private String tokenKey; @Value ( "${dtalk.taskRun}" ) private String taskRun; /** * 每隔1小时50分钟获取钉钉的access_token */ @Scheduled (fixedRate = cacheTime) @Async public void getDdTokenTask(){ if ( "true" .equals(taskRun)){ //刷新 System.out.println( "--->>>>>-------------获取钉钉token的定时任务开始了:" + DateUtil.formatDateToString( new Date(), "HH:mm:ss" )); String accessTokenUrl = tokenUrl + "?appkey=" + appKey + "&appsecret=" + appSecret; //访问获取access_token 有效期是2小时 String accessToken = JsonUtil.getJsonNode(HttpUtil.doGet(accessTokenUrl)).get( "access_token" ).asText(); //放入到redis中 jedisClient.set(tokenKey, accessToken); System.out.println( "--->>>>>-------------获取钉钉token的定时任务结束了,token:" +accessToken); } } /*private String getAccessToken(){ //需引入SDK,公司私服没有 DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken"); OapiGettokenRequest request = new OapiGettokenRequest(); request.setAppkey("appkey"); request.setAppsecret("appsecret"); request.setHttpMethod("GET"); OapiGettokenResponse response = client.execute(request); return ""; }*/ } |
(哈哈,其实文档中提供了钉钉SDK的java写法,不过公司私服没有该SDK,所以我就直接用htttp请求了)
3.获取用户userid
通过免登授权码和access_token获取用户的userid,请求路径:https://oapi.dingtalk.com/user/getuserinfo?access_token=access_token&code=code
4.获取用户详情
请求路径:https://oapi.dingtalk.com/user/get?access_token=ACCESS_TOKEN&userid=shaocui
会返回用户的姓名、手机号等所有信息
DdLoginController.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | @RestController @RequestMapping ( "/ddUser" ) @Api (value = "/ddUser" , description = "钉钉H5微应用登录" , tags = { "DdLoginController" }) public class DdLoginController { @Autowired private JedisClient jedisClient; @Value ( "${dtalk.userUrl}" ) private String userUrl; @Value ( "${dtalk.userDetailUrl}" ) private String userDetailUrl; @Value ( "${dtalk.redisTokenKey}" ) private String tokenKey; @Value ( "${dtalk.agentId}" ) private Integer agentId; @GetMapping ( "/noLogin" ) @ApiOperation ( "钉钉免登" ) @ApiImplicitParam (paramType = "query" , name = "code" , value = "免登授权码" , dataType = "String" ) public WebResponse noLogin( @RequestParam ( "code" ) String code, HttpServletResponse response){ //2.获取access_token String accessToken = jedisClient.get(tokenKey); //3.获取用户userid String userIdUrl =userUrl + "?access_token=" + accessToken + "&code=" + code; //访问获取userid JsonNode user = JsonUtil.getJsonNode(HttpUtil.doGet(userIdUrl)); if (user.get( "errcode" ).asInt() != 0 ){ //有些公司的公网ip不固定,导致微应用中设置的不对,这里就会报错 return WebResponse.resFail(user.get( "errmsg" ).asText()); } String userId = user.get( "userid" ).asText(); //4.获取用户详情 手机号 String userInfoUrl = userDetailUrl + "?access_token=" + accessToken + "&userid=" + userId; //访问获取mobile JsonNode userInfo = JsonUtil.getJsonNode(HttpUtil.doGet(userInfoUrl)); String mobile = userInfo.get( "mobile" ).asText(); System.out.println( "钉钉用户的手机号:" +mobile); //通过手机号获取该用户 SysUser sysUser = 。。。。。; if (sysUser == null ){ //不存在该用户 return WebResponse.resFail( "您无权限访问" , null ); } 。。。。。。 //钉钉发送免登成功消息给用户 sendMessage(accessToken, userId, userInfo.get( "name" ).asText()); return WebResponse.resSuccess( "免登成功" , loginUserInfo); } //钉钉发送消息给用户 private void sendMessage(String token, String userId, String userName){ String messageUrl = "https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=" +token; Map<String, Object> map = new HashMap<>(); map.put( "agent_id" , agentId.longValue()); map.put( "userid_list" , userId); map.put( "to_all_user" , false ); String content = "用户" +userName+ "在" + DateUtil.formatDateToString( new Date(), "yyyy-MM-dd HH:mm:ss" )+ "时成功登录xxH5端,并进入到xxx页面" ; String msg = "{\"msgtype\":\"text\",\"text\":{\"content\":" + "\"" +content+ "\"" + "}}" ; JSONObject jsonObj = JSONObject.parseObject(msg); map.put( "msg" ,jsonObj); HttpUtil.doPost(messageUrl, map, "UTF-8" , 20000 , null ); } } |
(哈哈,那个。。。。。处的代码是实现系统认证成功后的具体操作,故这里省略)
到此钉钉免登就实现了,然后免登成功后给用户发送消息
5.工作通知消息
POST请求,请求路径:https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token=ACCESS_TOKEN
请求体{agent_id、userid_list、dept_id_list、to_all_user、msg}
注意:
给同一个用户发送相同的内容,一天只能发一次;发送不同的内容,一天可以500次,
故这里我在发送的消息中添加了当前时间。
(哈哈,一些细节的东西我都写在代码的注释里了,最后发现这个小功能好像就一点点代码量)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本