Browse Source

🎉 升级blade-admin,增加账号密码登录,增强钉钉监控支持

test
smallchill 4 years ago
parent
commit
c1f7f9a99e
  1. 17
      blade-ops/blade-admin/DINGDING_README.md
  2. 21
      blade-ops/blade-admin/README.md
  3. 45
      blade-ops/blade-admin/pom.xml
  4. 17
      blade-ops/blade-admin/src/main/java/org/springblade/admin/config/AdminConfiguration.java
  5. 52
      blade-ops/blade-admin/src/main/java/org/springblade/admin/config/DingTalkConfiguration.java
  6. 65
      blade-ops/blade-admin/src/main/java/org/springblade/admin/config/SecurityConfiguration.java
  7. 105
      blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkNotifier.java
  8. 110
      blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkService.java
  9. 67
      blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/MonitorProperties.java
  10. 123
      blade-ops/blade-admin/src/main/java/org/springblade/admin/notifier/CustomNotifier.java
  11. 26
      blade-ops/blade-admin/src/main/resources/bootstrap.yml

17
blade-ops/blade-admin/DINGDING_README.md

@ -1,17 +0,0 @@
## SDK下载
#### Java SDK 下载
下载SDK: https://open-doc.dingtalk.com/microapp/faquestions/vzbp02
## 配置项
#### bootstrap.yml
```
spring:
boot:
admin:
notify:
dingtalk:
webhook-token: ${Your DingDing Robot Token}
```

21
blade-ops/blade-admin/README.md

@ -0,0 +1,21 @@
## SDK下载
#### Java SDK 下载
下载SDK: https://open-doc.dingtalk.com/microapp/faquestions/vzbp02
## 配置项
#### bootstrap.yml
```
# 监控的相关配置
monitor:
ding-talk:
enabled: false
# 用于自定义域名,默认会自动填充为 http://ip:port
link: http://localhost:${server.port}
# 钉钉配置的令牌
access-token: xxx
# 如果采用密钥形式,需要添加,否则需要去掉该参数
secret: xxx
```

45
blade-ops/blade-admin/pom.xml

@ -19,21 +19,62 @@
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-common</artifactId>
<exclusions>
<exclusion>
<groupId>org.springblade</groupId>
<artifactId>blade-core-launch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springblade</groupId>
<artifactId>blade-core-cloud</artifactId>
<artifactId>blade-core-launch</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--Admin-Server-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<!--Security-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<!--Taobao-Sdk-->
<dependency>
<groupId>com.taobao</groupId>
<artifactId>taobao-sdk</artifactId>
<version>20200415</version>
<version>20201116</version>
</dependency>
</dependencies>

17
blade-ops/blade-admin/src/main/java/org/springblade/admin/config/AdminConfiguration.java

@ -16,26 +16,17 @@
*/
package org.springblade.admin.config;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import org.springblade.admin.notifier.CustomNotifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springblade.admin.dingtalk.MonitorProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* AdminConfiguration
* 启动器
*
* @author Chill
*/
@Configuration
@EnableConfigurationProperties(MonitorProperties.class)
public class AdminConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.boot.admin.notify.dingtalk.enabled", havingValue = "true")
public CustomNotifier customNotifier(InstanceRepository repository) {
return new CustomNotifier(repository);
}
}

52
blade-ops/blade-admin/src/main/java/org/springblade/admin/config/DingTalkConfiguration.java

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.springblade.admin.config;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import org.springblade.admin.dingtalk.DingTalkNotifier;
import org.springblade.admin.dingtalk.DingTalkService;
import org.springblade.admin.dingtalk.MonitorProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.reactive.function.client.WebClient;
/**
* 钉钉自动配置
*
* @author L.cm
*/
@Configuration
@ConditionalOnProperty(value = "monitor.ding-talk.enabled", havingValue = "true")
public class DingTalkConfiguration {
@Bean
public DingTalkService dingTalkService(MonitorProperties properties,
WebClient.Builder builder) {
return new DingTalkService(properties, builder.build());
}
@Bean
public DingTalkNotifier dingTalkNotifier(MonitorProperties properties,
DingTalkService dingTalkService,
InstanceRepository repository,
Environment environment) {
return new DingTalkNotifier(dingTalkService, properties, environment, repository);
}
}

65
blade-ops/blade-admin/src/main/java/org/springblade/admin/config/SecurityConfiguration.java

@ -0,0 +1,65 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.springblade.admin.config;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import java.net.URI;
/**
* 监控安全配置
*
* @author L.cm
*/
@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfiguration {
private final String contextPath;
public SecurityConfiguration(AdminServerProperties adminServerProperties) {
this.contextPath = adminServerProperties.getContextPath();
}
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// @formatter:off
RedirectServerAuthenticationSuccessHandler successHandler = new RedirectServerAuthenticationSuccessHandler();
successHandler.setLocation(URI.create(contextPath + "/"));
return http.headers().frameOptions().disable().and()
.authorizeExchange()
.pathMatchers(
contextPath + "/assets/**"
, contextPath + "/login"
, contextPath + "/actuator/**"
).permitAll()
.anyExchange().authenticated().and()
.formLogin().loginPage(contextPath + "/login")
.authenticationSuccessHandler(successHandler).and()
.logout().logoutUrl(contextPath + "/logout").and()
.httpBasic().disable()
.csrf().disable()
.build();
// @formatter:on
}
}

