• 钉钉小程序入门3—钉钉扫码登录PC端网站


    文章导航

    钉钉小程序生态1—区分企业内部应用、第三方企业应用、第三方个人应用
    钉钉小程序生态2—区分小程序和H5微应用
    钉钉小程序生态3—钉钉扫码登录PC端网站
    钉钉小程序生态4—钉钉小程序三方企业应用事件与回调
    钉钉小程序生态5—钉钉群机器人消息通知和钉钉工作通知
    钉钉小程序生态6—钉钉OA自定义审批流的创建和使用
    钉钉小程序生态7—企业机器人加互动卡片,改善用户体验的开始!

    第一部分、准备材料🌲

    1.公网环境

    老版钉钉扫码中必须要配置一个域名才可以调试,新版支持IP配置调了。我是手机打开热点,电脑连接热点进行调试的,比老版要方便了不少。

    查看本机IP地址方法:
    如果使用的Windows,执行命令:ipconfig

    如果使用的Mac,执行命令:ifconfig en0

    2.创建一个小程序(企业内应用即可)

    这个小程序可以是H5微应用也可以是小程序,但需要是企业内应用。
    将创建后的小程序appKey和appSecret复制后保存下来。

    在这里插入图片描述

    3.搭建SpringBoot项目

    目录结构如下:
    在这里插入图片描述

    注意:resources中的目录结构和文件名一定要和我的一致,不能多不能少,否则会出现启动报错或者找不到文件的问题。

    第二部分、环境配置⚙

    1.SpringBoot项目pom.xml

    这里我引入了VM模板用来放置扫码页与首页的html代码,引入DingTalk调用三方接口获取扫码用户的基本信息。

    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0modelVersion>
        <parent>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-parentartifactId>
            <version>2.7.1version>
            <relativePath/> 
        parent>
        <groupId>com.examplegroupId>
        <artifactId>SpringBoot-DDScanartifactId>
        <version>0.0.1-SNAPSHOTversion>
        <name>SpringBoot-DDScanname>
        <description>Demo project for Spring Bootdescription>
        <properties>
            <java.version>1.8java.version>
        properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starterartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
    
            
            <dependency>
                <groupId>com.aliyungroupId>
                <artifactId>alibaba-dingtalk-service-sdkartifactId>
                <version>1.1.1version>
                <exclusions>
                    <exclusion>
                        <artifactId>log4jartifactId>
                        <groupId>log4jgroupId>
                    exclusion>
                exclusions>
            dependency>
            <dependency>
                <groupId>com.aliyungroupId>
                <artifactId>dingtalkartifactId>
                <version>1.2.5version>
            dependency>
    
    
            
            <dependency>
                <groupId>com.alibaba.bootgroupId>
                <artifactId>velocity-spring-boot-starterartifactId>
                <version>1.0.4.RELEASEversion>
            dependency>
        dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                plugin>
            plugins>
        build>
    
    project>
    
    • 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

    2.SpringBoot项目application.properties

    spring.application.name=dd-scan
    server.port=8080
    
    ## 小程序AppID和AppSecret(此处填入自己复制的)
    qr.appId=xxxx
    qr.appSerret=xxxx
    
    # Velocity\u914D\u7F6E\uFF0C\u8BE6\u89C1 http://gitlab.alibaba-inc.com/middleware-container/pandora-boot/wikis/spring-boot-velocity
    spring.velocity.resource-loader-path=classpath:/velocity/templates
    spring.velocity.toolbox-config-location=/velocity/toolbox.xml
    spring.velocity.layout-url=/velocity/layout/default.vm
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.钉钉开放平台-登录与分享配置回调域名

    这里的回调地址就是扫码后跳转的地址。这里的地址与下面login.vm中的window.url 一定要一模一样,否则会扫码会弹出”回调域名配置错误“的提示。
    在这里插入图片描述

    4.钉钉开放平台-权限管理授权

    选择:个人手机号信息、成员信息读权限权限即可
    在这里插入图片描述

    第三部分、代码📚

    1.后端代码

    (1)SpringBootDdScanApplication.java

    package com.example.springbootddscan;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SpringBootDdScanApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootDdScanApplication.class, args);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    (2)MainController.java

    package com.example.springbootddscan.controller;
    
    import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
    import com.aliyun.dingtalkcontact_1_0.models.GetUserResponse;
    import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
    import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
    import com.aliyun.teaopenapi.models.Config;
    import com.aliyun.teautil.models.RuntimeOptions;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.servlet.ModelAndView;
    
    @Controller
    @RequestMapping("/login")
    public class MainController {
    
        @Value("${qr.appId}")
        private String qrAppId;
    
        @Value("${qr.appSerret}")
        private String qrAppSecret;
    
        @GetMapping("/toLoginPage")
        public ModelAndView toLoginPage() {
            return new ModelAndView("login");
        }
    
        @GetMapping("/scanLogin")
        public ModelAndView scanLogin(@RequestParam String authCode) throws Exception {
            //获取当前小程序的accesstoken
            GetUserTokenResponse userTokenResponse = getUserAccessToken(qrAppId, qrAppSecret, authCode, "authorization_code");
            //查询当前用户信息
            GetUserResponse me = getUserWithOptions(userTokenResponse.getBody().getAccessToken(), "me");
            //获取首页模板
            ModelAndView modelAndView = new ModelAndView("index");
            modelAndView.addObject("userid",me.getBody().getOpenId());
            modelAndView.addObject("userName",me.getBody().getNick());
            modelAndView.addObject("userPhone",me.getBody().getMobile());
            return modelAndView;
        }
    
        public GetUserResponse getUserWithOptions(String accessToken, String unionId) throws Exception {
            // 准备请求配置参数
            Config config = new Config();
            // 设置请求协议
            config.protocol = "https";
            // 设置请求区域
            config.regionId = "central";
            // 初始化账号Client
            com.aliyun.dingtalkcontact_1_0.Client client = new com.aliyun.dingtalkcontact_1_0.Client(config);
            GetUserHeaders getUserHeaders = new GetUserHeaders();
            getUserHeaders.xAcsDingtalkAccessToken = accessToken;
            return client.getUserWithOptions(unionId, getUserHeaders, new RuntimeOptions());
        }
    
        public GetUserTokenResponse getUserAccessToken(String suiteKey, String suiteSecret, String authCode, String grantType) throws Exception {
            // 准备请求配置参数
            Config config = new Config();
            // 设置请求协议
            config.protocol = "https";
            // 设置请求区域
            config.regionId = "central";
            // 初始化账号Client
            com.aliyun.dingtalkoauth2_1_0.Client client = new com.aliyun.dingtalkoauth2_1_0.Client(config);
            GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                    .setClientId(suiteKey)
                    .setClientSecret(suiteSecret)
                    .setCode(authCode)
                    .setGrantType(grantType);
            return client.getUserToken(getUserTokenRequest);
        }
    }
    
    • 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
    • 74
    • 75

    2.前端代码

    (1)login.vm

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
        <title>钉钉扫码登录title>
    head>
    <body>
    
    <script src="https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js">script>
    <script>
        <!-- 获取到的IP地址 -->
        window.url = 'http://xxx.xx.xxx.xx:8080/login/scanLogin'
        <!-- 小程序的appId -->
        window.appid = 'xxxx'
    script>
    
    <div id="self_defined_element" class="self-defined-classname">div>
    <style>
        /* STEP2:指定这个包裹容器元素的CSS样式,尤其注意宽高的设置 */
        .self-defined-classname {
            width: 300px;
            height: 300px;
        }
    style>
    <script>
        // STEP3:在需要的时候,调用 window.DTFrameLogin 方法构造登录二维码,并处理登录成功或失败的回调。
        window.DTFrameLogin(
                {
                    id: 'self_defined_element',
                    width: 300,
                    height: 300,
                },
                {
                    redirect_uri: encodeURIComponent(window.url),
                    client_id: window.appid,
                    scope: 'openid',
                    response_type: 'code',
                    state: 'test',
                    prompt: 'consent',
                },
                (loginResult) => {
                    console.log(loginResult)
                    const {redirectUrl, authCode, state} = loginResult;
                    // 这里可以直接进行重定向
                    window.location.href = redirectUrl;
                    // 也可以在不跳转页面的情况下,使用code进行授权
                    console.log(authCode);
                },
                (errorMsg) => {
                    // 这里一般需要展示登录失败的具体原因
                    alert(`Login Error: ${errorMsg}`);
                },
        );
    script>
    body>
    html>
    
    • 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

    (2)index.vm

    <h1>首页h1>
    <h2>userid: ${userid}h2>
    <h2>用户名: ${userName}h2>
    <h2>手机号码:${userPhone}h2>
    
    • 1
    • 2
    • 3
    • 4

    3.其他文件

    (1)toolbox.xml

    
    
    <tools>
        <data type="number" key="TOOLS_VERSION" value="2.0"/>
        <data type="boolean" key="GENERIC_TOOLS_AVAILABLE" value="true"/>
        <toolbox scope="application">
            <tool class="org.apache.velocity.tools.generic.AlternatorTool"/>
            <tool class="org.apache.velocity.tools.generic.ClassTool"/>
            <tool class="org.apache.velocity.tools.generic.ComparisonDateTool"/>
            <tool class="org.apache.velocity.tools.generic.ConversionTool"/>
            <tool class="org.apache.velocity.tools.generic.DisplayTool"/>
            <tool class="org.apache.velocity.tools.generic.EscapeTool"/>
            <tool class="org.apache.velocity.tools.generic.FieldTool"/>
            <tool class="org.apache.velocity.tools.generic.MathTool"/>
            <tool class="org.apache.velocity.tools.generic.NumberTool"/>
            <tool class="org.apache.velocity.tools.generic.ResourceTool"/>
            <tool class="org.apache.velocity.tools.generic.SortTool"/>
            <tool class="org.apache.velocity.tools.generic.XmlTool"/>
        toolbox>
        <toolbox scope="request">
            <tool class="org.apache.velocity.tools.generic.ContextTool"/>
            <tool class="org.apache.velocity.tools.generic.LinkTool"/>
            <tool class="org.apache.velocity.tools.generic.LoopTool"/>
            <tool class="org.apache.velocity.tools.generic.RenderTool"/>
            <tool class="org.apache.velocity.tools.view.CookieTool"/>
            <tool class="org.apache.velocity.tools.view.ImportTool"/>
            <tool class="org.apache.velocity.tools.view.IncludeTool"/>
            <tool class="org.apache.velocity.tools.view.PagerTool"/>
            <tool class="org.apache.velocity.tools.view.ParameterTool"/>
            <tool class="org.apache.velocity.tools.view.ViewContextTool"/>
            
            
            <tool class="org.apache.velocity.tools.generic.ResourceTool"/>
            
        toolbox>
    tools>
    
    • 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

    (2)default.vm

    $!{screen_content}
    
    • 1

    第四部分、演示🍓

    1.输入登录地址

    http://xxx.xxx.xxx.xxx:8080/login/toLoginPage

    这里记住一定要用IP,不能使用localhost或者127.0.0.1,否则即使二维码能出现,扫码完也不会有反应。
    在这里插入图片描述

    2.使用钉钉扫码

    扫完码后钉钉会弹出一个授权页,点击同意即可。

    在这里插入图片描述

    3.钉钉回调地址进入首页

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-llcMLSRh-1670244714400)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bbbfd463657841febc7bed7e481ce3b5~tplv-k3u1fbpfcp-watermark.image?)]

    第五部分、原理解释

    钉钉文档链接:https://open.dingtalk.com/document/orgapp-server/tutorial-obtaining-user-personal-information

  • 相关阅读:
    猿创征文|力扣15 - 三数之和【奇妙的双指针】
    振弦采集模块测量振弦传感器的流程步骤?
    【Spring面试】八、事务相关
    Cy3-PEG-COOH/carboxylic acid,Cy3-聚乙二醇-羧基,COOH/NH2/NHS/MAL/N3-PEG-Cy3
    Datax-异构数据源离线同步
    Scss
    公钥私钥传输,以及对CA证书的理解
    2.1.9.4 MySQL udf提权
    MQ 之 RoketMQ(下载、安装、快速启动、控制台、集群部署)
    主流X86-ARM-RISC-V-MIPS芯片架构分析
  • 原文地址:https://blog.csdn.net/weixin_33005117/article/details/128192486