- 修改应用服务的application.yml
authority:
nacos:
server-addr: localhost:8848 # nacos地址
dataId: orderservice-authority-rules
groupId: SENTINEL_GROUP
rule-type: authority # 还可以是:degrade、authority、param-flow
- 修改com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfig.java
@Bean
public Converter<List<AuthorityRuleEntity>, String> authorityRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<AuthorityRuleEntity>> authorityRuleEntityDecoder() {
return s -> JSON.parseArray(s, AuthorityRuleEntity.class);
}
- 修改com.alibaba.csp.sentinel.dashboard.rule.nacos.NacosConfigUtil
添加代码
/**
* 授权
*/
public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
- 新增com.alibaba.csp.sentinel.dashboard.rule.nacos.AuthorityRuleNacosProvider.java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
/**
* Sentinel 授权规则 持久化 Push 模式 Nacos
* @Author jianghx
* @Date 2021/12/27 16:14
* @Version 1.0
**/
@Component("authorityRuleNacosProvider")
public class AuthorityRuleNacosProvider implements DynamicRuleProvider<List<AuthorityRuleEntity>>
{
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<AuthorityRuleEntity>> converter;
@Override
public List<AuthorityRuleEntity> getRules(String appName) throws Exception {
String rules = configService.getConfig(appName + NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
- 新增com.alibaba.csp.sentinel.dashboard.rule.nacos.AuthorityRuleNacosPublisher.java
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Sentinel 授权规则 持久化 Push 模式 Nacos
* @Author jianghx
* @Date 2021/12/27 16:14
* @Version 1.0
**/
@Component("authorityRuleNacosPublisher")
public class AuthorityRuleNacosPublisher implements DynamicRulePublisher<List<AuthorityRuleEntity>>
{
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<AuthorityRuleEntity>, String> converter;
@Override
public CompletableFuture<Void> publish(String app, List<AuthorityRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return null;
}
configService.publishConfig(app + NacosConfigUtil.AUTHORITY_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
return null;
}
}
- 新增com.alibaba.csp.sentinel.dashboard.controller.v2.AuthorityRuleControllerV2.java
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.controller.v2;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Sentinel 授权规则 持久化 Push 模式 Nacos
* @Author jianghx
* @Date 2021/12/24 11:47
* @Version 1.0
**/
@RestController
@RequestMapping(value = "/v2/authority")
public class AuthorityRuleControllerV2 {
private final Logger logger = LoggerFactory.getLogger(AuthorityRuleControllerV2.class);
@Autowired
private RuleRepository<AuthorityRuleEntity, Long> repository;
@Autowired
@Qualifier("authorityRuleNacosProvider")
private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;
@Autowired
@Qualifier("authorityRuleNacosPublisher")
private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip cannot be null or empty");
}
if (port == null || port <= 0) {
return Result.ofFail(-1, "Invalid parameter: port");
}
try {
List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);
if (rules != null && !rules.isEmpty()) {
for (AuthorityRuleEntity entity : rules) {
entity.setApp(app);
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying authority rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}
private <R> Result<R> checkEntityInternal(AuthorityRuleEntity entity) {
if (entity == null) {
return Result.ofFail(-1, "bad rule body");
}
if (StringUtil.isBlank(entity.getApp())) {
return Result.ofFail(-1, "app can't be null or empty");
}
if (StringUtil.isBlank(entity.getIp())) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (entity.getPort() == null || entity.getPort() <= 0) {
return Result.ofFail(-1, "port can't be null");
}
if (entity.getRule() == null) {
return Result.ofFail(-1, "rule can't be null");
}
if (StringUtil.isBlank(entity.getResource())) {
return Result.ofFail(-1, "resource name cannot be null or empty");
}
if (StringUtil.isBlank(entity.getLimitApp())) {
return Result.ofFail(-1, "limitApp should be valid");
}
if (entity.getStrategy() != RuleConstant.AUTHORITY_WHITE
&& entity.getStrategy() != RuleConstant.AUTHORITY_BLACK) {
return Result.ofFail(-1, "Unknown strategy (must be blacklist or whitelist)");
}
return null;
}
@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<AuthorityRuleEntity> apiAddAuthorityRule(@RequestBody AuthorityRuleEntity entity) {
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
entity.setId(null);
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
publishRules(entity.getApp());
} catch (Throwable throwable) {
logger.error("Failed to add authority rule", throwable);
return Result.ofThrowable(-1, throwable);
}
// if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
// logger.info("Publish authority rules failed after rule add");
// }
return Result.ofSuccess(entity);
}
@PutMapping("/rule/{id}")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<AuthorityRuleEntity> apiUpdateParamFlowRule(@PathVariable("id") Long id,
@RequestBody AuthorityRuleEntity entity) {
if (id == null || id <= 0) {
return Result.ofFail(-1, "Invalid id");
}
Result<AuthorityRuleEntity> checkResult = checkEntityInternal(entity);
if (checkResult != null) {
return checkResult;
}
entity.setId(id);
Date date = new Date();
entity.setGmtCreate(null);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
publishRules(entity.getApp());
if (entity == null) {
return Result.ofFail(-1, "Failed to save authority rule");
}
} catch (Throwable throwable) {
logger.error("Failed to save authority rule", throwable);
return Result.ofThrowable(-1, throwable);
}
// if (!publishRules(entity.getApp(), entity.getIp(), entity.getPort())) {
// logger.info("Publish authority rules failed after rule update");
// }
return Result.ofSuccess(entity);
}
@DeleteMapping("/rule/{id}")
@AuthAction(PrivilegeType.DELETE_RULE)
public Result<Long> apiDeleteRule(@PathVariable("id") Long id) {
if (id == null) {
return Result.ofFail(-1, "id cannot be null");
}
AuthorityRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}
try {
repository.delete(id);
publishRules(oldEntity.getApp());
} catch (Exception e) {
return Result.ofFail(-1, e.getMessage());
}
// if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
// logger.error("Publish authority rules failed after rule delete");
// }
return Result.ofSuccess(id);
}
private CompletableFuture<Void> publishRules(String app) throws Exception {
List<AuthorityRuleEntity> rules = repository.findAllByApp(app);
return rulePublisher.publish(app, rules);
}
}
- 修改resources\app\scripts\directives\sidebar\sidebar.html
<li ui-sref-active="active" ng-if="entry.appType==0">
<a ui-sref="dashboard.authority2({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 授权规则-Nacos</a>
</li>
- 修改resources\app\scripts\app.js
.state('dashboard.authority2', {
templateUrl: 'app/views/authority_v2.html',
url: '/v2/authority/:app',
controller: 'AuthorityRuleController2',
resolve: {
loadMyFiles: ['$ocLazyLoad', function ($ocLazyLoad) {
return $ocLazyLoad.load({
name: 'sentinelDashboardApp',
files: [
'app/scripts/controllers/authority_v2.js',
]
});
}]
}
})
- 修改resources\dist\js\app.js
.state("dashboard.authority2", {
templateUrl: "app/views/authority_v2.html",
url: "/v2/authority/:app",
controller: "AuthorityRuleController2",
resolve: {
loadMyFiles: ["$ocLazyLoad", function (e) {
return e.load({name: "sentinelDashboardApp", files: ["app/scripts/controllers/authority_v2.js"]})
}]
}
})
- 新增resources\app\views\authority_v2.html
将authority.html赋值一个名字改为authority_v2.html
- 新增resources\app\scripts\controllers\authority_v2.js
复制resources\app\scripts\controllers\authority.js改名resources\app\scripts\controllers\authority_v2.js并修改相关内容
- 新增resources\app\scripts\services\authority_service_v2.js
复制一个resources\app\scripts\services\authority_service_v2.js 并改名resources\app\scripts\services\authority_service_v2.js修改相关内容:
将AuthorityRuleService改为AuthorityRuleService2
并将url添加前缀
/v2/
/**
* Authority rule service.
*/
angular.module('sentinelDashboardApp').service('AuthorityRuleService2', ['$http', function ($http) {
this.queryMachineRules = function(app, ip, port) {
var param = {
app: app,
ip: ip,
port: port
};
return $http({
url: '/v2/authority/rules',
params: param,
method: 'GET'
});
};
this.addNewRule = function(rule) {
return $http({
url: '/v2/authority/rule',
data: rule,
method: 'POST'
});
};
this.saveRule = function (entity) {
return $http({
url: '/v2/authority/rule/' + entity.id,
data: entity,
method: 'PUT'
});
};
this.deleteRule = function (entity) {
return $http({
url: '/v2/authority/rule/' + entity.id,
method: 'DELETE'
});
};
this.checkRuleValid = function checkRuleValid(rule) {
if (rule.resource === undefined || rule.resource === '') {
alert('资源名称不能为空');
return false;
}
if (rule.limitApp === undefined || rule.limitApp === '') {
alert('流控针对应用不能为空');
return false;
}
if (rule.strategy === undefined) {
alert('必须选择黑白名单模式');
return false;
}
return true;
};
}]);
- 修改resources\dist\js\app.js
angular.module("sentinelDashboardApp").service("AuthorityRuleService2", ["$http", function (a) {
this.queryMachineRules = function (e, t, r) {
return a({url: "/v2/authority/rules", params: {app: e, ip: t, port: r}, method: "GET"})
}, this.addNewRule = function (e) {
return a({url: "/v2/authority/rule", data: e, method: "POST"})
}, this.saveRule = function (e) {
return a({url: "/v2/authority/rule/" + e.id, data: e, method: "PUT"})
}, this.deleteRule = function (e) {
return a({url: "/v2/authority/rule/" + e.id, method: "DELETE"})
}, this.checkRuleValid = function (e) {
return void 0 === e.resource || "" === e.resource ? (alert("资源名称不能为空"), !1) : void 0 === e.limitApp || "" === e.limitApp ? (alert("流控针对应用不能为空"), !1) : void 0 !== e.strategy || (alert("必须选择黑白名单模式"), !1)
}
}])
- 修改resources\gulpfile.js
'app/scripts/services/authority_service_v2.js',