使用 Spring Boot 快速构建企业微信 JS-SDK 权限签名后端服务

本篇文章将介绍如何使用 Spring Boot 快速构建一个用于支持企业微信 JS-SDK 权限校验的后端接口,并提供一个简单的 HTML 页面进行功能测试。适用于需要在企业微信网页端使用扫一扫、定位、录音等接口的场景。


一、项目目标

我们希望实现一个包含以下功能的服务:

  • 提供获取企业微信 access_token 的接口
  • 提供获取部门成员信息的接口(需要带 token)
  • 提供 JS-SDK 前端初始化所需签名参数的接口(wx.config() 配置)
  • 提供一个前端页面用于测试扫码、定位、数据表格展示等功能

二、开发环境与依赖

  • JDK 17
  • IDEA
  • Spring Boot 3.2.5
  • Maven 3.x

三、项目结构

DemoAPI
├── pom.xml                       		  // 项目依赖配置
├── src
│   └── main
│       ├── java
│       │   └── org.example
│       │       ├── Main.java             // 项目启动类
│       │       ├── WeComController.java  // 控制器:处理请求
│       │       └── WeComService.java     // 服务类:处理逻辑
│       └── resources
│           └── static
│               └── index.html            // 测试前端页面

说明: 本项目未配置 application.yml,Spring Boot 默认即可运行。


四、完整功能实现

第一步:修改 pom.xml,添加 Spring Boot 配置

pom.xml 中我们引入了:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

可能遇到的问题:

  • 依赖下载失败,可通过加速器优化下载速度。
  • 注意 Spring Boot 3.x 要使用 JDK 17+。

第二步:刷新依赖

你可以点击 IntelliJ 右侧 “Maven” 工具窗口的刷新按钮(🔄),或者右键 pom.xml → Add as Maven Project,IDE 会自动下载 Spring Boot 依赖。

第三步:修改你的 Main.java,变成 Spring Boot 启动类

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

问题回顾: 如果你忘记添加 @SpringBootApplication,将导致 ApplicationContext 启动失败,同时控制台可能提示找不到 Web 容器类(如 Tomcat)或无法创建 Controller Bean。解决办法:确保注解已加。

第四步:创建一个服务类 WeComService.java

提供 access_token 缓存获取、jsapi_ticket 缓存、JS-SDK 签名生成逻辑:

String raw = String.format(
    "jsapi_ticket=%s&noncestr=%s&timestamp=%d&url=%s",
    jsapiTicket, nonceStr, timestamp, url);

MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(raw.getBytes(StandardCharsets.UTF_8));

注意:

  • 签名计算必须严格按参数顺序和格式
  • access_tokenjsapi_ticket 建议缓存,避免频繁请求
  • 返回格式需包括 appIdtimestampnonceStrsignature

JS-SDK 参数生成

  • 参数组成:jsapi_ticketnonceStrtimestampurl
  • 算法:SHA-1(raw字符串) 生成签名
  • 返回结构:包含 appIdtimestampnonceStrsignature

第五步:控制器类 WeComController.java

提供如下接口:

接口地址 请求方法 功能描述
/wecom/token GET 获取 access_token
/wecom/department/users GET 获取指定部门的成员列表
/wecom/js-sdk-config GET 获取 JS-SDK 初始化配置信息

常见问题:

  • 若自动注入失败,请确认 @Service@RestController 注解是否添加
  • 如果依赖注入失败,控制台会提示 UnsatisfiedDependencyException

第六步:创建前端测试页面 index.html

功能:

  • 获取 Token 并展示
  • 获取部门成员并展示表格(含滚动条)
  • 初始化 JS SDK,支持扫码、定位等测试按钮
wx.config({
  appId: config.appId,
  timestamp: config.timestamp,
  nonceStr: config.nonceStr,
  signature: config.signature,
  jsApiList: ["scanQRCode", "getLocation"]
});

wx.ready(function() {
  alert("✅ 企业微信 JS SDK 初始化成功");
});

失败处理:

wx.error(function (err) {
    alert("❌ SDK 初始化失败: " + JSON.stringify(err));
});

页面结构清晰,所有逻辑通过 window.onload 初始化即可。

第七步:运行你的 Spring Boot 应用

在 IntelliJ 中右键 Main.java → Run ‘Main’,或点击绿色的 ▶ 按钮。

看到类似:

Tomcat started on port(s): 8080
Started Main in x.xxx seconds

说明服务已成功启动。

