资讯动态

钉钉微应用免登进入H5系统:Java后端四次API调用全解析

发布时间:2026/10/9 1:53:11 来源:尧图企业网站定制
简介一份基于Java实现钉钉微应用免登进入H5系统首页的PDF教程适合需要对接钉钉开放平台、实现企业内部应用免登跳转的Java开发人员。资料从钉钉微应用创建、参数配置agentId、appKey、appSecret、corpId讲起再到前端ddNoLogin页面通过requestAuthCode获取免登授权码后端调用钉钉API验证并返回用户信息完整串联了前后端协作的关键环节。其中还涉及access_token的定时刷新与Redis缓存策略以及免登成功后的消息通知扩展能帮助读者规避常见权限和接口调用问题。文中包含可运行的前端示例片段和后端任务类代码对理解免登流程中的请求时序与配置要点尤其直观。无论是企业内部应用还是第三方应用均可参考其中的参数设置与代码结构。资源为单个PDF文件总大小129KB内容紧凑适合在开发前快速通读或作为参考手册查阅。目前已有2113人学习下载对于正在实现钉钉免登功能的开发者具有一定参考价值。1. 钉钉微应用免登进入H5系统首页一次点击背后的四次API调用用户打开钉钉App点一下工作台里的微应用图标不经过登录页直接落到公司H5系统的首页——这个体验看起来只是“少输了一次账号密码”但真正实现起来背后要串起前端JS-API、后端OpenAPI、Redis缓存和自有业务系统的用户体系。本文要拆的这套Java实现核心链路就四步前端用钉钉JS-API拿免登授权码code后端用appKey和appSecret换access_token再用code换userid、用userid换用户手机号最后拿手机号跟H5系统数据库比对存在就放行、不存在就提示“您无权限”。这套逻辑适合企业内部H5系统接入钉钉工作台、想省掉账号密码登录的开发者也适合刚接触钉钉开放平台、对“免登”概念还停留在文档层面的新手。代码量不大但坑不少尤其是token时效、IP白名单和消息频率限制这三处。2. 免登前的准备四个固定参数与一个不能省的白名单这一章讲的都是开发前的硬性条件缺一个都跑不通。第一次接钉钉开放平台时最容易被“创建一个微应用”这六个字带过去结果创建完了发现参数对不上、接口调不通再回头看文档才发现入口就选错了。2.1 企业内部开发与第三方企业应用选错入口后面全白搭钉钉开放平台的创建入口分两类企业内部开发和第三方企业应用。本场景是“公司内部员工用钉钉点微应用进公司H5系统”属于企业内部开发。第三方企业应用是开发者以第三方身份、把应用提供给钉钉上其他组织使用权限模型完全不同免登时需要套suiteTicket和第三方应用凭证链路比内部应用长得多。我见过同事把应用建到第三方入口结果拿着agentId怎么调都提示“无权限”排查到凌晨才发现是入口选错。所以第一件事就是确认登录 open-dev.dingtalk.com选择“企业内部开发”创建H5微应用不要选第三方。这个区别在钉钉文档里写得很清楚但实际操作时入口按钮挨得近很容易点错。2.2 四个固定参数agentId、appKey、appSecret、corpId微应用创建完成后会生成三个参数agentId、appKey、appSecret。还有一个corpId在钉钉开发者平台首页能看到。这四个值是后续所有API调用的凭证建议第一时间放进配置文件不要硬编码在代码里。参数获取位置用途corpId钉钉开发者平台首页前端requestAuthCode时传入标识企业agentId微应用详情页发送工作通知消息时标识应用appKey微应用详情页获取access_token的凭证之一appSecret微应用详情页获取access_token的凭证之二注意保密我的习惯是放到application.yml里用Value注入避免参数散落在代码各处。另外appSecret别提交到Git仓库公司内部项目也建议用配置中心或环境变量兜底。2.3 公网IP白名单与接口权限curl ifconfig.me 之后别忘了发布钉钉的OpenAPI需要校验调用方的公网IP微应用配置页面里要填公司服务器的公网IP。查看IP的常用命令是curl ifconfig.me执行后返回的字符串就是当前出口公网IP把它填进微应用的服务器IP白名单里。这个IP不是固定的也常见——公司网络出口IP经常变每次变了之后免登接口就会报“不允许访问”。我后面会单独写这个坑的排查方法。接口权限也需要开通在“权限管理-接口权限”里找到企业通讯录相关的接口把获取用户userid、获取用户详情、工作通知消息发送这几项都申请开通。最后一步是“发布应用”很多人开发完没发布自己调试时能过同事点进去却报错就是因为应用还是“未发布”状态。发布后微应用才会出现在钉钉工作台里别人也才访问得到。3. 前端拿免登码ddNoLogin.html 与 requestAuthCode 的最小实现免登的第一步必须由前端完成在钉钉容器里调用JS-API拿到一次性授权码code。这个code有效期很短而且只能用一次所以拿到后要立刻传给后端。下面是最小可用的页面实现直接可以放到H5工程的静态目录里。3.1 钉钉JS-API的引入与dd.ready时机钉钉JS-API的脚本地址是http://g.alicdn.com/dingding/open-develop/1.9.0/dingtalk.js页面里还需要jQuery方便做AJAX回传。注意这个JS-API必须在钉钉App内置浏览器里才能正常工作普通浏览器打开页面dd.ready根本不会触发这是正常现象不是代码问题。!DOCTYPE html html head title微应用登陆/title meta charsetutf-8 meta nameviewport contentwidthdevice-width,initial-scale1 user-scalable0 / script srchttps://cdn.bootcss.com/jquery/3.3.1/jquery.min.js/script script typetext/javascript srchttp://g.alicdn.com/dingding/open-develop/1.9.0/dingtalk.js/script /head body div idddNoLogin/div script typetext/javascript dd.ready(function() { dd.runtime.permission.requestAuthCode({ corpId : corpId, onSuccess : function(result) { var code result.code; getUserInfo(code); }, onFail : function(err) { alert(出错了, err); } }); }); function getUserInfo(code) { $.ajax({ type : GET, url : /ddUser/noLogin?code code, async : false, dataType : json, contentType : application/json;charsetutf-8, success : (function(res) { if(res.code 0000){ window.location.href /#/xxxxx; }else{ $(#ddNoLogin).html(res.msg); } }), }); } /script /body /html注意corpId需要替换成你自己企业的corpId。async: false是为了让页面在拿到后端返回结果后再决定跳转还是展示错误信息虽然同步AJAX在部分浏览器里有兼容性警告但在钉钉容器里实测是能用的。dd.ready的触发时机是钉钉容器注入JS-API完成后所以页面一加载就会执行不需要额外等待。3.2 requestAuthCode 拿 code 与 AJAX 回传dd.runtime.permission.requestAuthCode这个方法是整个免登的起点它做的事情是向钉钉客户端申请一个授权码code。这个code就是后端换取用户身份的唯一凭证注意几个关键点code是一次性的用完后失效前端不能重复回传同一个codecode有有效期通常只有几分钟拿到后要尽快传给后端corpId错误会直接进入onFail回调错误信息是“无效的corpId”后端接口接收到code之后会返回一个JSONres.code 0000表示免登成功这里建议和后端约定一个统一响应码不要直接拿钉钉的errcode来用。后端返回的res.msg在有权限时可以作为登录成功的提示信息无权限时就是“您无权限”之类的话术。3.3 不需要鉴权的方法免登码接口与dd.config的边界钉钉的JSAPI分两类需要鉴权和不需要鉴权。requestAuthCode属于不需要鉴权的方法所以这里不用写dd.config。这一点在钉钉的JSAPI总览里有标识我最初没注意按网上教程先写了dd.config结果在配置签名时反复报错最后发现这个方法压根不需要。如果后面要用到钉钉的定位、扫一扫、选人接口那就必须走dd.config鉴权需要后端提供签名链路就复杂了。所以本场景只做免登的话前端保持最小实现即可别提前引入鉴权逻辑。4. Java后端串起免登主链路token定时刷新、userid换取与权限判断前端把code交到后端后后端要依次完成拿token、换userid、换用户信息、比对权限几个动作。这一章是核心我会把三个关键类和每个请求的参数说明写清楚。4.1 access_token两小时有效期与定时刷新任务钉钉的access_token是调用OpenAPI的通行证有效期2小时有效期内重复获取会返回相同结果并自动续期。官方文档的建议是缓存起来不要每次都请求。我的实现是用Spring的定时任务每隔1小时50分钟主动刷新一次把token存到Redis里。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; // https://oapi.dingtalk.com/gettoken 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; Scheduled(fixedRate cacheTime) Async public void getDdTokenTask() { if (true.equals(taskRun)) { String accessTokenUrl tokenUrl ?appkey appKey appsecret appSecret; String accessToken JsonUtil.getJsonNode(HttpUtil.doGet(accessTokenUrl)).get(access_token).asText(); jedisClient.set(tokenKey, accessToken); } } }几个关键点fixedRate是固定间隔触发从上次调度开始时间算起这里设成1小时50分钟比token的2小时有效期提前10分钟刷新留足余量。Async避免定时任务阻塞Spring的主线程因为HttpUtil.doGet是同步网络请求最坏情况下要等超时才返回。taskRun开关配置成true才执行目的是在测试环境不想刷token时直接关掉。Redis的key我用的是配置文件里的tokenKey不建议把token存在本地内存里因为多实例部署时会出现某个实例没有token的情况。钉钉官方提供了Java SDK的写法用DefaultDingTalkClient发起请求但前提是公司私服里有对应依赖。我一开始也想用SDK发现私服没有之后就直接HTTP直连了代码反而更少。后面避坑章节会详细说这个。4.2 code换userid、userid换手机号两次GET请求的异常处理拿到code后后端要用access_tokencode换取userid再用access_tokenuserid换取用户详细信息。典型实现如下RestController RequestMapping(/ddUser) public class DdLoginController { Autowired private JedisClient jedisClient; Value(${dtalk.userUrl}) private String userUrl; // https://oapi.dingtalk.com/user/getuserinfo Value(${dtalk.userDetailUrl}) private String userDetailUrl; // https://oapi.dingtalk.com/user/get Value(${dtalk.redisTokenKey}) private String tokenKey; Value(${dtalk.agentId}) private Integer agentId; GetMapping(/noLogin) public WebResponse noLogin(RequestParam(code) String code, HttpServletResponse response) { // 1. 从Redis取access_token String accessToken jedisClient.get(tokenKey); if (accessToken null) { return WebResponse.resFail(access_token为空请检查定时任务是否执行); } // 2. code换userid String userIdUrl userUrl ?access_token accessToken code code; JsonNode user JsonUtil.getJsonNode(HttpUtil.doGet(userIdUrl)); if (user.get(errcode).asInt() ! 0) { return WebResponse.resFail(user.get(errmsg).asText()); } String userId user.get(userid).asText(); // 3. userid换用户详情手机号 String userInfoUrl userDetailUrl ?access_token accessToken userid userId; JsonNode userInfo JsonUtil.getJsonNode(HttpUtil.doGet(userInfoUrl)); String mobile userInfo.get(mobile).asText(); String name userInfo.get(name).asText(); // 4. 按手机号查自有系统的用户表 SysUser sysUser sysUserMapper.findByMobile(mobile); if (sysUser null) { return WebResponse.resFail(您无权限访问, null); } // 5. 免登成功发送钉钉工作通知 sendMessage(accessToken, userId, name); return WebResponse.resSuccess(免登成功, loginUserInfo); } }这里要解释两个细节。第一user.get(errcode).asInt() ! 0的判断不能省因为钉钉接口在出错时也会返回200只靠HTTP状态码判断会漏掉业务异常。第二用手机号作为比对字段是因为钉钉返回的用户详情里手机号是真实的而企业内部的H5系统通常也维护了员工手机号两者天然可以关联。如果你们系统的员工ID和钉钉userid有对应关系直接用userId查更省事但大多数老系统的用户表没有钉钉userid字段手机号是最通用的关联键。4.3 用户比对与免登成功后的工作通知消息用户比对通过后除了返回登录成功之外还可以给用户发一条钉钉工作通知告知“某某在什么时间登录了系统”。这个功能的接口是topapi/message/corpconversation/asyncsend_v2POST请求参数说明如下private void sendMessage(String token, String userId, String userName) { String messageUrl https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2?access_token token; MapString, 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); }agent_id必须是Long类型传Integer会报类型错误这是我自己踩过的坑。userid_list是字符串多个用户时用英文逗号分隔。to_all_user设为false表示只发给指定用户。消息内容里拼接当前时间不只是为了可读性更重要的是避开钉钉的一个限制给同一个用户发送内容完全相同的消息一天只能发一次内容不同则可以发500次。加上时间戳后每次消息内容都不同就能绕过这个限制。5. 免登开发避坑五个踩过的坑与对应解法这一章写的是我在实现这个功能时真实遇到过的五个问题每一条都按“现象 → 原因 → 解决”的结构记录希望能帮你少走弯路。5.1 定时任务死活不跑现象Redis里一直取不到token后端调用免登接口时直接报“access_token为空”。原因Scheduled注解没有被Spring扫描到。我最初把DdTokenTask类放在了一个没有被ComponentScan覆盖的包路径下注解等于没写。另外EnableScheduling必须要在配置类上显式声明光加Component不够。解决确认启动类或配置类上有EnableScheduling确认DdTokenTask所在的包能被扫描到然后在taskRun配置为true的前提下单独启动一次任务看控制台有没有打印“获取钉钉token的定时任务开始了”这行日志。5.2 公网IP不固定导致接口报“不允许访问”现象开发环境调通了换到测试服务器后code换userid时返回的errcode非0errmsg提示不允许访问。原因钉钉微应用里配置的公网IP白名单是旧的测试服务器的出口IP不在白名单里。这个项目里配置的是公司路由器出口IP但公司有两根宽带线路每次路由切换IP就变了。解决先用curl ifconfig.me拿到当前出口IP去微应用配置里替换白名单。如果公司网络经常变建议联系网络管理员固定出口IP或者用一台有固定IP的云服务器做反向代理转发。5.3 免登成功却收不到工作通知消息现象登录流程一切正常但用户收不到钉钉消息。原因给同一个用户发送内容完全相同的消息一天只能发一次。测试时反复用同一个模板内容发送第一次成功后面的都被钉钉拦截了但接口本身返回的errcode是0所以代码里完全感知不到。解决消息内容里拼接当前时间戳。我现在的习惯是每次发送前生成yyyy-MM-dd HH:mm:ss时间字符串放进内容里保证每次消息内容都不一样。另外注意agent_id类型传成Integer会造成消息发送失败。5.4 code一次性使用与前端重复回传现象用户在微应用页面里连续点击或者页面回退后再前进后端就开始报 “code已经被使用” 之类的错误。原因钉钉的免登授权码code是一次性的用一次就失效。前端AJAX如果超时或失败后重试拿的还是同一个旧code。解决前端拿到code后立刻回传不做重试逻辑如果后端失败引导用户关掉页面重新打开让dd.ready重新执行一次拿到新code。后端也要对errcode做兜底返回友好提示而不是直接把钉钉的错误信息抛给用户。5.5 钉钉SDK依赖拉不下来改用HTTP直连现象参考钉钉官方文档写代码时发现DefaultDingTalkClient、OapiGettokenRequest这些类找不到公司私服里也没有钉钉的SDK依赖。原因钉钉的SDK包需要单独引入依赖很多公司的私有Maven仓库没有收录这个包私服拉不到就报编译错误。解决直接用HTTP直连拼URL请求。获取token的接口是https://oapi.dingtalk.com/gettoken?appkeykeyappsecretsecret用HttpUtil.doGet拿返回的JSON再解析就行。注意设置合理的连接超时时间我一般设20秒钉钉接口偶尔会慢。6. 免登链路的验证技巧从钉钉容器到后端日志的完整闭环功能写完不是终点验证链路通不通才是重点。免登场景特殊在普通浏览器里无法完整模拟必须在钉钉容器里点。所以我的验证顺序是固定的每次上线前都强制走一遍能省掉大量返工时间。第一步先做后端单点验证。用Postman直接调noLogin接口传一个假的code确认接口能正常返回“code无效”之类的错误信息这一步验证的是后端链路本身有没有问题。第二步在钉钉开发者后台启用“调试模式”或者直接把应用发布到测试环境然后在钉钉App里打开微应用观察浏览器控制台dd.ready有没有触发、requestAuthCode的onSuccess有没有拿到code、AJAX回传后后端返回的JSON结构对不对。第三步查看后端日志里打印的“钉钉用户的手机号”这行确认手机号是否正确从钉钉用户详情里取出来了。第四步验证消息通知登录成功后看用户能不能收到钉钉工作通知收不到就先看是不是“相同内容一天一次”的限制去消息内容里看时间戳有没有变。还有一个细节技巧本地联调时不需要每次都在钉钉里点可以在浏览器里模拟。钉钉开放平台提供了免登码的模拟接口允许你在非钉钉容器里生成测试code这就能摆脱“必须在手机钉钉里调试”的约束。具体做法是按文档找到“调试免登授权码”入口生成一个临时code填到接口测试工具里配合你的Redis里已有的token整个过程在PC上就能跑完。验证时最该盯的日志关键字是这三处NoLogin接口收到的code、钉钉返回的errcode、以及最后一条免登成功。我见过很多次“前端跳转了但后端没收到请求”的情况基本上都是AJAX URL写错或者H5工程没把页面部署到微应用对应的域名下这类问题看Network面板比看后端日志更直观。最后说一个我这边的使用教训从那以后我每次上线免登功能都强制自己先在钉钉App里完整走一遍流程再故意把一个用户的手机号从系统里删掉验证“无权限”分支是否正常展示。这套免登机制最怕的不是技术难点而是权限判断和消息通知这类边界行为在真实环境里悄悄失效。测试环境的钉钉和线上环境的钉钉参数不同所以上线前最后一遍验证必须用线上配置跑别图省事用测试环境的agentId去发工作通知否则线上用户收到的消息可能来自一个未发布的应用。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