---
order: 9
---
# 钉钉登录

SuccBI支持与钉钉单点登录，可以直接使用钉钉账号进行身份验证，无需繁琐的登录步骤。

\[\[toc]]

:::tip 重要说明

- 需要确保部署SuccBI的服务器可以访问 `https://api.dingtalk.com`。

- 成为钉钉开发者，拥有开发企业自建应用权限的帐号。

- 根据[钉钉官方文档](https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information)完成实现登录第三方网站的配置。

:::

## 钉钉配置{#dingtalk-settings}

### 创建并配置应用{#settings-manage}

在开发者后台创建一个H5微应用，并完成通讯录权限和用户个人手机号权限的配置，用于获取用户个人信息。

在**开发管理**页面，根据以下内容配置开发信息。

![开发管理](./images/sso-dingtalk-manage.png)

1. 开发模式： 设置为`开发应用`。
2. 服务器出口IP： 设置为SuccBI系统的公网域名。[详细操作参见文档。](https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information#title-n1p-r2l-g3c)
3. 应用首页地址： 示例为`https://{domain}/{contextPath}/api/auth/doSSOAuthRedirect?ssoid={ssoid}&redirect_uri={redirect_uri}`
4. PC端首页地址： 示例为`https://{domain}/{contextPath}/api/auth/doSSOAuthRedirect?ssoid={ssoid}&redirect_uri={redirect_uri}`

:::tip 说明

- domain 和 contextPath，请替换为环境地址。
- ssoid 为SuccBI设置的[登录方案ID](#syssettings-base)，请根据实际情况替换。
- redirect\_uri，可以设置为`/`，表示登录成功后跳转到BI设置的首页，也可以按需替换为需要钉钉展示的地址，如`/Test/app/test.app` 表示跳转到SuccBI 中命名为 Test项目的 test.app中进行展示。redirect\_uri需要注意[URL转义](https://tool.chinaz.com/tools/urlencode.aspx)。

:::

### 添加接口权限{#settings-permission}

进入**权限管理**页面，根据以下配置添加接口调用权限。[详细操作参见文档。](https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information#title-qpi-0qv-anm)

![添加接口权限](./images/sso-dingtalk-permission.png)

### 设置第三方网站的回调域名{#settings-login}

![登录与分享](./images/sso-dingtalk-login.png)

示例： `https://{domain}/{contextPath}/api/auth/redirect?ssoid={ssoid}&redirect_url={redirect_url}`，参数设置见上文描述。

### 版本管理与发布{#settings-publish}

![版本管理与发布](./images/sso-dingtalk-publish.png)

以上配置完毕后，最后需要完成版本的发布。按需设置权限范围。

## 系统设置{#syssettings}

需要在**系统设置**>**安全**>**单点登录**中添加一个脚本定制的单点登录方案。
![系统设置](./images/syssettings-sso-dingtalk.png)

### 基本设置{#syssettings-base}

单点登录方案[通用设置](../../sys-settings/security/sso.md#common-settings)

![基本设置](./images/syssettings-sso-dingtalk-base.png)

1. 启用： `勾选`，这里勾选才会启用此单点方式。
2. 登录方案ID：ID用于唯一标识登录方案，英文字母或数字组成，如`dingding`，下文中的脚本示例用了此处的`dingding`，若是改为其他名称，脚本需要做对应的修改。

### 用户匹配规则设置{#syssettings-match}

![用户匹配规则设置](./images/syssettings-sso-dingtalk-match.png)

1. 启用： `勾选`，这里勾选才会允许作为内部用户登录系统。
2. 匹配用户依据1： 根据指定的用户信息在用户表中查找对应的用户，找到后将使用指定的用户登录系统。这里设置为`移动电话`（对应物理字段为`PHONE`）。
3. 匹配用户依据2： 如果根据“匹配用户依据1”无法匹配到用户，那么还可以根据第二个用户信息进行匹配。

### 添加单点登录脚本{#syssettings-script}

脚本位置放在**系统数据**项目，位于 `/sysdata/settings/hooks/security.action.ts`中，详细设置参考[钩子脚本开发](../../dev/script/hooks/hooks-action-ts.md)。

![单点登录脚本](./images/syssettings-sso-dingtalk-script.png)

::: details 点击展开查看脚本示例代码>>

```typescript

import utils from "svr-api/utils";
import http from 'svr-api/http';

let appkey = 'dingdnpb1t8cor2fdwqc';// 请按需替换为创建应用的appkey
let appsecret = 'f9UWa7FzRa63FE3GoO6Ku11RB60Uu13FZkyciP6qwHwc7UpsItz5sI_JtwtsG6PS';//请替换为创建应用的秘钥
let dingdingSSOId = "dingding";// 替换为创建的单点登录方案ID

/**
 * 用于构建跳转钉钉授权展示界面的URL，为通过钉钉工作台中打开应用时调用
 */
function onSSOGetAuthRedirectURL(request: HttpServletRequest, response: HttpServletResponse, redirect_uri: string, ssoArgs: SSOArgs): boolean | string {
    let session = request.getSession();
    let state = utils.uuid().substring(0, 5)
    session.setAttribute("state", state);

    let ssoid = request.getParameter('ssoid');
    let url;
    if (ssoid === dingdingSSOId) {
        url = getDingtalkLoginUrl(state, redirect_uri);
    }
    if (url == null) {
        return false;
    }

    // print(url);
    return url;
}

function getDingtalkLoginUrl(state: string, redirect_url: string) {
    // print(appkey);
    return `https://login.dingtalk.com/oauth2/auth?redirect_uri=${encodeURIComponent(redirect_url)}&response_type=code&scope=openid&prompt=consent&client_id=${appkey}&state=${state}`;
}

/**
 * 用于用户在钉钉授权页面点击授权后，跳转到BI时调用（这个地址由 getDingtalkLoginUrl 函数返回URL中的redirect_uri参数决定）
 */
function onSSOCheckTicket(request: HttpServletRequest, response: HttpServletResponse, ssoArgs: SSOArgs): UserInfo | void {
    let ssoid = request.getParameter('ssoid');
    if (ssoid === dingdingSSOId) {
        return doDingTalkAuth(request, response, ssoArgs);
    }
}

function doDingTalkAuth(request: HttpServletRequest, response: HttpServletResponse, ssoArgs: SSOArgs): UserInfo | void {
    let code = request.getParameter('authCode');
    // print("code is " + code);
    if (!code) {
        response.sendRedirect(request.getContextPath() + '/login');
        return;
    }

    let json = JSON.parse(http.request({
        url: "https://api.dingtalk.com/v1.0/oauth2/userAccessToken",
        method: "POST",
        data: {
            clientId: appkey,
            clientSecret: appsecret,
            code,
            grantType: "authorization_code"
        },
        headers: {
            "Content-Type": "application/json"
        }
    }).responseText);
    // print(json);

    let access_token = json.accessToken;

    json = JSON.parse(http.request({
        url: "https://api.dingtalk.com/v1.0/contact/users/me",
        method: "GET",
        headers: {
            "Content-Type": "application/json",
            "x-acs-dingtalk-access-token": access_token
        }
    }).responseText)
    // print(json);


    /* 返回一组用户信息，用于匹配在系统中存储的用户 */
    return <{
        /** id， 同 userId */
        userId: string;
        /** 用户名称， 同 userName */
        USER_NAME?: string;
        /** 部门ID */
        DEPT_ID?: string;
        /** 电话 */
        PHONE?: string;
        /** 邮箱 */
        EMAIL?: string;
        /** 获取头像地址 */
        avatar?: string;
        /** 扩展字段数据, 为用户表字段的名称*/
        [propName: string]: any;
    }>{
            userId: json.unionId,//只传第一个userId都可以,
            USER_NAME: json.nick,
            PHONE: json.mobile,
            EMAIL: json.email,
            avatar: json.avatarUrl === '' ? undefined : json.avatarUrl
        };
}


```

:::

## 访问SuccBI页面地址{#settings-access}

完成以上配置后，可以免密访问SuccBI页面地址。登录后，进入工作台页面，点击Demo应用正常跳转到SuccBI页面。

|钉钉工作台页面|授权后登录页面|登录成功页面|
| --- | --- | --- |
|<div style="padding-top: 10px"><img width="250" src="./images/sso-dingtalk-workbench.png"/></div>|<div style="padding-top: 10px"><img width="250" src="./images/sso-dingtalk-auth.png"/></div>|<div style="padding-top: 10px"><img width="250" src="./images/sso-dingtalk-success.png"/></div>|