第八步:界面展示

http://localhost:8080/index.html

在这里插入图片描述

运行 & 测试(可选)

启动 Spring Boot 项目后,浏览器访问可访问下面的接口:

http://localhost:8080/wecom/token
http://localhost:8080/wecom/department/users?id=1


六、常见问题总结

问题 说明 解决办法
SDK 初始化失败 签名无效、时间戳不一致等 保证 URL 不带 #,参数顺序正确
Bean 注入失败 启动报错找不到 Controller Bean 检查是否缺少 @SpringBootApplication@Service 注解
依赖无法拉取 Maven 仓库连接慢 配置阿里云镜像源,提高稳定性
HTML 无法访问 资源路径未设置正确 放到 resources/static/ 下由 Spring Boot 自动映射

❌ 错误核心提示:

APPLICATION FAILED TO START

Web application could not be started as there was no
org.springframework.boot.web.servlet.server.ServletWebServerFactory bean defined in the context.

原因解释:Spring Boot 应用是一个 Web 项目,但 缺少内嵌 Servlet 容器(比如 Tomcat)依赖,也就是没有 ServletWebServerFactory,Spring Boot 启动 Web 服务失败。

最常见的原因:

  • pom.xml 中 缺失或拼错了 spring-boot-starter-web 依赖
  • Maven 没有下载成功依赖(网络或仓库问题)
  • 没有添加 @SpringBootApplication

七、后续可扩展方向

  • 接入企业微信身份认证(OAuth2)
  • 支持更多 JS API(如录音、语音识别、打开地图)
  • 使用 Redis 缓存 token,提升性能与健壮性
  • 前后端分离,使用 Vue、React 等框架

八、结语

通过本项目我们实现了从零搭建一个企业微信 JS-SDK 权限校验服务,具备了完整的后端支持和前端测试页面。如果想正常使用企业微信的扫描等功能需要在企业微信内部访问,那么就需要设置 IP 白名单、域名、网页授权及JS-SDK、企业微信授权登录和应用主页等。


九、推荐

Maven Central(Maven 中央仓库 Web 版)

这是最常用、几乎所有 Java 开发者都会用的网站 —— 一个图形化的 Maven 中央仓库检索平台:

👉 网站地址:
🌐 https://mvnrepository.com

使用 Spring Initializr 官网 创建项目(图形化窗口版)

这个网站会自动帮你生成一个可运行的 Spring Boot 项目,并打包成一个 zip 文件。解压 zip,然后用 IDEA 打开即可。

👉 地址:
🌐 https://start.spring.io


附录:完整文件(可自行补全代码)

pom.xml ✅

<?xml version="1.0" encoding="UTF-8"?>
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>DemoAPI</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <!-- Spring Boot 父项目 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/>
    </parent>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Spring Boot Web 模块(包含内嵌 Tomcat) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 开发工具(自动重启,非必须) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