105
blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkNotifier.java

@ -0,0 +1,105 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.springblade.admin.dingtalk;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.domain.values.Registration;
import de.codecentric.boot.admin.server.domain.values.StatusInfo;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.lang.NonNull;
import reactor.core.publisher.Mono;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
/**
* 服务上下线告警
*
* <p>
* 注意AbstractStatusChangeNotifier 这个事件有毛病
* </p>
*
* @author L.cm
*/
@Slf4j
public class DingTalkNotifier extends AbstractEventNotifier {
private final DingTalkService dingTalkService;
private final MonitorProperties properties;
private final Environment environment;
public static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public DingTalkNotifier(DingTalkService dingTalkService, MonitorProperties properties,
Environment environment, InstanceRepository repository) {
super(repository);
this.dingTalkService = dingTalkService;
this.properties = properties;
this.environment = environment;
}
@NonNull
@Override
protected Mono<Void> doNotify(@NonNull InstanceEvent event, @NonNull Instance instance) {
if (event instanceof InstanceStatusChangedEvent) {
// 构造请求结构
return createAndPushMsg(event, instance);
}
return Mono.empty();
}
private Mono<Void> createAndPushMsg(InstanceEvent event, Instance instance) {
Registration registration = instance.getRegistration();
// 服务名
String appName = registration.getName();
// 服务地址
String serviceUrl = registration.getServiceUrl();
StatusInfo status = instance.getStatusInfo();
// 时间
LocalDateTime localDateTime = LocalDateTime.ofInstant(event.getTimestamp(), ZoneId.systemDefault());
MonitorProperties.DingTalk dingTalk = properties.getDingTalk();
String title = dingTalk.getService().getTitle();
String message = "## **" + title + "**\n" +
"#### **【服务】** " + appName + "\n" +
"#### **【环境】** " + environment.getActiveProfiles()[0] + "\n" +
"#### **【地址】** " + serviceUrl + "\n" +
"#### **【状态】** " + statusCn(status) + "\n" +
"#### **【时间】** " + DATETIME_FORMATTER.format(localDateTime) + "\n" +
"#### **【详情】** " + dingTalk.getLink() + "\n";
return dingTalkService.pushMsg(title, message);
}
private String statusCn(StatusInfo status) {
if (status.isUp()) {
return "应用上线(IS UP)";
} else if (status.isDown()) {
return "应用宕机(IS DOWN)";
} else if (status.isOffline()) {
return "应用掉线(IS OFFLINE)";
} else if (status.isUnknown()) {
return "未知状态(UNKNOWN)";
} else {
return "异常状态";
}
}
}

110
blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/DingTalkService.java

@ -0,0 +1,110 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.springblade.admin.dingtalk;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.util.Base64Utils;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriUtils;
import reactor.core.publisher.Mono;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
/**
* 钉钉 服务
*
* @author L.cm
*/
@Slf4j
@RequiredArgsConstructor
public class DingTalkService {
private static final String DING_TALK_ROBOT_URL = "https://oapi.dingtalk.com/robot/send?access_token=";
private final MonitorProperties properties;
private final WebClient webClient;
/**
* 发送消息
*
* @param title title
* @param text 消息
*/
public Mono<Void> pushMsg(String title, String text) {
log.info("钉钉消息:[创建消息体]title:{}, text:{}", title, text);
HashMap<String, String> params = new HashMap<>(2);
params.put("title", title);
params.put("text", text);
Map<String, Object> body = new HashMap<>(2);
body.put("msgtype", "markdown");
body.put("markdown", params);
log.info("创建消息体 json:{}", body);
MonitorProperties.DingTalk dingTalk = properties.getDingTalk();
String accessToken = dingTalk.getAccessToken();
if (StringUtils.isEmpty(accessToken)) {
log.error("DingTalk alert config accessToken ${monitor.ding-talk.access-token} is blank.");
return Mono.empty();
}
String urlString = DING_TALK_ROBOT_URL + dingTalk.getAccessToken();
// 有私钥要签名
String secret = dingTalk.getSecret();
if (StringUtils.hasText(secret)) {
long timestamp = System.currentTimeMillis();
urlString += String.format("&timestamp=%s&sign=%s", timestamp, getSign(secret, timestamp));
}
return webClient.post()
.uri(URI.create(urlString))
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(body))
.retrieve()
.bodyToMono(String.class)
.doOnSuccess((result) -> log.info("钉钉消息:[消息返回]result:{}", result))
.then();
}
private static String getSign(String secret, long timestamp) {
String stringToSign = timestamp + "\n" + secret;
byte[] hmacSha256Bytes = digestHmac(stringToSign, secret);
return UriUtils.encode(Base64Utils.encodeToString(hmacSha256Bytes), StandardCharsets.UTF_8);
}
public static byte[] digestHmac(String data, String key) {
SecretKey secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
try {
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e.getMessage());
}
}
}

