Merge branch 'master' into registry-nacos

pull/1/head
tanghc 5 years ago
commit fb4108e980
  1. 5
      changelog.md
  2. 9
      sop-1.13.0.sql
  3. 2
      sop-admin/sop-admin-server/pom.xml
  4. 98
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/IPBlacklistApi.java
  5. 28
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ConfigIpBlackForm.java
  6. 19
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/ConfigIpBlacklistPageParam.java
  7. 30
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/ConfigIpBlacklistVO.java
  8. 4
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/bean/ZookeeperContext.java
  9. 39
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigIpBlacklist.java
  10. 12
      sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/mapper/ConfigIpBlacklistMapper.java
  11. 2
      sop-admin/sop-admin-server/src/main/resources/public/index.html
  12. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-34c76be7.f531fb07.css
  13. 0
      sop-admin/sop-admin-server/src/main/resources/public/static/css/chunk-9b31c83a.3b12267b.css
  14. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.095e8b9c.js
  15. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/app.4a507d5e.js
  16. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d2085ef.7c741493.js
  17. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d208c3a.93f165b2.js
  18. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-2d221c34.8f017357.js
  19. 2
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-34c76be7.98e1e7e5.js
  20. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-4a59cbe4.a6360c68.js
  21. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-6a68a33e.b6685cb9.js
  22. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-6f78c9fe.f1ed64fa.js
  23. 1
      sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.2758df30.js
  24. 14
      sop-admin/sop-admin-vue/src/router/index.js
  25. 8
      sop-admin/sop-admin-vue/src/views/isv/role.vue
  26. 184
      sop-admin/sop-admin-vue/src/views/service/ipBlacklist.vue
  27. 0
      sop-admin/sop-admin-vue/src/views/service/limit.vue
  28. 347
      sop-admin/sop-admin-vue/src/views/service/limit/index.vue
  29. 0
      sop-admin/sop-admin-vue/src/views/service/log.vue
  30. 0
      sop-admin/sop-admin-vue/src/views/service/route.vue
  31. 0
      sop-admin/sop-admin-vue/src/views/service/serviceList.vue
  32. 2
      sop-common/pom.xml
  33. 4
      sop-common/sop-gateway-common/pom.xml
  34. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
  35. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java
  36. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/AbstractConfiguration.java
  37. 38
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultIPBlacklistManager.java
  38. 16
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/IPBlacklistManager.java
  39. 4
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/ZookeeperContext.java
  40. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/message/ErrorEnum.java
  41. 10
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/ApiParam.java
  42. 3
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/param/BaseParamBuilder.java
  43. 14
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
  44. 2
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/filter/PreValidateFilter.java
  45. 5
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/zuul/param/ZuulParamBuilder.java
  46. 1
      sop-common/sop-gateway-common/src/main/resources/i18n/open/error_en.properties
  47. 1
      sop-common/sop-gateway-common/src/main/resources/i18n/open/error_zh_CN.properties
  48. 2
      sop-common/sop-registry-api/pom.xml
  49. 4
      sop-common/sop-service-common/pom.xml
  50. 2
      sop-example/sop-auth/pom.xml
  51. 2
      sop-example/sop-book/sop-book-web/pom.xml
  52. 2
      sop-example/sop-easyopen/pom.xml
  53. 2
      sop-example/sop-springmvc/pom.xml
  54. 2
      sop-example/sop-story/sop-story-web/pom.xml
  55. 2
      sop-gateway/pom.xml
  56. 64
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbIPBlacklistManager.java
  57. 1
      sop-gateway/src/main/java/com/gitee/sop/gateway/manager/ManagerInitializer.java
  58. 22
      sop-gateway/src/main/java/com/gitee/sop/gateway/mapper/IPBlacklistMapper.java
  59. 2
      sop-website/website-server/pom.xml
  60. 11
      sop.sql

@ -1,5 +1,10 @@
# changelog
## 1.13.0
- 新增IP黑名单
## 1.12.4
- 优化属性文件配置