index.html ✅

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>企业微信接口测试</title>
    <style>
        body {
            font-family: "微软雅黑", sans-serif;
            margin: 20px;
        }
        table {
            border-collapse: collapse;
            width: 100%;
            margin-top: 10px;
        }
        th, td {
            border: 1px solid #ccc;
            padding: 6px 12px;
            text-align: center;
        }
        th {
            background-color: #f5f5f5;
        }
        pre {
            background-color: #eee;
            padding: 10px;
        }
        .scroll-box {
            max-height: 160px;
            overflow-y: auto;
            border: 1px solid #ccc;
        }
        #scanModal {
            display: none; /* 初始隐藏 */
            position: fixed;
            z-index: 1000;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.6);

            /* 居中弹窗 */
            display: flex;
            justify-content: center;
            align-items: center;
        }
        #scanModal.hidden {
            display: none !important;
        }
        #scanBox {
            background: white;
            width: 320px;
            max-width: 90vw;
            padding: 16px;
            border-radius: 12px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
            text-align: center;
            position: relative;
            box-sizing: border-box;
        }
        #scanBox #reader {
            width: 100%;
            height: auto;
            aspect-ratio: 1;
            border: 1px solid #ccc;
            margin-bottom: 10px;
        }
        #scanBox button {
            background-color: #36eef4;
            color: white;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
        }
    </style>

    <!-- 引入企业微信 JS SDK -->
    <script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

    <!-- html5-qrcode 扫码库 -->
    <script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>

    <script>
        // 判断企业微信环境
        function isWeComBrowser() {
            return /wxwork/i.test(navigator.userAgent);
        }

        // 判断设备类型
        function isMobileDevice() {
            return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
        }

        // 初始化企业微信 JS SDK
        async function initWeComJsSdk() {
            const url = window.location.href.split('#')[0];
            const res = await fetch('/wecom/js-sdk-config?url=' + encodeURIComponent(url));
            const config = await res.json();

            wx.config({
                beta: true,
                debug: false,
                appId: config.appId,
                timestamp: config.timestamp,
                nonceStr: config.nonceStr,
                signature: config.signature,
                jsApiList: ["scanQRCode", "getLocation"]
            });

            wx.ready(function () {
                console.log("企业微信 JS SDK 就绪");
                alert("✅ 企业微信 JS SDK 初始化成功!");

                document.getElementById('scanBtn').onclick = function () {
                    wx.scanQRCode({
                        needResult: 1,
                        scanType: ["qrCode", "barCode"],
                        success: function (res) {
                            alert("扫码结果:" + res.resultStr);
                        }
                    });
                };

                document.getElementById('locBtn').onclick = function () {
                    wx.getLocation({
                        type: 'wgs84',
                        success: function (res) {
                            alert("当前位置:经度 " + res.longitude + ",纬度 " + res.latitude);
                        }
                    });
                };
            });

            wx.error(function (err) {
                console.error("JS SDK 初始化失败:", err);
                alert("❌ 企业微信 JS SDK 初始化失败!\n" + JSON.stringify(err));
            });
        }

        async function getToken() {
            const res = await fetch('/wecom/token');
            const token = await res.text();
            document.getElementById('token').innerText = token;
        }

        async function getUsers() {
            const deptId = document.getElementById('dept').value || '1';
            const res = await fetch(`/wecom/department/users?id=${deptId}`);
            const json = await res.json();

            document.getElementById('result').innerText = JSON.stringify(json, null, 2);

            if (json.userlist) {
                renderTable(json.userlist);
            } else {
                document.getElementById('userTableBody').innerHTML = "<tr><td colspan='6'>无成员数据</td></tr>";
            }
        }

        function renderTable(users) {
            const tbody = document.getElementById("userTableBody");
            tbody.innerHTML = "";
            users.forEach(user => {
                const row = document.createElement("tr");
                row.innerHTML = `
                    <td>${user.name}</td>
                    <td>${user.userid}</td>
                    <td>${(user.department || []).join(',')}</td>
                    <td>${user.isleader === 1 ? '是' : '否'}</td>
                    <td>${translateStatus(user.status)}</td>
                    <td>${user.telephone || ''}</td>
                `;
                tbody.appendChild(row);
            });
        }

        function translateStatus(status) {
            switch (status) {
                case 1: return "正常";
                case 2: return "已禁用";
                case 4: return "未激活";
                default: return "未知";
            }
        }

        // 全局变量:摄像头列表和当前索引等
        let html5QrCode = null;
        let cameras = [];
        let currentCameraIndex = 0;

        // 启动 HTML5 扫码窗口
        function startHtml5Scan() {
            const modal = document.getElementById("scanModal");
            modal.classList.remove("hidden"); // 显示弹窗

            try {
                html5QrCode = new Html5Qrcode("reader");
            } catch (err) {
                alert("❌ 初始化扫码对象失败:" + err);
                return;
            }

            Html5Qrcode.getCameras().then(deviceCameras => {
                if (!deviceCameras || deviceCameras.length === 0) {
                    alert("⚠️ 没有检测到摄像头!");
                    return;
                }

                cameras = deviceCameras;
                currentCameraIndex = 0;

                startCameraByIndex(currentCameraIndex);
            }).catch(err => {
                alert("⚠️ 获取摄像头失败:" + err);
            });
        }

        // 根据索引启动摄像头
        function startCameraByIndex(index) {
            const cameraId = cameras[index].id;

            html5QrCode.start(
                cameraId,
                { fps: 10, qrbox: 250 },
                (decodedText) => {
                    alert("✅ 扫码成功:" + decodedText);
                    closeScanModal();
                },
                (errMsg) => {
                    console.log("识别中...", errMsg);
                }
            ).catch(err => {
                alert("❌ 启动摄像头失败:" + err);
            });
        }

        // 切换摄像头
        function switchCamera() {
            if (!cameras || cameras.length <= 1) {
                alert("没有其他摄像头可切换!");
                return;
            }

            html5QrCode.stop().then(() => {
                html5QrCode.clear();

                currentCameraIndex = (currentCameraIndex + 1) % cameras.length;

                startCameraByIndex(currentCameraIndex);
            }).catch(err => {
                alert("切换失败:" + err);
            });
        }

        // 停止 HTML5 扫码
        function stopHtml5Scan() {
            if (html5QrCode) {
                html5QrCode.stop().then(() => {
                    html5QrCode.clear();
                    document.getElementById("reader").style.display = "none";
                }).catch(err => {
                    console.error("停止失败", err);
                });
            }
        }

        // 关闭 HTML5 扫码窗口
        function closeScanModal() {
            if (html5QrCode) {
                html5QrCode.stop().then(() => {
                    html5QrCode.clear();
                    html5QrCode = null;
                }).catch(err => {
                    console.error("停止失败", err);
                });
            }

            const modal = document.getElementById("scanModal");
            modal.classList.add("hidden");
            document.getElementById("reader").innerHTML = ""; // 清除内容,避免重复初始化
        }

        // 非企业微信环境绑定 html5 扫码逻辑
        function bindHtml5Fallback() {
            console.warn("非企业微信环境,使用 html5-qrcode");

            document.getElementById("scanBtn").onclick = startHtml5Scan;
            document.getElementById("locBtn").onclick = useBrowserLocation;
        }

        function useBrowserLocation() {
            if (!navigator.geolocation) {
                alert("❌ 当前浏览器不支持地理定位!");
                return;
            }

            navigator.geolocation.getCurrentPosition(
                function (position) {
                    const lat = position.coords.latitude.toFixed(6);
                    const lon = position.coords.longitude.toFixed(6);
                    alert("📍 当前定位成功:纬度 " + lat + ",经度 " + lon);
                },
                function (err) {
                    console.error("定位失败", err);
                    switch (err.code) {
                        case 1:
                            alert("❌ 用户拒绝授权定位");
                            break;
                        case 2:
                            alert("❌ 位置不可用");
                            break;
                        case 3:
                            alert("❌ 获取位置超时");
                            break;
                        default:
                            alert("❌ 定位失败:" + err.message);
                    }
                },
                {
                    enableHighAccuracy: true, // 请求高精度定位
                    timeout: 10000,
                    maximumAge: 0
                }
            );
        }

        window.onload = function () {
            if (isWeComBrowser() && isMobileDevice()) {
                initWeComJsSdk();
            } else {
                bindHtml5Fallback();
            }
        };
    </script>