67
blade-ops/blade-admin/src/main/java/org/springblade/admin/dingtalk/MonitorProperties.java

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2028, DreamLu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the dreamlu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: DreamLu 卢春梦 (596392912@qq.com)
*/
package org.springblade.admin.dingtalk;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
/**
* 监控配置
*
* @author L.cm
*/
@Getter
@Setter
@RefreshScope
@ConfigurationProperties("monitor")
public class MonitorProperties {
private DingTalk dingTalk = new DingTalk();
@Getter
@Setter
public static class DingTalk {
/**
* 启用钉钉告警默认为 true
*/
private boolean enabled = false;
/**
* 钉钉机器人 token
*/
private String accessToken;
/**
* 签名如果有 secret 则进行签名兼容老接口
*/
private String secret;
/**
* 地址配置
*/
private String link;
private Service service = new Service();
}
@Getter
@Setter
public static class Service {
/**
* 服务 状态 title
*/
private String title = "服务状态通知";
}
}

123
blade-ops/blade-admin/src/main/java/org/springblade/admin/notifier/CustomNotifier.java

@ -1,123 +0,0 @@
/*
* Copyright (c) 2015-2025, Jeckxu All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* Neither the name of the jeckxu.net developer nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
* Author: Jeckxu (chinajeckxu@163.com)
*/
package org.springblade.admin.notifier;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.taobao.api.ApiException;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import reactor.core.publisher.Mono;
/**
* DingTalk Notice
* Can you find these packages ? , Plase Read DINGDING_README.md
* {@code com.dingtalk.api.DefaultDingTalkClient}
* {@code com.dingtalk.api.DingTalkClient}
* {@code com.dingtalk.api.request.OapiRobotSendRequest}
* {@code com.taobao.api.ApiException}
*
* @author jeckxu
*/
@Slf4j
public class CustomNotifier extends AbstractEventNotifier {
/**
* massage template
*/
private static final String TEMPLATE = "服务名:%s(%s) n状态:%s(%s) n服务ip:%s";
@Value("${spring.boot.admin.notify.dingtalk.webhook-token}")
private String dingTalkToken;
public CustomNotifier(InstanceRepository repository) {
super(repository);
}
@Override
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
return Mono.fromRunnable(() -> {
if (event instanceof InstanceStatusChangedEvent) {
log.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(),
((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());
String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
String messageText = null;
switch (status) {
// 健康检查没通过
case "DOWN":
log.info("发送 健康检查没通过 的通知!");
messageText = String.format(TEMPLATE, instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "健康检查没通过", instance.getRegistration().getServiceUrl());
this.sendMessage(messageText);
break;
// 服务离线
case "OFFLINE":
log.info("发送 服务离线 的通知!");
messageText = String.format(TEMPLATE, instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "服务离线", instance.getRegistration().getServiceUrl());
this.sendMessage(messageText);
break;
//服务上线
case "UP":
log.info("发送 服务上线 的通知!");
messageText = String.format(TEMPLATE, instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "服务上线", instance.getRegistration().getServiceUrl());
this.sendMessage(messageText);
break;
// 服务未知异常
case "UNKNOWN":
log.info("发送 服务未知异常 的通知!");
messageText = String.format(TEMPLATE, instance.getRegistration().getName(), event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(), "服务未知异常", instance.getRegistration().getServiceUrl());
this.sendMessage(messageText);
break;
default:
break;
}
} else {
log.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
event.getType());
}
});
}
/**
* massage send
*
* @param messageText
*/
private void sendMessage(String messageText) {
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token=" + dingTalkToken);
OapiRobotSendRequest request = new OapiRobotSendRequest();
request.setMsgtype("text");
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
text.setContent(messageText);
request.setText(text);
try {
client.execute(request);
} catch (ApiException e) {
log.info("[ERROR] sendMessage", e);
}
}
}

26
blade-ops/blade-admin/src/main/resources/bootstrap.yml

@ -17,7 +17,25 @@ spring:
ignored-services:
- consul
- serverAddr
notify:
dingtalk:
enabled: false
webhook-token: Your DingDing Robot Token
ui:
title: BladeX Monitor
external-views:
- label: 架构官网
url: https://bladex.vip/
order: 1
iframe: true
security:
user:
name: blade
password: blade
# 监控的相关配置
monitor:
ding-talk:
enabled: false
# 用于自定义域名,默认会自动填充为 http://ip:port
link: http://localhost:${server.port}
# 钉钉配置的令牌
access-token: xxx
# 如果采用密钥形式,需要添加,否则需要去掉该参数
secret:

Loading…
Cancel
Save