@ -0,0 +1,9 @@
CREATE TABLE `config_ip_blacklist` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT 'ip',
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ip` (`ip`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='IP黑名单';

@ -30,7 +30,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-registry-api</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<!-- easyopen starter -->

@ -0,0 +1,98 @@
package com.gitee.sop.adminserver.api.service;
import com.alibaba.fastjson.JSON;
import com.gitee.easyopen.annotation.Api;
import com.gitee.easyopen.annotation.ApiService;
import com.gitee.easyopen.doc.annotation.ApiDoc;
import com.gitee.easyopen.doc.annotation.ApiDocMethod;
import com.gitee.easyopen.util.CopyUtil;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.query.Sort;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlackForm;
import com.gitee.sop.adminserver.api.service.param.ConfigIpBlacklistPageParam;
import com.gitee.sop.adminserver.api.service.result.ConfigIpBlacklistVO;
import com.gitee.sop.adminserver.bean.ChannelMsg;
import com.gitee.sop.adminserver.bean.ConfigLimitDto;
import com.gitee.sop.adminserver.bean.ZookeeperContext;
import com.gitee.sop.adminserver.common.BizException;
import com.gitee.sop.adminserver.entity.ConfigIpBlacklist;
import com.gitee.sop.adminserver.mapper.ConfigIpBlacklistMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author tanghc
*/
@ApiService
@ApiDoc("IP黑名单管理")
@Slf4j
public class IPBlacklistApi {
@Autowired
ConfigIpBlacklistMapper configIpBlacklistMapper;
@ApiDocMethod(description = "获取IP黑名单,分页")
@Api(name = "ip.blacklist.page")
PageEasyui<ConfigIpBlacklistVO> page(ConfigIpBlacklistPageParam form) {
Query query = Query.build(form);
query.orderby("id", Sort.DESC);
return MapperUtil.queryForEasyuiDatagrid(configIpBlacklistMapper, query, ConfigIpBlacklistVO.class);
}
@ApiDocMethod(description = "IP黑名单--新增")
@Api(name = "ip.blacklist.add")
void add(ConfigIpBlackForm form) {
ConfigIpBlacklist rec = configIpBlacklistMapper.getByColumn("ip", form.getIp());
if (rec != null) {
throw new BizException("IP已添加");
}
rec = new ConfigIpBlacklist();
CopyUtil.copyPropertiesIgnoreNull(form, rec);
configIpBlacklistMapper.saveIgnoreNull(rec);
try {
this.sendIpBlacklistMsg(rec, BlacklistMsgType.ADD);
} catch (Exception e) {
log.error("推送IP黑名单失败, rec:{}",rec, e);
throw new BizException("推送IP黑名单失败");
}
}
@ApiDocMethod(description = "IP黑名单--修改")
@Api(name = "ip.blacklist.update")
void update(ConfigIpBlackForm form) {
ConfigIpBlacklist rec = configIpBlacklistMapper.getById(form.getId());
CopyUtil.copyPropertiesIgnoreNull(form, rec);
configIpBlacklistMapper.updateIgnoreNull(rec);
}
@ApiDocMethod(description = "IP黑名单--删除")
@Api(name = "ip.blacklist.del")
void del(long id) {
ConfigIpBlacklist rec = configIpBlacklistMapper.getById(id);
if (rec == null) {
return;
}
configIpBlacklistMapper.deleteById(id);
try {
this.sendIpBlacklistMsg(rec, BlacklistMsgType.DELETE);
} catch (Exception e) {
log.error("推送IP黑名单失败, rec:{}",rec, e);
throw new BizException("推送IP黑名单失败");
}
}
public void sendIpBlacklistMsg(ConfigIpBlacklist configIpBlacklist, BlacklistMsgType blacklistMsgType) throws Exception {
String configData = JSON.toJSONString(configIpBlacklist);
ChannelMsg channelMsg = new ChannelMsg(blacklistMsgType.name().toLowerCase(), configData);
String jsonData = JSON.toJSONString(channelMsg);
String path = ZookeeperContext.getIpBlacklistChannelPath();
log.info("消息推送--IP黑名单设置({}), path:{}, data:{}",blacklistMsgType.name(), path, jsonData);
ZookeeperContext.createOrUpdateData(path, jsonData);
}
enum BlacklistMsgType {
ADD, DELETE
}
}

@ -0,0 +1,28 @@
package com.gitee.sop.adminserver.api.service.param;
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
/**
* 表名config_ip_black
* 备注IP黑名单
*
* @author tanghc
*/
@Data
public class ConfigIpBlackForm {
/** 数据库字段:id */
private Long id;
/** ip, 数据库字段:ip */
@NotBlank(message = "不能为空")
@Length(max = 30, message = "ip长度太长")
private String ip;
/** 备注, 数据库字段:remark */
@Length(max = 100, message = "备注长度太长")
private String remark;
}

@ -0,0 +1,19 @@
package com.gitee.sop.adminserver.api.service.param;
import com.gitee.easyopen.doc.annotation.ApiDocField;
import com.gitee.fastmybatis.core.query.Operator;
import com.gitee.fastmybatis.core.query.annotation.Condition;
import com.gitee.fastmybatis.core.query.param.PageParam;
import lombok.Getter;
import lombok.Setter;
/**
* @author tanghc
*/
@Getter
@Setter
public class ConfigIpBlacklistPageParam extends PageParam {
@ApiDocField(description = "IP")
@Condition(operator = Operator.like)
private String ip;
}

@ -0,0 +1,30 @@
package com.gitee.sop.adminserver.api.service.result;
import lombok.Data;
import java.util.Date;
/**
* 表名config_ip_black
* 备注IP黑名单
*
* @author tanghc
*/
@Data
public class ConfigIpBlacklistVO {
/** 数据库字段:id */
private Long id;
/** ip, 数据库字段:ip */
private String ip;
/** 备注, 数据库字段:remark */
private String remark;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -104,6 +104,10 @@ public class ZookeeperContext {
return SOP_MSG_CHANNEL_PATH + "/limit-conf";
}
public static String getIpBlacklistChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/ipblacklist-conf";
}
public static CuratorFramework getClient() {
return client;
}

@ -0,0 +1,39 @@
package com.gitee.sop.adminserver.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* 表名config_ip_blacklist
* 备注IP黑名单
*
* @author tanghc
*/
@Table(name = "config_ip_blacklist")
@Data
public class ConfigIpBlacklist {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
/** 数据库字段:id */
private Long id;
/** ip, 数据库字段:ip */
private String ip;
/** 备注, 数据库字段:remark */
private String remark;
/** 数据库字段:gmt_create */
private Date gmtCreate;
/** 数据库字段:gmt_modified */
private Date gmtModified;
}

@ -0,0 +1,12 @@
package com.gitee.sop.adminserver.mapper;
import com.gitee.fastmybatis.core.mapper.CrudMapper;
import com.gitee.sop.adminserver.entity.ConfigIpBlacklist;
/**
* @author tanghc
*/
public interface ConfigIpBlacklistMapper extends CrudMapper<ConfigIpBlacklist, Long> {
}

@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function t(t){for(var r,c,o=t[0],i=t[1],f=t[2],l=0,d=[];l<o.length;l++)c=o[l],u[c]&&d.push(u[c][0]),u[c]=0;for(r in i)Object.prototype.hasOwnProperty.call(i,r)&&(e[r]=i[r]);h&&h(t);while(d.length)d.shift()();return a.push.apply(a,f||[]),n()}function n(){for(var e,t=0;t<a.length;t++){for(var n=a[t],r=!0,c=1;c<n.length;c++){var o=n[c];0!==u[o]&&(r=!1)}r&&(a.splice(t--,1),e=i(i.s=n[0]))}return e}var r={},c={runtime:0},u={runtime:0},a=[];function o(e){return i.p+"static/js/"+({}[e]||e)+"."+{"chunk-009073d4":"28312808","chunk-238a81e9":"5955f13d","chunk-25908fca":"ca176fa6","chunk-29e7142c":"994a3ac0","chunk-2d2085ef":"a63a74dc","chunk-2d22c2e3":"4a098244","chunk-37401378":"4e39ec9b","chunk-4a59cbe4":"a6360c68","chunk-6a68a33e":"b6685cb9","chunk-73b2dcec":"14f248eb"}[e]+".js"}function i(t){if(r[t])return r[t].exports;var n=r[t]={i:t,l:!1,exports:{}};return e[t].call(n.exports,n,n.exports,i),n.l=!0,n.exports}i.e=function(e){var t=[],n={"chunk-009073d4":1,"chunk-238a81e9":1,"chunk-25908fca":1,"chunk-29e7142c":1,"chunk-37401378":1,"chunk-6a68a33e":1,"chunk-73b2dcec":1};c[e]?t.push(c[e]):0!==c[e]&&n[e]&&t.push(c[e]=new Promise(function(t,n){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-009073d4":"0af16c7e","chunk-238a81e9":"e8e2beee","chunk-25908fca":"89ab33e8","chunk-29e7142c":"d10599db","chunk-2d2085ef":"31d6cfe0","chunk-2d22c2e3":"31d6cfe0","chunk-37401378":"a43114f3","chunk-4a59cbe4":"31d6cfe0","chunk-6a68a33e":"3b12267b","chunk-73b2dcec":"99cf6327"}[e]+".css",u=i.p+r,a=document.getElementsByTagName("link"),o=0;o<a.length;o++){var f=a[o],l=f.getAttribute("data-href")||f.getAttribute("href");if("stylesheet"===f.rel&&(l===r||l===u))return t()}var d=document.getElementsByTagName("style");for(o=0;o<d.length;o++){f=d[o],l=f.getAttribute("data-href");if(l===r||l===u)return t()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=t,h.onerror=function(t){var r=t&&t.target&&t.target.src||u,a=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");a.code="CSS_CHUNK_LOAD_FAILED",a.request=r,delete c[e],h.parentNode.removeChild(h),n(a)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)t.push(r[2]);else{var a=new Promise(function(t,n){r=u[e]=[t,n]});t.push(r[2]=a);var f,l=document.createElement("script");l.charset="utf-8",l.timeout=120,i.nc&&l.setAttribute("nonce",i.nc),l.src=o(e),f=function(t){l.onerror=l.onload=null,clearTimeout(d);var n=u[e];if(0!==n){if(n){var r=t&&("load"===t.type?"missing":t.type),c=t&&t.target&&t.target.src,a=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");a.type=r,a.request=c,n[1](a)}u[e]=void 0}};var d=setTimeout(function(){f({type:"timeout",target:l})},12e4);l.onerror=l.onload=f,document.head.appendChild(l)}return Promise.all(t)},i.m=e,i.c=r,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i.oe=function(e){throw console.error(e),e};var f=window["webpackJsonp"]=window["webpackJsonp"]||[],l=f.push.bind(f);f.push=t,f=f.slice();for(var d=0;d<f.length;d++)t(f[d]);var h=l;n()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.095e8b9c.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><link rel=icon href=favicon.ico><title>SOP Admin</title><link href=static/css/chunk-elementUI.81cf475c.css rel=stylesheet><link href=static/css/chunk-libs.3dfb7769.css rel=stylesheet><link href=static/css/app.4f0872ef.css rel=stylesheet></head><body><noscript><strong>We're sorry but SOP Admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div><script>(function(e){function n(n){for(var r,c,a=n[0],f=n[1],i=n[2],l=0,d=[];l<a.length;l++)c=a[l],u[c]&&d.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);h&&h(n);while(d.length)d.shift()();return o.push.apply(o,i||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var a=t[c];0!==u[a]&&(r=!1)}r&&(o.splice(n--,1),e=f(f.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(e){return f.p+"static/js/"+({}[e]||e)+"."+{"chunk-238a81e9":"5955f13d","chunk-25908fca":"ca176fa6","chunk-29e7142c":"994a3ac0","chunk-2d2085ef":"7c741493","chunk-2d208c3a":"93f165b2","chunk-2d221c34":"8f017357","chunk-34c76be7":"98e1e7e5","chunk-37401378":"4e39ec9b","chunk-6f78c9fe":"f1ed64fa","chunk-73b2dcec":"14f248eb","chunk-9b31c83a":"2758df30"}[e]+".js"}function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=[],t={"chunk-238a81e9":1,"chunk-25908fca":1,"chunk-29e7142c":1,"chunk-34c76be7":1,"chunk-37401378":1,"chunk-73b2dcec":1,"chunk-9b31c83a":1};c[e]?n.push(c[e]):0!==c[e]&&t[e]&&n.push(c[e]=new Promise(function(n,t){for(var r="static/css/"+({}[e]||e)+"."+{"chunk-238a81e9":"e8e2beee","chunk-25908fca":"89ab33e8","chunk-29e7142c":"d10599db","chunk-2d2085ef":"31d6cfe0","chunk-2d208c3a":"31d6cfe0","chunk-2d221c34":"31d6cfe0","chunk-34c76be7":"f531fb07","chunk-37401378":"a43114f3","chunk-6f78c9fe":"31d6cfe0","chunk-73b2dcec":"99cf6327","chunk-9b31c83a":"3b12267b"}[e]+".css",u=f.p+r,o=document.getElementsByTagName("link"),a=0;a<o.length;a++){var i=o[a],l=i.getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(l===r||l===u))return n()}var d=document.getElementsByTagName("style");for(a=0;a<d.length;a++){i=d[a],l=i.getAttribute("data-href");if(l===r||l===u)return n()}var h=document.createElement("link");h.rel="stylesheet",h.type="text/css",h.onload=n,h.onerror=function(n){var r=n&&n.target&&n.target.src||u,o=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");o.code="CSS_CHUNK_LOAD_FAILED",o.request=r,delete c[e],h.parentNode.removeChild(h),t(o)},h.href=u;var s=document.getElementsByTagName("head")[0];s.appendChild(h)}).then(function(){c[e]=0}));var r=u[e];if(0!==r)if(r)n.push(r[2]);else{var o=new Promise(function(n,t){r=u[e]=[n,t]});n.push(r[2]=o);var i,l=document.createElement("script");l.charset="utf-8",l.timeout=120,f.nc&&l.setAttribute("nonce",f.nc),l.src=a(e),i=function(n){l.onerror=l.onload=null,clearTimeout(d);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var d=setTimeout(function(){i({type:"timeout",target:l})},12e4);l.onerror=l.onload=i,document.head.appendChild(l)}return Promise.all(n)},f.m=e,f.c=r,f.d=function(e,n,t){f.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,n){if(1&n&&(e=f(e)),8&n)return e;if(4&n&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)f.d(t,r,function(n){return e[n]}.bind(null,r));return t},f.n=function(e){var n=e&&e.__esModule?function(){return e["default"]}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="",f.oe=function(e){throw console.error(e),e};var i=window["webpackJsonp"]=window["webpackJsonp"]||[],l=i.push.bind(i);i.push=n,i=i.slice();for(var d=0;d<i.length;d++)n(i[d]);var h=l;t()})([]);</script><script src=static/js/chunk-elementUI.8ebdfbab.js></script><script src=static/js/chunk-libs.9cf9cc40.js></script><script src=static/js/app.4a507d5e.js></script></body></html>

@ -1 +1 @@
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-b81bfbde]{cursor:pointer;margin-left:10px}
.custom-tree-node{-webkit-box-flex:1;-ms-flex:1;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;padding-right:8px}.el-input.is-disabled .el-input__inner,.el-radio__input.is-disabled+span.el-radio__label{color:#909399}.limit-tip[data-v-51d6f4a2]{cursor:pointer;margin-left:10px}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d22c2e3"],{f1ac:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[n("el-form-item",{attrs:{label:"serviceId"}},[n("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),n("el-form-item",[n("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),n("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[n("el-table-column",{attrs:{prop:"serviceId",label:"服务名称",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",{domProps:{innerHTML:t._s(t.renderServiceName(e.row))}})]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"ipPort",label:"IP端口",width:"250"}}),t._v(" "),n("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?n("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?n("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?n("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),n("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(n("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var n=e.parentId;0===n||t.forEach(function(t){if(t.id===n){var a=t.children;a||(a=[]),a.push(e),t.children=a}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.offline",t,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.online",t,function(){this.tip("上线成功"),e()})})},renderServiceName:function(t){var e="";if(t.children&&t.children.length>0){var n=t.children.filter(function(t){return"UP"===t.status}).length;e=" (".concat(n,"/").concat(t.children.length,")")}return t.serviceId+e}}}),o=i,s=n("2877"),l=Object(s["a"])(o,a,r,!1,null,null,null);e["default"]=l.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d208c3a"],{a5d4:function(t,e,n){"use strict";n.r(e);var a=function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"app-container"},[n("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:t.searchFormData,size:"mini"}},[n("el-form-item",{attrs:{label:"serviceId"}},[n("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"serviceId"},model:{value:t.searchFormData.serviceId,callback:function(e){t.$set(t.searchFormData,"serviceId",e)},expression:"searchFormData.serviceId"}})],1),t._v(" "),n("el-form-item",[n("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:t.onSearchTable}},[t._v("查询")])],1)],1),t._v(" "),n("el-table",{staticStyle:{width:"100%","margin-bottom":"20px"},attrs:{data:t.tableData,border:"","row-key":"id"}},[n("el-table-column",{attrs:{prop:"serviceId",label:"服务名称",width:"200"},scopedSlots:t._u([{key:"default",fn:function(e){return[n("span",{domProps:{innerHTML:t._s(t.renderServiceName(e.row))}})]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"ipPort",label:"IP端口",width:"250"}}),t._v(" "),n("el-table-column",{attrs:{prop:"status",label:"服务状态",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-tag",{attrs:{type:"success"}},[t._v("已上线")]):t._e(),t._v(" "),e.row.parentId>0&&"STARTING"===e.row.status?n("el-tag",{attrs:{type:"info"}},[t._v("正在启动")]):t._e(),t._v(" "),e.row.parentId>0&&"UNKNOWN"===e.row.status?n("el-tag",[t._v("未知")]):t._e(),t._v(" "),e.row.parentId>0&&("OUT_OF_SERVICE"===e.row.status||"DOWN"===e.row.status)?n("el-tag",{attrs:{type:"danger"}},[t._v("已下线")]):t._e()]}}])}),t._v(" "),n("el-table-column",{attrs:{prop:"updateTime",label:"最后更新时间",width:"160"}}),t._v(" "),n("el-table-column",{attrs:{label:"操作",width:"100"},scopedSlots:t._u([{key:"default",fn:function(e){return[e.row.parentId>0&&"UP"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOffline(e.row)}}},[t._v("下线")]):t._e(),t._v(" "),e.row.parentId>0&&"OUT_OF_SERVICE"===e.row.status?n("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(n){return t.onOnline(e.row)}}},[t._v("上线")]):t._e()]}}])})],1)],1)},r=[],i=(n("ac6a"),{data:function(){return{searchFormData:{serviceId:""},tableData:[]}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("service.instance.list",this.searchFormData,function(t){this.tableData=this.buildTreeData(t.data)})},buildTreeData:function(t){return t.forEach(function(e){var n=e.parentId;0===n||t.forEach(function(t){if(t.id===n){var a=t.children;a||(a=[]),a.push(e),t.children=a}})}),t=t.filter(function(t){return 0===t.parentId}),t},onSearchTable:function(){this.loadTable()},onOffline:function(t){this.confirm("确定要下线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.offline",t,function(){this.tip("下线成功"),e()})})},onOnline:function(t){this.confirm("确定要上线【"+t.serviceId+"】吗?",function(e){this.post("service.instance.online",t,function(){this.tip("上线成功"),e()})})},renderServiceName:function(t){var e="";if(t.children&&t.children.length>0){var n=t.children.filter(function(t){return"UP"===t.status}).length;e=" (".concat(n,"/").concat(t.children.length,")")}return t.serviceId+e}}}),o=i,s=n("2877"),l=Object(s["a"])(o,a,r,!1,null,null,null);e["default"]=l.exports}}]);

@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d221c34"],{cc75:function(a,t,e){"use strict";e.r(t);var i=function(){var a=this,t=a.$createElement,e=a._self._c||t;return e("div",{staticClass:"app-container"},[e("el-form",{staticClass:"demo-form-inline",attrs:{inline:!0,model:a.searchFormData,size:"mini"}},[e("el-form-item",{attrs:{label:"IP"}},[e("el-input",{staticStyle:{width:"250px"},attrs:{clearable:!0,placeholder:"输入IP"},model:{value:a.searchFormData.ip,callback:function(t){a.$set(a.searchFormData,"ip",t)},expression:"searchFormData.ip"}})],1),a._v(" "),e("el-form-item",[e("el-button",{attrs:{type:"primary",icon:"el-icon-search"},on:{click:a.loadTable}},[a._v("查询")])],1)],1),a._v(" "),e("el-button",{staticStyle:{"margin-bottom":"10px"},attrs:{type:"primary",size:"mini",icon:"el-icon-plus"},on:{click:a.onAdd}},[a._v("新增IP")]),a._v(" "),e("el-table",{attrs:{data:a.pageInfo.rows,border:"","highlight-current-row":""}},[e("el-table-column",{attrs:{prop:"ip",label:"IP",width:"200"}}),a._v(" "),e("el-table-column",{attrs:{prop:"remark",label:"备注",width:"300"}}),a._v(" "),e("el-table-column",{attrs:{prop:"gmtCreate",label:"添加时间",width:"160"}}),a._v(" "),e("el-table-column",{attrs:{prop:"gmtModified",label:"修改时间",width:"160"}}),a._v(" "),e("el-table-column",{attrs:{label:"操作",width:"150"},scopedSlots:a._u([{key:"default",fn:function(t){return[e("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(e){return a.onTableUpdate(t.row)}}},[a._v("修改")]),a._v(" "),e("el-button",{attrs:{type:"text",size:"mini"},on:{click:function(e){return a.onTableDelete(t.row)}}},[a._v("删除")])]}}])})],1),a._v(" "),e("el-pagination",{staticStyle:{"margin-top":"5px"},attrs:{background:"","current-page":a.searchFormData.pageIndex,"page-size":a.searchFormData.pageSize,"page-sizes":[5,10,20,40],total:a.pageInfo.total,layout:"total, sizes, prev, pager, next"},on:{"size-change":a.onSizeChange,"current-change":a.onPageIndexChange}}),a._v(" "),e("el-dialog",{attrs:{title:a.dialogTitle,visible:a.dialogVisible,"close-on-click-modal":!1},on:{"update:visible":function(t){a.dialogVisible=t},close:function(t){return a.resetForm("dialogForm")}}},[e("el-form",{ref:"dialogForm",attrs:{rules:a.dialogFormRules,model:a.dialogFormData,"label-width":"120px",size:"mini"}},[e("el-form-item",{attrs:{prop:"ip",label:"IP"}},[e("el-input",{directives:[{name:"show",rawName:"v-show",value:0===a.dialogFormData.id,expression:"dialogFormData.id === 0"}],model:{value:a.dialogFormData.ip,callback:function(t){a.$set(a.dialogFormData,"ip",t)},expression:"dialogFormData.ip"}}),a._v(" "),e("span",{directives:[{name:"show",rawName:"v-show",value:a.dialogFormData.id>0,expression:"dialogFormData.id > 0"}]},[a._v(a._s(a.dialogFormData.ip))])],1),a._v(" "),e("el-form-item",{attrs:{prop:"remark",label:"备注"}},[e("el-input",{model:{value:a.dialogFormData.remark,callback:function(t){a.$set(a.dialogFormData,"remark",t)},expression:"dialogFormData.remark"}})],1)],1),a._v(" "),e("div",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[e("el-button",{on:{click:function(t){a.dialogVisible=!1}}},[a._v("取 消")]),a._v(" "),e("el-button",{attrs:{type:"primary"},on:{click:a.onDialogSave}},[a._v("保 存")])],1)],1)],1)},o=[],l={data:function(){var a=function(a,t,e){if(""===t)e(new Error("请输入IP"));else{var i=/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/;i.test(t)||e(new Error("IP格式不正确")),e()}};return{searchFormData:{ip:"",pageIndex:1,pageSize:10},pageInfo:{rows:[],total:0},dialogVisible:!1,dialogTitle:"",dialogFormData:{id:0,ip:"",remark:""},dialogFormRules:{ip:[{validator:a,trigger:"blur"},{min:1,max:64,message:"长度在 1 到 64 个字符",trigger:"blur"}],remark:[{max:100,message:"不能超过 100 个字符",trigger:"blur"}]}}},created:function(){this.loadTable()},methods:{loadTable:function(){this.post("ip.blacklist.page",this.searchFormData,function(a){this.pageInfo=a.data})},onTableUpdate:function(a){var t=this;this.dialogTitle="修改IP",this.dialogVisible=!0,this.$nextTick(function(){Object.assign(t.dialogFormData,a)})},onTableDelete:function(a){this.confirm("确认要移除IP【".concat(a.ip,"】吗?"),function(t){var e={id:a.id};this.post("ip.blacklist.del",e,function(){t(),this.tip("删除成功"),this.loadTable()})})},onDialogSave:function(){var a=this;this.$refs.dialogForm.validate(function(t){if(t){var e=a.dialogFormData.id?"ip.blacklist.update":"ip.blacklist.add";a.post(e,a.dialogFormData,function(){this.dialogVisible=!1,this.loadTable()})}})},onSizeChange:function(a){this.searchFormData.pageSize=a,this.loadTable()},onAdd:function(){this.dialogTitle="新增IP",this.dialogVisible=!0,this.dialogFormData.id=0},onPageIndexChange:function(a){this.searchFormData.pageIndex=a,this.loadTable()}}},r=l,n=e("2877"),s=Object(n["a"])(r,i,o,!1,null,null,null);t["default"]=s.exports}}]);

@ -64,26 +64,32 @@ export const constantRoutes = [
{
path: 'list',
name: 'ServiceList',
component: () => import('@/views/service/list/index'),
component: () => import('@/views/service/serviceList'),
meta: { title: '服务列表' }
},
{
path: 'route',
name: 'Route',
component: () => import('@/views/service/route/index'),
component: () => import('@/views/service/route'),
meta: { title: '路由管理' }
},
{
path: 'limit',
name: 'Limit',
component: () => import('@/views/service/limit/index2'),
component: () => import('@/views/service/limit'),
meta: { title: '限流管理' }
},
{
path: 'log',
name: 'Log',
component: () => import('@/views/service/log/index'),
component: () => import('@/views/service/log'),
meta: { title: '监控日志' }
},
{
path: 'blacklist',
name: 'Blacklist',
component: () => import('@/views/service/ipBlacklist'),
meta: { title: 'IP黑名单' }
}
]
},

@ -108,17 +108,11 @@ export default {
roleDialogFormRules: {
roleCode: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 64, message: '长度在 1 到 100 个字符', trigger: 'blur' }
{ min: 1, max: 64, message: '长度在 1 到 64 个字符', trigger: 'blur' }
],
description: [
{ max: 64, message: '不能超过 64 个字符', trigger: 'blur' }
]
},
rulesIsvForm: {
appKey: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
]
}
}
},

@ -0,0 +1,184 @@
<template>
<div class="app-container">
<el-form :inline="true" :model="searchFormData" class="demo-form-inline" size="mini">
<el-form-item label="IP">
<el-input v-model="searchFormData.ip" :clearable="true" placeholder="输入IP" style="width: 250px;" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="loadTable">查询</el-button>
</el-form-item>
</el-form>
<el-button type="primary" size="mini" icon="el-icon-plus" style="margin-bottom: 10px;" @click="onAdd">新增IP</el-button>
<el-table
:data="pageInfo.rows"
border
highlight-current-row
>
<el-table-column
prop="ip"
label="IP"
width="200"
/>
<el-table-column
prop="remark"
label="备注"
width="300"
/>
<el-table-column
prop="gmtCreate"
label="添加时间"
width="160"
/>
<el-table-column
prop="gmtModified"
label="修改时间"
width="160"
/>
<el-table-column
label="操作"
width="150"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
<el-button type="text" size="mini" @click="onTableDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
background
style="margin-top: 5px"
:current-page="searchFormData.pageIndex"
:page-size="searchFormData.pageSize"
:page-sizes="[5, 10, 20, 40]"
:total="pageInfo.total"
layout="total, sizes, prev, pager, next"
@size-change="onSizeChange"
@current-change="onPageIndexChange"
/>
<!--dialog-->
<el-dialog
:title="dialogTitle"
:visible.sync="dialogVisible"
:close-on-click-modal="false"
@close="resetForm('dialogForm')"
>
<el-form
ref="dialogForm"
:rules="dialogFormRules"
:model="dialogFormData"
label-width="120px"
size="mini"
>
<el-form-item prop="ip" label="IP">
<el-input v-show="dialogFormData.id === 0" v-model="dialogFormData.ip" />
<span v-show="dialogFormData.id > 0">{{ dialogFormData.ip }}</span>
</el-form-item>
<el-form-item prop="remark" label="备注">
<el-input v-model="dialogFormData.remark" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="onDialogSave"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
data() {
const ipValidator = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入IP'))
} else {
const regexIP = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
if (!regexIP.test(value)) {
callback(new Error('IP格式不正确'))
}
callback()
}
}
return {
searchFormData: {
ip: '',
pageIndex: 1,
pageSize: 10
},
pageInfo: {
rows: [],
total: 0
},
dialogVisible: false,
dialogTitle: '',
dialogFormData: {
id: 0,
ip: '',
remark: ''
},
dialogFormRules: {
ip: [
{ validator: ipValidator, trigger: 'blur' },
{ min: 1, max: 64, message: '长度在 1 到 64 个字符', trigger: 'blur' }
],
remark: [
{ max: 100, message: '不能超过 100 个字符', trigger: 'blur' }
]
}
}
},
created() {
this.loadTable()
},
methods: {
loadTable: function() {
this.post('ip.blacklist.page', this.searchFormData, function(resp) {
this.pageInfo = resp.data
})
},
onTableUpdate: function(row) {
this.dialogTitle = '修改IP'
this.dialogVisible = true
this.$nextTick(() => {
Object.assign(this.dialogFormData, row)
})
},
onTableDelete: function(row) {
this.confirm(`确认要移除IP【${row.ip}】吗?`, function(done) {
const data = {
id: row.id
}
this.post('ip.blacklist.del', data, function() {
done()
this.tip('删除成功')
this.loadTable()
})
})
},
onDialogSave: function() {
this.$refs.dialogForm.validate((valid) => {
if (valid) {
const uri = this.dialogFormData.id ? 'ip.blacklist.update' : 'ip.blacklist.add'
this.post(uri, this.dialogFormData, function() {
this.dialogVisible = false
this.loadTable()
})
}
})
},
onSizeChange: function(size) {
this.searchFormData.pageSize = size
this.loadTable()
},
onAdd: function() {
this.dialogTitle = '新增IP'
this.dialogVisible = true
this.dialogFormData.id = 0
},
onPageIndexChange: function(pageIndex) {
this.searchFormData.pageIndex = pageIndex
this.loadTable()
}
}
}
</script>

@ -1,347 +0,0 @@
<template>
<div class="app-container">
<el-container>
<el-aside style="min-height: 300px;width: 200px;">
<el-input v-model="filterText" prefix-icon="el-icon-search" placeholder="搜索服务..." style="margin-bottom:20px;" size="mini" clearable />
<el-tree
ref="tree2"
:data="treeData"
:props="defaultProps"
:filter-node-method="filterNode"
:highlight-current="true"
:expand-on-click-node="false"
empty-text="无数据"
node-key="id"
class="filter-tree"
default-expand-all
@node-click="onNodeClick"
>
<span slot-scope="{ node, data }" class="custom-tree-node">
<span v-if="data.label.length < 15">{{ data.label }}</span>
<span v-else>
<el-tooltip :content="data.label" class="item" effect="light" placement="right">
<span>{{ data.label.substring(0, 15) + '...' }}</span>
</el-tooltip>
</span>
</span>
</el-tree>
</el-aside>
<el-main style="padding-top:0">
<el-form :inline="true" :model="searchFormData" class="demo-form-inline">
<el-form-item label="路由名称">
<el-input v-model="searchFormData.id" placeholder="输入接口名或版本号" size="mini" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="onSearchTable">查询</el-button>
</el-form-item>
</el-form>
<el-table
:data="tableData"
border
max-height="500"
>
<el-table-column
prop="name"
label="接口名 (版本号)"
width="200"
>
<template slot-scope="scope">
{{ scope.row.name + (scope.row.version ? ' (' + scope.row.version + ')' : '') }}
</template>
</el-table-column>
<el-table-column
prop="limitType"
label="限流策略"
width="120"
>
<template slot-scope slot="header">
限流策略 <i class="el-icon-question" style="cursor: pointer" @click="onLimitTypeTipClick"></i>
</template>
<template slot-scope="scope">
<span v-if="scope.row.limitType === 1">漏桶策略</span>
<span v-if="scope.row.limitType === 2">令牌桶策略</span>
</template>
</el-table-column>
<el-table-column
prop="info"
label="限流信息"
width="500"
>
<template slot-scope="scope">
<span v-html="infoRender(scope.row)"></span>
</template>
</el-table-column>
<el-table-column
prop="limitStatus"
label="状态"
width="80"
>
<template slot-scope="scope">
<span v-if="scope.row.limitStatus === 1" style="color:#67C23A">已开启</span>
<span v-if="scope.row.limitStatus === 0" style="color:#909399">已关闭</span>
</template>
</el-table-column>
<el-table-column
label="操作"
width="80"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="onTableUpdate(scope.row)">修改</el-button>
</template>
</el-table-column>
</el-table>
<!-- dialog -->
<el-dialog
title="设置限流"
:visible.sync="limitDialogVisible"
:close-on-click-modal="false"
@close="onLimitDialogClose"
>
<el-form ref="limitDialogFormMain" :model="limitDialogFormData">
<el-form-item label="id" :label-width="formLabelWidth">
<el-input v-model="limitDialogFormData.routeId" readonly="readonly"/>
</el-form-item>
<el-form-item label="限流策略" :label-width="formLabelWidth">
<el-radio-group v-model="limitDialogFormData.limitType">
<el-radio :label="1">漏桶策略</el-radio>
<el-radio :label="2">令牌桶策略</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="开启状态" :label-width="formLabelWidth">
<el-switch
v-model="limitDialogFormData.limitStatus"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
</el-form>
<el-form
v-show="limitDialogFormData.limitType === 1 && limitDialogFormData.limitStatus"
ref="limitDialogFormLeaky"
:rules="rulesLeaky"
:model="limitDialogFormData"
>
<el-form-item label="每秒可处理请求数" prop="execCountPerSecond" :label-width="formLabelWidth">
<el-input-number v-model="limitDialogFormData.execCountPerSecond" controls-position="right" :min="1" />
</el-form-item>
<el-form-item label="错误码" prop="limitCode" :label-width="formLabelWidth">
<el-input v-model="limitDialogFormData.limitCode" />
</el-form-item>
<el-form-item label="错误信息" prop="limitMsg" :label-width="formLabelWidth">
<el-input v-model="limitDialogFormData.limitMsg" />
</el-form-item>
</el-form>
<el-form
v-show="limitDialogFormData.limitType === 2 && limitDialogFormData.limitStatus"
ref="limitDialogFormToken"
:rules="rulesToken"
:model="limitDialogFormData"
>
<el-form-item label="令牌桶容量" prop="tokenBucketCount" :label-width="formLabelWidth">
<el-input-number v-model="limitDialogFormData.tokenBucketCount" controls-position="right" :min="1" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="limitDialogVisible = false"> </el-button>
<el-button type="primary" @click="onLimitDialogSave"> </el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</div>
</template>
<script>
export default {
data() {
return {
filterText: '',
treeData: [],
tableData: [],
serviceId: '',
searchFormData: {},
defaultProps: {
children: 'children',
label: 'label'
},
// dialog
limitDialogFormData: {
routeId: '',
execCountPerSecond: 5,
limitCode: '',
limitMsg: '',
tokenBucketCount: 5,
limitStatus: 0, // 0: 1
limitType: 1
},
rulesLeaky: {
execCountPerSecond: [
{ required: true, message: '不能为空', trigger: 'blur' }
],
limitCode: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 64, message: '长度在 1 到 64 个字符', trigger: 'blur' }
],
limitMsg: [
{ required: true, message: '不能为空', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
]
},
rulesToken: {
tokenBucketCount: [
{ required: true, message: '不能为空', trigger: 'blur' }
]
},
formLabelWidth: '150px',
limitDialogVisible: false
}
},
watch: {
filterText(val) {
this.$refs.tree2.filter(val)
}
},
created() {
this.loadTree()
},
methods: {
//
loadTree: function() {
this.post('zookeeper.service.list', {}, function(resp) {
const respData = resp.data
this.treeData = this.convertToTreeData(respData, 0)
})
},
//
filterNode(value, data) {
if (!value) return true
return data.label.indexOf(value) !== -1
},
//
onNodeClick(data, node, tree) {
if (data.parentId) {
this.serviceId = data.label
this.searchFormData.serviceId = this.serviceId
this.loadTable()
}
},
/**
* 数组转成树状结构
* @param data 数据结构 [{
"_parentId": 14,
"gmtCreate": "2019-01-15 09:44:38",
"gmtUpdate": "2019-01-15 09:44:38",
"id": 15,
"isShow": 1,
"name": "用户注册",
"orderIndex": 10000,
"parentId": 14
},...]
* @param pid 初始父节点id一般是0
* @return 返回结果 [{
label: '一级 1',
children: [{
label: '二级 1-1',
children: [{
label: '三级 1-1-1'
}]
}]
}
*/
convertToTreeData(data, pid) {
const result = []
const root = {
label: '服务列表',
parentId: pid
}
const children = []
for (let i = 0; i < data.length; i++) {
const item = { label: data[i].serviceId, parentId: 1 }
children.push(item)
}
root.children = children
result.push(root)
return result
},
// table
loadTable: function() {
this.post('route.limit.list', this.searchFormData, function(resp) {
this.tableData = resp.data
})
},
onSearchTable: function() {
this.loadTable()
},
onTableUpdate: function(row) {
this.limitDialogVisible = true
this.$nextTick(() => {
Object.assign(this.limitDialogFormData, row)
})
},
resetForm(formName) {
const frm = this.$refs[formName]
frm && frm.resetFields()
},
onLimitDialogClose: function() {
this.resetForm('limitDialogFormLeaky')
this.resetForm('limitDialogFormToken')
this.limitDialogVisible = false
},
infoRender: function(row) {
if (!row.hasRecord) {
return '--'
}
const html = []
if (row.limitType === 1) {
html.push('每秒可处理请求数:' + row.execCountPerSecond)
html.push('subCode:' + row.limitCode)
html.push('subMsg:' + row.limitMsg)
} else if (row.limitType === 2) {
html.push('令牌桶容量:' + row.tokenBucketCount)
}
return html.join(',')
},
onLimitDialogSave: function() {
this.doValidate(function() {
this.limitDialogFormData.serviceId = this.serviceId
this.post('route.limit.update', this.limitDialogFormData, function(resp) {
this.limitDialogVisible = false
this.loadTable()
})
})
},
doValidate: function(callback) {
const that = this
if (this.limitDialogFormData.limitStatus === 0) {
callback.call(this)
return
}
if (this.limitDialogFormData.limitType === 1) {
this.$refs['limitDialogFormLeaky'].validate((valid) => {
if (valid) {
callback.call(that)
}
})
} else {
this.$refs['limitDialogFormToken'].validate((valid) => {
if (valid) {
callback.call(that)
}
})
}
},
onLimitTypeTipClick: function() {
const leakyRemark = '漏桶策略:每秒处理固定数量的请求,超出请求返回错误信息。'
const tokenRemark = '令牌桶策略:每秒放置固定数量的令牌数,每个请求进来后先去拿令牌,拿到了令牌才能继续,拿不到则等候令牌重新生成了再拿。'
const content = leakyRemark + '<br>' + tokenRemark
this.$alert(content, '限流策略', {
dangerouslyUseHTMLString: true
})
}
}
}
</script>

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>

@ -5,11 +5,11 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sop-gateway-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sop-gateway-common</name>

@ -5,10 +5,12 @@ import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor;
import com.gitee.sop.gatewaycommon.limit.DefaultLimitManager;
import com.gitee.sop.gatewaycommon.limit.LimitManager;
import com.gitee.sop.gatewaycommon.manager.DefaultIPBlacklistManager;
import com.gitee.sop.gatewaycommon.manager.DefaultIsvRoutePermissionManager;
import com.gitee.sop.gatewaycommon.manager.DefaultLimitConfigManager;
import com.gitee.sop.gatewaycommon.manager.DefaultRouteConfigManager;
import com.gitee.sop.gatewaycommon.manager.DefaultServiceErrorManager;
import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager;
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
import com.gitee.sop.gatewaycommon.manager.LimitConfigManager;
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
@ -115,6 +117,11 @@ public class ApiConfig {
*/
private LimitConfigManager limitConfigManager = new DefaultLimitConfigManager();
/**
* IP黑名单
*/
private IPBlacklistManager ipBlacklistManager = new DefaultIPBlacklistManager();
/**
* 限流管理
*/

@ -17,4 +17,9 @@ public class GatewayParamBuilder extends BaseParamBuilder<ServerWebExchange> {
Map<String, String> params = GatewayContext.getRequestParams(exchange);
return params == null ? Collections.emptyMap() : params;
}
@Override
public String getIP(ServerWebExchange ctx) {
return ctx.getRequest().getRemoteAddress().getAddress().getHostAddress();
}
}

@ -53,6 +53,12 @@ public class AbstractConfiguration implements ApplicationContextAware {
LimitConfigManager limitConfigManager() {
return ApiConfig.getInstance().getLimitConfigManager();
}
@Bean
IPBlacklistManager ipBlacklistManager() {
return ApiConfig.getInstance().getIpBlacklistManager();
}
/**
* 跨域过滤器
*

@ -0,0 +1,38 @@
package com.gitee.sop.gatewaycommon.manager;
import com.google.common.collect.Sets;
import org.apache.commons.lang.StringUtils;
import java.util.Set;
/**
* ip黑名单管理
* @author tanghc
*/
public class DefaultIPBlacklistManager implements IPBlacklistManager {
private static Set<String> ipList = Sets.newConcurrentHashSet();
@Override
public void add(String ip) {
ipList.add(ip);
}
@Override
public void remove(String ip) {
ipList.remove(ip);
}
@Override
public boolean contains(String ip) {
if (StringUtils.isBlank(ip)) {
return false;
}
return ipList.contains(ip);
}
@Override
public void load() {
}
}

@ -0,0 +1,16 @@
package com.gitee.sop.gatewaycommon.manager;
import com.gitee.sop.gatewaycommon.bean.BeanInitializer;
/**
* @author tanghc
*/
public interface IPBlacklistManager extends BeanInitializer {
void add(String ip);
void remove(String ip);
boolean contains(String ip);
}

@ -81,6 +81,10 @@ public class ZookeeperContext {
return SOP_MSG_CHANNEL_PATH + "/limit-conf";
}
public static String getIpBlacklistChannelPath() {
return SOP_MSG_CHANNEL_PATH + "/ipblacklist-conf";
}
public static CuratorFramework getClient() {
return client;
}

@ -100,6 +100,8 @@ public enum ErrorEnum {
ISV_ROUTE_NO_PERMISSIONS(Codes.CODE_ISV_PERM, "isv.route-no-permissions"),
/** 禁止访问 */
ISV_ACCESS_FORBIDDEN(Codes.CODE_ISV_PERM, "isv.access-forbidden"),
/** 禁止IP访问 */
ISV_IP_FORBIDDEN(Codes.CODE_ISV_PERM, "isv.ip-forbidden"),
;
private ErrorMeta errorMeta;

@ -27,6 +27,8 @@ public class ApiParam extends JSONObject implements Param {
private String restName;
private String restVersion;
private String ip;
private transient ApiUploadContext apiUploadContext;
public void fitNameVersion() {
@ -236,4 +238,12 @@ public class ApiParam extends JSONObject implements Param {
public void setRestVersion(String restVersion) {
this.restVersion = restVersion;
}
public void setIp(String ip) {
this.ip = ip;
}
public String fetchIp() {
return ip;
}
}

@ -19,6 +19,8 @@ public abstract class BaseParamBuilder<T> implements ParamBuilder<T> {
public abstract Map<String, String> buildRequestParams(T ctx);
public abstract String getIP(T ctx);
@Override
public ApiParam build(T ctx) {
ApiParam apiParam = this.newApiParam(ctx);
@ -27,6 +29,7 @@ public abstract class BaseParamBuilder<T> implements ParamBuilder<T> {
apiParam.put(entry.getKey(), entry.getValue());
}
this.initOtherProperty(apiParam);
apiParam.setIp(this.getIP(ctx));
return apiParam;
}

@ -6,6 +6,7 @@ import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
import com.gitee.sop.gatewaycommon.bean.Isv;
import com.gitee.sop.gatewaycommon.bean.RouteConfig;
import com.gitee.sop.gatewaycommon.bean.TargetRoute;
import com.gitee.sop.gatewaycommon.manager.IPBlacklistManager;
import com.gitee.sop.gatewaycommon.manager.IsvRoutePermissionManager;
import com.gitee.sop.gatewaycommon.manager.RouteConfigManager;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
@ -46,6 +47,7 @@ public class ApiValidator implements Validator {
@Override
public void validate(ApiParam param) {
checkIP(param);
checkEnable(param);
ApiConfig apiConfig = ApiContext.getApiConfig();
@ -64,6 +66,18 @@ public class ApiValidator implements Validator {
checkPermission(param);
}
/**
* 是否在IP黑名单中
* @param param 接口参数
*/
protected void checkIP(ApiParam param) {
IPBlacklistManager ipBlacklistManager = ApiConfig.getInstance().getIpBlacklistManager();
String ip = param.fetchIp();
if (ipBlacklistManager.contains(ip)) {
throw ErrorEnum.ISV_IP_FORBIDDEN.getErrorMeta().getException();
}
}
/**
* 检测能否访问
* @param param 接口参数

@ -36,7 +36,7 @@ public class PreValidateFilter extends BaseZuulFilter {
try {
validator.validate(param);
} catch (ApiException e) {
log.error("验证失败,params:{}", param.toJSONString(), e);
log.error("验证失败,ip:{}, params:{}", param.fetchIp(), param.toJSONString(), e);
throw e;
} finally {
param.fitNameVersion();

@ -62,6 +62,11 @@ public class ZuulParamBuilder extends BaseParamBuilder<RequestContext> {
return params;
}
@Override
public String getIP(RequestContext ctx) {
return RequestUtil.getIP(ctx.getRequest());
}
@Override
protected ApiParam newApiParam(RequestContext ctx) {
ApiParam apiParam = super.newApiParam(ctx);

@ -56,3 +56,4 @@ open.error_40006_isv.insufficient-isv-permissions=Insufficient ISV permissions
open.error_40006_isv.insufficient-user-permissions=Insufficient user permissions
open.error_40006_isv.route-no-permissions=No api permissions
open.error_40006_isv.access-forbidden=Access forbidden
open.error_40006_isv.ip-forbidden=IP access forbidden

@ -111,3 +111,4 @@ open.error_40006_isv.insufficient-isv-permissions=\u8bf7\u68c0\u67e5\u914d\u7f6e
open.error_40006_isv.insufficient-user-permissions=\u4ee3\u7406\u7684\u5546\u6237\u6ca1\u6709\u5f53\u524d\u63a5\u53e3\u6743\u9650
open.error_40006_isv.route-no-permissions=\u6ca1\u6709\u5f53\u524d\u63a5\u53e3\u6743\u9650
open.error_40006_isv.access-forbidden=\u65e0\u6743\u8bbf\u95ee
open.error_40006_isv.ip-forbidden=IP\u65e0\u6743\u8bbf\u95ee

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-registry-api</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

@ -6,11 +6,11 @@
<parent>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sop-service-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>sop-service-common</name>

@ -26,7 +26,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

@ -24,7 +24,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>

@ -29,7 +29,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>

@ -20,7 +20,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<!-- eureka 服务发现 -->
<dependency>

@ -24,7 +24,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.gitee.sop</groupId>

@ -23,7 +23,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓ -->

@ -0,0 +1,64 @@
package com.gitee.sop.gateway.manager;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.gateway.mapper.IPBlacklistMapper;
import com.gitee.sop.gatewaycommon.bean.ChannelMsg;
import com.gitee.sop.gatewaycommon.manager.DefaultIPBlacklistManager;
import com.gitee.sop.gatewaycommon.manager.ZookeeperContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import javax.annotation.PostConstruct;
import java.util.List;
/**
* 限流配置管理
* @author tanghc
*/
@Slf4j
public class DbIPBlacklistManager extends DefaultIPBlacklistManager {
@Autowired
IPBlacklistMapper ipBlacklistMapper;
@Autowired
Environment environment;
@Override
public void load() {
List<String> ipList = ipBlacklistMapper.listAllIP();
log.info("加载IP黑名单, size:{}", ipList.size());
ipList.stream().forEach(this::add);
}
@PostConstruct
protected void after() throws Exception {
ZookeeperContext.setEnvironment(environment);
String path = ZookeeperContext.getIpBlacklistChannelPath();
ZookeeperContext.listenPath(path, nodeCache -> {
String nodeData = new String(nodeCache.getCurrentData().getData());
ChannelMsg channelMsg = JSON.parseObject(nodeData, ChannelMsg.class);
final IPDto ipDto = JSON.parseObject(channelMsg.getData(), IPDto.class);
String ip = ipDto.getIp();
switch (channelMsg.getOperation()) {
case "add":
log.info("添加IP黑名单,ip:{}", ip);
add(ip);
break;
case "delete":
log.info("移除IP黑名单,ip:{}", ip);
remove(ip);
break;
}
});
}
@Data
private static class IPDto {
private String ip;
}
}

@ -13,5 +13,6 @@ public class ManagerInitializer {
apiConfig.setIsvRoutePermissionManager(new DbIsvRoutePermissionManager());
apiConfig.setRouteConfigManager(new DbRouteConfigManager());
apiConfig.setLimitConfigManager(new DbLimitConfigManager());
apiConfig.setIpBlacklistManager(new DbIPBlacklistManager());
}
}

@ -0,0 +1,22 @@
package com.gitee.sop.gateway.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* IP黑名单
* @author tanghc
*/
@Mapper
public interface IPBlacklistMapper {
/**
* 获取所有IP
* @return
*/
@Select("SELECT ip FROM config_ip_blacklist")
List<String> listAllIP();
}

@ -25,7 +25,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-registry-api</artifactId>
<version>1.12.4-SNAPSHOT</version>
<version>1.13.0-SNAPSHOT</version>
</dependency>
<dependency>

@ -14,6 +14,7 @@ DROP TABLE IF EXISTS `config_limit`;
DROP TABLE IF EXISTS `admin_user_info`;
DROP TABLE IF EXISTS `config_common`;
DROP TABLE IF EXISTS `isv_keys`;
DROP TABLE IF EXISTS `config_ip_blacklist`;
CREATE TABLE `admin_user_info` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
@ -164,6 +165,16 @@ CREATE TABLE `isv_keys` (
UNIQUE KEY `uk_appkey` (`app_key`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='ISV秘钥';
CREATE TABLE `config_ip_blacklist` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`ip` varchar(64) NOT NULL DEFAULT '' COMMENT 'ip',
`remark` varchar(128) DEFAULT NULL COMMENT '备注',
`gmt_create` datetime DEFAULT CURRENT_TIMESTAMP,
`gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ip` (`ip`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='IP黑名单';
SET FOREIGN_KEY_CHECKS = @PREVIOUS_FOREIGN_KEY_CHECKS;

Loading…
Cancel
Save