</head>
<body>

<h1>企业微信接口测试</h1>

<!-- 获取 Token -->
<button onclick="getToken()">获取 Token</button>
<p>Token:<code id="token">(点击上面按钮)</code></p>

<!-- 获取部门成员 -->
<hr>
<label>部门 ID:</label>
<input type="text" id="dept" value="1">
<button onclick="getUsers()">获取部门成员</button>

<!-- 显示返回数据 -->
<h3>接口返回数据:</h3>
<pre id="result">(点击按钮查看 JSON)</pre>

<!-- 成员列表表格 -->
<h3>成员列表表格:</h3>
<div class="scroll-box">
    <table>
        <thead>
        <tr>
            <th>姓名</th>
            <th>用户ID</th>
            <th>部门</th>
            <th>是否领导</th>
            <th>状态</th>
            <th>座机</th>
        </tr>
        </thead>
        <tbody id="userTableBody"></tbody>
    </table>
</div>

<!-- 功能测试 -->
<h3>功能测试:</h3>
<button id="scanBtn">扫一扫</button>
<button id="locBtn">获取当前位置</button>

<!-- 扫码模态窗口(初始隐藏) -->
<div id="scanModal" class="hidden">
    <div id="scanBox">
        <h3>📷 请对准二维码</h3>
        <div id="reader"></div>
        <div style="margin-top: 8px;">
            <button onclick="switchCamera()">🔁</button>
            <button onclick="closeScanModal()"></button>
        </div>
    </div>
</div>

</body>
</html>

Main.java ✅

package org.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * ==================================================
 * This class ${NAME} is responsible for [功能描述].
 *
 * @author Darker
 * @version 1.0
 * ==================================================
 */

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

WeComService.java ✅

package org.example;

import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.http.ResponseEntity;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import java.time.Instant;

/**
 * ==================================================
 * This class WeComService is responsible for [功能描述].
 *
 * @author Darker
 * @version 1.0
 * ==================================================
 */

@Service
public class WeComService {
    private static final String CORP_ID = "你的企业微信ID";
    private static final String SECRET = "你的自建应用的Secret";
    private static final String TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";

    private String accessToken;
    private long expireTime = 0;

    // jsapi_ticket(缓存 2 小时)
    private String jsapiTicket;
    private long ticketExpire = 0;

    public String getAccessToken() {
        long now = Instant.now().getEpochSecond();
        if (accessToken != null && now < expireTime) {
            return accessToken;
        }

        // 请求新的 token
        RestTemplate restTemplate = new RestTemplate();
        UriComponentsBuilder builder = UriComponentsBuilder
                .fromHttpUrl(TOKEN_URL)
                .queryParam("corpid", CORP_ID)
                .queryParam("corpsecret", SECRET);

        ResponseEntity<WeComTokenResponse> response = restTemplate.getForEntity(
                builder.toUriString(), WeComTokenResponse.class);

        WeComTokenResponse body = response.getBody();
        if (body != null && body.getAccess_token() != null) {
            this.accessToken = body.getAccess_token();
            this.expireTime = now + body.getExpires_in() - 60; // 提前60秒过期
            return accessToken;
        }

        throw new RuntimeException("无法获取 access_token");
    }

    public Map<String, Object> getJsSdkConfig(String url) {
        String jsapiTicket = getJsApiTicket(); // 用下面方法实现
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        long timestamp = System.currentTimeMillis() / 1000;

        String raw = String.format(
                "jsapi_ticket=%s&noncestr=%s&timestamp=%d&url=%s",
                jsapiTicket, nonceStr, timestamp, url
        );

        String signature;
        try {
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(raw.getBytes(StandardCharsets.UTF_8));
            signature = bytesToHex(md.digest());
        } catch (Exception e) {
            throw new RuntimeException("签名失败", e);
        }

        Map<String, Object> result = new HashMap<>();
        result.put("appId", CORP_ID);
        result.put("timestamp", timestamp);
        result.put("nonceStr", nonceStr);
        result.put("signature", signature);
        return result;
    }

    private String bytesToHex(byte[] bytes) {
        Formatter formatter = new Formatter();
        for (byte b : bytes) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    public String getJsApiTicket() {
        long now = System.currentTimeMillis() / 1000;
        if (jsapiTicket != null && now < ticketExpire) {
            return jsapiTicket;
        }
        String token = getAccessToken();
        String url = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=" + token;
        RestTemplate restTemplate = new RestTemplate();
        Map<String, Object> res = restTemplate.getForObject(url, Map.class);
        if (res != null && res.get("ticket") != null) {
            jsapiTicket = (String) res.get("ticket");
            ticketExpire = now + ((Integer) res.get("expires_in")) - 60;
            return jsapiTicket;
        }
        throw new RuntimeException("获取 jsapi_ticket 失败");
    }

    // 内部类用于接收 JSON 响应
    public static class WeComTokenResponse {
        private String access_token;
        private int expires_in;

        public String getAccess_token() {
            return access_token;
        }

        public void setAccess_token(String access_token) {
            this.access_token = access_token;
        }

        public int getExpires_in() {
            return expires_in;
        }

        public void setExpires_in(int expires_in) {
            this.expires_in = expires_in;
        }
    }
}

WeComController.java ✅

package org.example;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.util.UriComponentsBuilder;

/**
 * ==================================================
 * This class WeComController is responsible for [功能描述].
 *
 * @author Darker
 * @version 1.0
 * ==================================================
 */
 
@RestController
@RequestMapping("/wecom")
public class WeComController {

    @Autowired
    private WeComService weComService;

    // GET 接口:/wecom/token
    @GetMapping("/token")
    public String getToken() {
        return weComService.getAccessToken();
    }

    @GetMapping("/department/users")
    public Object getDepartmentUsers(@RequestParam("id") String departmentId) {
        String token = weComService.getAccessToken();

        String url = "https://qyapi.weixin.qq.com/cgi-bin/user/list";
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
                .queryParam("access_token", token)
                .queryParam("department_id", departmentId);

        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<Object> response = restTemplate.getForEntity(
                builder.toUriString(), Object.class
        );

        return response.getBody();
    }

    // GET 接口:/wecom/js-sdk-config?url=xxx
    @GetMapping("/js-sdk-config")
    public Object getJsSdkConfig(@RequestParam("url") String url) {
        return weComService.getJsSdkConfig(url);
    }
}
Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