diff --git a/changelog.md b/changelog.md
index 6249dd9a..dd4d0905 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,9 @@
# changelog
+## 2.5.0
+
+- 网关可校验token,见`com.gitee.sop.storyweb.controller.TokenController`(需要执行`sop-2.5.0.sql`升级文件)
+
## 2.4.1
- 优化restful接口调用(如果正在使用此功能,必看 [doc](https://durcframework.gitee.io/sop/#/files/10100_%E6%8F%90%E4%BE%9Brestful%E6%8E%A5%E5%8F%A3?t=1571107529449))
diff --git a/doc/docs/_sidebar.md b/doc/docs/_sidebar.md
index 38a92c3e..6270b7b6 100644
--- a/doc/docs/_sidebar.md
+++ b/doc/docs/_sidebar.md
@@ -1,34 +1,35 @@
-* [首页](/?t=1571107529420)
+* [首页](/?t=1572076365237)
* 开发文档
- * [快速体验](files/10010_快速体验.md?t=1571107529423)
- * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1571107529447)
- * [新增接口](files/10020_新增接口.md?t=1571107529447)
- * [开发流程](files/10021_开发流程.md?t=1571107529447)
- * [业务参数校验](files/10030_业务参数校验.md?t=1571107529447)
- * [错误处理](files/10040_错误处理.md?t=1571107529447)
- * [编写文档](files/10041_编写文档.md?t=1571107529447)
- * [接口交互详解](files/10050_接口交互详解.md?t=1571107529448)
- * [easyopen支持](files/10070_easyopen支持.md?t=1571107529448)
- * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1571107529448)
- * [ISV管理](files/10085_ISV管理.md?t=1571107529448)
- * [自定义返回结果](files/10087_自定义返回结果.md?t=1571107529448)
- * [自定义过滤器](files/10088_自定义过滤器.md?t=1571107529448)
- * [路由授权](files/10090_路由授权.md?t=1571107529448)
- * [接口限流](files/10092_接口限流.md?t=1571107529448)
- * [监控日志](files/10093_监控日志.md?t=1571107529448)
- * [SDK开发](files/10095_SDK开发.md?t=1571107529449)
- * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1571107529449)
- * [应用授权](files/10097_应用授权.md?t=1571107529449)
- * [提供restful接口](files/10100_提供restful接口.md?t=1571107529449)
- * [文件上传](files/10104_文件上传.md?t=1571107529449)
- * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1571107529449)
- * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1571107529449)
- * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1571107529450)
- * [使用eureka](files/10112_使用eureka.md?t=1571107529450)
- * [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1571107529450)
+ * [快速体验](files/10010_快速体验.md?t=1572076365239)
+ * [项目接入到SOP](files/10011_项目接入到SOP.md?t=1572076365257)
+ * [新增接口](files/10020_新增接口.md?t=1572076365257)
+ * [开发流程](files/10021_开发流程.md?t=1572076365257)
+ * [业务参数校验](files/10030_业务参数校验.md?t=1572076365258)
+ * [错误处理](files/10040_错误处理.md?t=1572076365258)
+ * [编写文档](files/10041_编写文档.md?t=1572076365258)
+ * [接口交互详解](files/10050_接口交互详解.md?t=1572076365258)
+ * [easyopen支持](files/10070_easyopen支持.md?t=1572076365258)
+ * [使用签名校验工具](files/10080_使用签名校验工具.md?t=1572076365258)
+ * [ISV管理](files/10085_ISV管理.md?t=1572076365258)
+ * [自定义返回结果](files/10087_自定义返回结果.md?t=1572076365259)
+ * [自定义过滤器](files/10088_自定义过滤器.md?t=1572076365259)
+ * [自定义校验token](files/10089_自定义校验token.md?t=1572076365259)
+ * [路由授权](files/10090_路由授权.md?t=1572076365259)
+ * [接口限流](files/10092_接口限流.md?t=1572076365259)
+ * [监控日志](files/10093_监控日志.md?t=1572076365259)
+ * [SDK开发](files/10095_SDK开发.md?t=1572076365259)
+ * [使用SpringCloudGateway](files/10096_使用SpringCloudGateway.md?t=1572076365260)
+ * [应用授权](files/10097_应用授权.md?t=1572076365260)
+ * [提供restful接口](files/10100_提供restful接口.md?t=1572076365260)
+ * [文件上传](files/10104_文件上传.md?t=1572076365260)
+ * [配置Sleuth链路追踪](files/10109_配置Sleuth链路追踪.md?t=1572076365260)
+ * [预发布灰度发布](files/10110_预发布灰度发布.md?t=1572076365261)
+ * [动态修改请求参数](files/10111_动态修改请求参数.md?t=1572076365261)
+ * [使用eureka](files/10112_使用eureka.md?t=1572076365261)
+ * [扩展其它注册中心](files/10113_扩展其它注册中心.md?t=1572076365261)
* 原理分析
- * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1571107529450)
- * [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1571107529450)
- * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1571107529450)
- * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1571107529450)
- * [常见问题](files/90100_常见问题.md?t=1571107529450)
+ * [原理分析之@ApiMapping](files/90010_原理分析之@ApiMapping.md?t=1572076365261)
+ * [原理分析之如何存储路由](files/90011_原理分析之如何存储路由.md?t=1572076365261)
+ * [原理分析之如何路由](files/90012_原理分析之如何路由.md?t=1572076365261)
+ * [原理分析之文档归纳](files/90013_原理分析之文档归纳.md?t=1572076365262)
+ * [常见问题](files/90100_常见问题.md?t=1572076365262)
diff --git a/doc/docs/files/10089_自定义校验token.md b/doc/docs/files/10089_自定义校验token.md
new file mode 100644
index 00000000..365e77c2
--- /dev/null
+++ b/doc/docs/files/10089_自定义校验token.md
@@ -0,0 +1,54 @@
+# 自定义校验token(2.5.0)
+
+从2.5.0开始在`@ApiMapping`注解中新增了一个属性`needToken`,用来告诉网关是否校验token
+
+```java
+/**
+* 是否需要appAuthToken,设置为true,网关端会校验token是否存在
+*/
+boolean needToken() default false;
+```
+
+使用方式:
+
+```java
+@ApiMapping(value = "story.token.get", needToken = true/* 设置true,网关会校验token是否存在 */)
+public StoryResult token(StoryParam story) {
+ OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
+ String appAuthToken = openContext.getAppAuthToken();
+ StoryResult result = new StoryResult();
+ result.setName("appAuthToken:" + appAuthToken);
+ return result;
+}
+```
+
+指定了needToken=true后,网关会判断客户端是否传了`app_auth_token`参数,没有传则返回错误信息。
+
+网关默认简单校验参数值是否存在,如果要校验有效性,需要自己实现。
+
+自己实现步骤:
+
+- 在ZuulConfig类中重写`doAfter`方法
+- 设置`ApiConfig中的tokenValidator属性`
+
+`TokenValidator`是一个函数式接口,可以直接使用Lambda表达式,示例代码如下:
+
+```java
+public class ZuulConfig extends AlipayZuulConfiguration {
+
+ @Override
+ protected void doAfter() {
+ ApiConfig.getInstance().setTokenValidator(apiParam -> {
+ // 获取客户端传递过来的token
+ String token = apiParam.fetchAccessToken();
+ if (StringUtils.isBlank(token)) {
+ return false;
+ }
+ // TODO: 校验token有效性,可以从redis中读取
+
+ // 返回true表示这个token真实、有效
+ return true;
+ });
+ }
+}
+```
diff --git a/sop-2.5.0.sql b/sop-2.5.0.sql
new file mode 100644
index 00000000..5743cdad
--- /dev/null
+++ b/sop-2.5.0.sql
@@ -0,0 +1 @@
+ALTER TABLE `sop`.`config_service_route` ADD COLUMN `need_token` TINYINT NOT NULL DEFAULT 0 COMMENT '是否需要token' AFTER `permission`;
diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java
index 46205320..9a1819e5 100644
--- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java
+++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/param/RouteSearchParam.java
@@ -26,4 +26,8 @@ public class RouteSearchParam extends PageParam {
@ApiDocField(description = "是否授权接口,1:是")
@Condition(ignoreValue = "0")
private Integer permission;
+
+ @ApiDocField(description = "是否需要token接口,1:是")
+ @Condition(ignoreValue = "0")
+ private Integer needToken;
}
diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/RouteVO.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/RouteVO.java
index c189ab70..9c548c9a 100644
--- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/RouteVO.java
+++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/api/service/result/RouteVO.java
@@ -49,6 +49,9 @@ public class RouteVO {
/** 是否合并结果, 数据库字段:merge_result */
private Byte mergeResult;
+ /** 是否需要token, 数据库字段:need_token */
+ private Byte needToken;
+
/** 是否需要授权才能访问, 数据库字段:permission */
private Byte permission;
diff --git a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigServiceRoute.java b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigServiceRoute.java
index 369b2e42..0234fb21 100644
--- a/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigServiceRoute.java
+++ b/sop-admin/sop-admin-server/src/main/java/com/gitee/sop/adminserver/entity/ConfigServiceRoute.java
@@ -61,6 +61,9 @@ public class ConfigServiceRoute {
/** 是否需要授权才能访问, 数据库字段:permission */
private Byte permission;
+ /** 是否需要token, 数据库字段:need_token */
+ private Byte needToken;
+
/** 数据库字段:gmt_create */
private Date gmtCreate;
diff --git a/sop-admin/sop-admin-server/src/main/resources/public/index.html b/sop-admin/sop-admin-server/src/main/resources/public/index.html
index ff0b6f35..359b4615 100644
--- a/sop-admin/sop-admin-server/src/main/resources/public/index.html
+++ b/sop-admin/sop-admin-server/src/main/resources/public/index.html
@@ -1 +1 @@
-
SOP Admin
\ No newline at end of file
+SOP Admin
\ No newline at end of file
diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.52bc6b2c.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.52bc6b2c.js
new file mode 100644
index 00000000..7b966db8
--- /dev/null
+++ b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.52bc6b2c.js
@@ -0,0 +1 @@
+(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9b31c83a"],{"048e":function(e,t,o){},"0ce7":function(e,t,o){"use strict";var i=o("048e"),a=o.n(i);a.a},"5f67":function(e,t,o){"use strict";o.r(t);var i=function(){var e=this,t=e.$createElement,o=e._self._c||t;return o("div",{staticClass:"app-container"},[o("el-container",[o("el-aside",{staticStyle:{"min-height":"300px",width:"250px"}},[o("el-button",{staticStyle:{display:"none"},attrs:{type:"primary",plain:"",size:"mini",icon:"el-icon-plus"},on:{click:function(t){return t.stopPropagation(),e.addService(t)}}},[e._v("\n 新建服务\n ")]),e._v(" "),o("el-input",{staticStyle:{"margin-bottom":"10px","margin-top":"10px"},attrs:{"prefix-icon":"el-icon-search",placeholder:"搜索服务...",size:"mini",clearable:""},model:{value:e.filterText,callback:function(t){e.filterText=t},expression:"filterText"}}),e._v(" "),o("el-tree",{ref:"serviceTree",staticClass:"filter-tree",attrs:{data:e.treeData,props:e.defaultProps,"filter-node-method":e.filterNode,"highlight-current":!0,"expand-on-click-node":!1,"empty-text":"无数据","node-key":"serviceId","default-expand-all":""},on:{"node-click":e.onNodeClick},scopedSlots:e._u([{key:"default",fn:function(t){t.node;var i=t.data;return o("span",{staticClass:"custom-tree-node"},[o("div",[o("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:i.custom,expression:"data.custom"}],staticClass:"item",attrs:{content:"自定义服务",effect:"light",placement:"left"}},[o("i",{staticClass:"el-icon-warning-outline"})]),e._v(" "),i.label.length0?t.join(", "):'未授权'},onRouteDialogSave:function(){var e=this;this.$refs.routeDialogFormRef.validate(function(t){if(t){var o=e.routeDialogFormData.id?"route.update":"route.add";e.routeDialogFormData.serviceId=e.serviceId,e.post(o,e.routeDialogFormData,function(){this.routeDialogVisible=!1,this.loadTable()})}})},onAuthDialogSave:function(){this.post("route.role.update",this.authDialogFormData,function(){this.authDialogVisible=!1,this.loadTable()})},addService:function(){this.addServiceDialogVisible=!0},closeAddServiceDlg:function(){this.$refs.addServiceForm.resetFields()},onAddService:function(){var e=this;this.$refs.addServiceForm.validate(function(t){t&&e.post("service.custom.add",e.addServiceForm,function(e){this.addServiceDialogVisible=!1,this.tip("添加成功"),this.loadTree()})})},onDelService:function(e){var t=e.serviceId;this.confirm("确认要删除服务"+t+"吗,【对应的路由配置会一起删除】",function(e){var o={serviceId:t};this.post("service.custom.del",o,function(){e(),this.tip("删除成功"),this.loadTree()})})},onSizeChange:function(e){this.searchFormData.pageSize=e,this.loadTable()},onPageIndexChange:function(e){this.searchFormData.pageIndex=e,this.loadTable()}}},l=r,s=(o("0ce7"),o("2877")),n=Object(s["a"])(l,i,a,!1,null,null,null);t["default"]=n.exports}}]);
\ No newline at end of file
diff --git a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.e60adb43.js b/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.e60adb43.js
deleted file mode 100644
index 4831025f..00000000
--- a/sop-admin/sop-admin-server/src/main/resources/public/static/js/chunk-9b31c83a.e60adb43.js
+++ /dev/null
@@ -1 +0,0 @@
-(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9b31c83a"],{"048e":function(e,t,i){},"0ce7":function(e,t,i){"use strict";var o=i("048e"),a=i.n(o);a.a},"5f67":function(e,t,i){"use strict";i.r(t);var o=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("div",{staticClass:"app-container"},[i("el-container",[i("el-aside",{staticStyle:{"min-height":"300px",width:"250px"}},[i("el-button",{staticStyle:{display:"none"},attrs:{type:"primary",plain:"",size:"mini",icon:"el-icon-plus"},on:{click:function(t){return t.stopPropagation(),e.addService(t)}}},[e._v("\n 新建服务\n ")]),e._v(" "),i("el-input",{staticStyle:{"margin-bottom":"10px","margin-top":"10px"},attrs:{"prefix-icon":"el-icon-search",placeholder:"搜索服务...",size:"mini",clearable:""},model:{value:e.filterText,callback:function(t){e.filterText=t},expression:"filterText"}}),e._v(" "),i("el-tree",{ref:"serviceTree",staticClass:"filter-tree",attrs:{data:e.treeData,props:e.defaultProps,"filter-node-method":e.filterNode,"highlight-current":!0,"expand-on-click-node":!1,"empty-text":"无数据","node-key":"serviceId","default-expand-all":""},on:{"node-click":e.onNodeClick},scopedSlots:e._u([{key:"default",fn:function(t){t.node;var o=t.data;return i("span",{staticClass:"custom-tree-node"},[i("div",[i("el-tooltip",{directives:[{name:"show",rawName:"v-show",value:o.custom,expression:"data.custom"}],staticClass:"item",attrs:{content:"自定义服务",effect:"light",placement:"left"}},[i("i",{staticClass:"el-icon-warning-outline"})]),e._v(" "),o.label.length0?t.join(", "):'未授权'},onRouteDialogSave:function(){var e=this;this.$refs.routeDialogFormRef.validate(function(t){if(t){var i=e.routeDialogFormData.id?"route.update":"route.add";e.routeDialogFormData.serviceId=e.serviceId,e.post(i,e.routeDialogFormData,function(){this.routeDialogVisible=!1,this.loadTable()})}})},onAuthDialogSave:function(){this.post("route.role.update",this.authDialogFormData,function(){this.authDialogVisible=!1,this.loadTable()})},addService:function(){this.addServiceDialogVisible=!0},closeAddServiceDlg:function(){this.$refs.addServiceForm.resetFields()},onAddService:function(){var e=this;this.$refs.addServiceForm.validate(function(t){t&&e.post("service.custom.add",e.addServiceForm,function(e){this.addServiceDialogVisible=!1,this.tip("添加成功"),this.loadTree()})})},onDelService:function(e){var t=e.serviceId;this.confirm("确认要删除服务"+t+"吗,【对应的路由配置会一起删除】",function(e){var i={serviceId:t};this.post("service.custom.del",i,function(){e(),this.tip("删除成功"),this.loadTree()})})},onSizeChange:function(e){this.searchFormData.pageSize=e,this.loadTable()},onPageIndexChange:function(e){this.searchFormData.pageIndex=e,this.loadTable()}}},l=r,s=(i("0ce7"),i("2877")),n=Object(s["a"])(l,o,a,!1,null,null,null);t["default"]=n.exports}}]);
\ No newline at end of file
diff --git a/sop-admin/sop-admin-vue/src/views/service/route.vue b/sop-admin/sop-admin-vue/src/views/service/route.vue
index 5686365e..32d99639 100644
--- a/sop-admin/sop-admin-vue/src/views/service/route.vue
+++ b/sop-admin/sop-admin-vue/src/views/service/route.vue
@@ -66,6 +66,9 @@
授权接口
+
+ 需要token
+
查询
@@ -123,6 +126,16 @@
否
+
+
+ 是
+ 否
+
+
{{ routeDialogFormData.mergeResult === 1 ? '是' : '否' }}
+
+ {{ routeDialogFormData.needToken === 1 ? '是' : '否' }}
+
启用
@@ -269,6 +285,7 @@ export default {
id: '',
serviceId: '',
permission: 0,
+ needToken: 0,
pageIndex: 1,
pageSize: 10
},
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
index a61e9f6a..34ee0bb1 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
@@ -32,12 +32,14 @@ import com.gitee.sop.gatewaycommon.validate.ApiSigner;
import com.gitee.sop.gatewaycommon.validate.ApiValidator;
import com.gitee.sop.gatewaycommon.validate.Encrypter;
import com.gitee.sop.gatewaycommon.validate.Signer;
+import com.gitee.sop.gatewaycommon.validate.TokenValidator;
import com.gitee.sop.gatewaycommon.validate.Validator;
import com.gitee.sop.gatewaycommon.zuul.configuration.ZuulErrorController;
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParamBuilder;
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
import com.netflix.zuul.context.RequestContext;
import lombok.Data;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import java.util.ArrayList;
@@ -152,6 +154,11 @@ public class ApiConfig {
private ParameterFormatter parameterFormatter;
+ /**
+ * 校验token
+ */
+ private TokenValidator tokenValidator = apiParam -> apiParam != null && StringUtils.isNotBlank(apiParam.fetchAccessToken());
+
// -------- fields ---------
/**
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseRouteDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseRouteDefinition.java
deleted file mode 100644
index e46e6671..00000000
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseRouteDefinition.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.gitee.sop.gatewaycommon.bean;
-
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * @author tanghc
- */
-@Getter
-@Setter
-public class BaseRouteDefinition {
-
- /**
- * 路由的Id
- */
- private String id;
- /**
- * 路由规则转发的目标uri
- */
- private String uri;
-
- /**
- * uri后面跟的path
- */
- private String path;
-
- /**
- * 路由执行的顺序
- */
- private int order = 0;
-
- /**
- * 是否忽略验证,业务参数验证除外
- */
- private int ignoreValidate;
-
- /**
- * 是否合并结果
- */
- private int mergeResult;
-
- /**
- * 接口是否需要授权才能访问
- */
- private int permission;
-}
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseServiceRouteInfo.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseServiceRouteInfo.java
deleted file mode 100644
index a66d0f17..00000000
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/BaseServiceRouteInfo.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.gitee.sop.gatewaycommon.bean;
-
-import lombok.Data;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * @author tanghc
- */
-@Data
-public class BaseServiceRouteInfo {
- private String serviceId;
- private List routeDefinitionList = Collections.emptyList();
-
- public String fetchServiceIdLowerCase() {
- return this.serviceId.toLowerCase();
- }
-}
\ No newline at end of file
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java
index 8cfd257d..eb6fba9f 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/RouteDefinition.java
@@ -70,4 +70,9 @@ public class RouteDefinition {
* 是否需要授权才能访问
*/
private int permission;
+
+ /**
+ * 是否需要token
+ */
+ private int needToken;
}
\ No newline at end of file
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
index 1b92d609..7df72e6f 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/configuration/BaseGatewayConfiguration.java
@@ -16,6 +16,8 @@ import com.gitee.sop.gatewaycommon.manager.AbstractConfiguration;
import com.gitee.sop.gatewaycommon.manager.RouteRepositoryContext;
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
import com.gitee.sop.gatewaycommon.param.ParamNames;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -25,13 +27,19 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.codec.ServerCodecConfigurer;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.RequestPredicate;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.Collections;
@@ -41,6 +49,7 @@ import java.util.List;
/**
* @author tanghc
*/
+@Slf4j
public class BaseGatewayConfiguration extends AbstractConfiguration {
public BaseGatewayConfiguration() {
@@ -152,12 +161,20 @@ public class BaseGatewayConfiguration extends AbstractConfiguration {
@Bean
@ConditionalOnProperty(value = "sop.restful.enable", havingValue = "true")
RouterFunction routerFunction() {
- return RouterFunctions.route(RequestPredicates.GET(restPath + "/**"), (serverRequest) -> {
- String url = serverRequest.path();
- int index = url.indexOf(restPath);
+ RequestPredicate requestPredicate = RequestPredicates.all()
+ .and(RequestPredicates.path(restPath + "/**"));
+ return RouterFunctions.route(requestPredicate, (serverRequest) -> {
+ String path = serverRequest.path();
+ int index = path.indexOf(restPath);
// 取/rest的后面部分
- String path = url.substring(index + restPath.length());
- String query = ParamNames.API_NAME + "=" + path + "&" + ParamNames.VERSION_NAME + "=";
+ String servletPath = path.substring(index + restPath.length());
+ String query = serverRequest.uri().getQuery();
+ String appendQuery = ParamNames.API_NAME + "=" + servletPath + "&" + ParamNames.VERSION_NAME + "=";
+ if (StringUtils.isBlank(query)) {
+ query = appendQuery;
+ } else {
+ query += '&' + appendQuery;
+ }
return ServerResponse
.temporaryRedirect(URI.create("/?" + query))
.build();
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java
index 5ff62d11..3590c040 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java
@@ -21,7 +21,12 @@ public enum EnvironmentKeys {
/**
* sop.restful.enable=true,开启传统web开发模式
*/
- SOP_RESTFUL_ENABLE("sop.restful.enable");
+ SOP_RESTFUL_ENABLE("sop.restful.enable"),
+
+ /**
+ * sop.restful.path=/xx ,指定请求前缀,默认/rest
+ */
+ SOP_RESTFUL_PATH("sop.restful.path", "/rest");
private String key;
private String defaultValue;
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
index 7d4abb2b..8bdf8f24 100644
--- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/ApiValidator.java
@@ -83,6 +83,7 @@ public class ApiValidator implements Validator {
checkFormat(param);
checkUploadFile(param);
checkPermission(param);
+ checkToken(param);
}
/**
@@ -238,7 +239,7 @@ public class ApiValidator implements Validator {
/**
* 校验访问权限
*
- * @param apiParam
+ * @param apiParam 参数
*/
protected void checkPermission(ApiParam apiParam) {
String routeId = apiParam.fetchNameVersion();
@@ -254,4 +255,23 @@ public class ApiValidator implements Validator {
}
}
+ /**
+ * 校验token
+ *
+ * @param apiParam 参数
+ */
+ protected void checkToken(ApiParam apiParam) {
+ String routeId = apiParam.fetchNameVersion();
+ TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(routeId);
+ RouteDefinition routeDefinition = targetRoute.getRouteDefinition();
+ boolean needToken = BooleanUtils.toBoolean(routeDefinition.getNeedToken());
+ if (needToken) {
+ TokenValidator tokenValidator = ApiConfig.getInstance().getTokenValidator();
+ boolean rightToken = tokenValidator.validateToken(apiParam);
+ if (!rightToken) {
+ throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
+ }
+ }
+ }
+
}
diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/TokenValidator.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/TokenValidator.java
new file mode 100644
index 00000000..42959fbf
--- /dev/null
+++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/validate/TokenValidator.java
@@ -0,0 +1,11 @@
+package com.gitee.sop.gatewaycommon.validate;
+
+import com.gitee.sop.gatewaycommon.param.ApiParam;
+
+/**
+ * @author tanghc
+ */
+@FunctionalInterface
+public interface TokenValidator {
+ boolean validateToken(ApiParam apiParam);
+}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiAbility.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiAbility.java
index 44dbf4d0..9283a3d6 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiAbility.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiAbility.java
@@ -35,4 +35,9 @@ public @interface ApiAbility {
* 指定接口是否需要授权才能访问,可在admin中进行修改
*/
boolean permission() default false;
+
+ /**
+ * 是否需要appAuthToken,设置为true,网关端会校验token是否存在
+ */
+ boolean needToken() default false;
}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiMapping.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiMapping.java
index a9ba2841..db9b9af8 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiMapping.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/annotation/ApiMapping.java
@@ -25,7 +25,7 @@ public @interface ApiMapping {
/**
* 版本号,默认版本号是""
- * 改默认版本号:ServiceContext.getSopServerConfig().setDefaultVersion("1.0");
+ * 改默认版本号:ServiceConfig.getInstance().setDefaultVersion("1.0");
*/
String version() default "";
@@ -44,6 +44,11 @@ public @interface ApiMapping {
*/
boolean permission() default false;
+ /**
+ * 是否需要appAuthToken,设置为true,网关端会校验token是否存在
+ */
+ boolean needToken() default false;
+
// ------------ 自定义属性 end ------------
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java
index 0795c0ad..b3f2d3d3 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContext.java
@@ -80,12 +80,23 @@ public interface OpenContext extends OpenBeanFactory {
*/
Date getTimestamp();
+ /**
+ * 返回token,即access_token.
+ *
+ * @deprecated 废弃,使用getAppAuthToken()
+ * @return 返回token
+ */
+ @Deprecated
+ String appAuthToken();
+
/**
* 返回token,即access_token
*
* @return 返回token
*/
- String appAuthToken();
+ default String getAppAuthToken() {
+ return appAuthToken();
+ }
/**
* 返回回调地址
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java
index 3171e193..7998d941 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/ServiceApiInfo.java
@@ -28,6 +28,8 @@ public class ServiceApiInfo {
private int mergeResult;
/** 是否需要授权才能访问 */
private int permission;
+ /** 是否需要token */
+ private int needToken;
/** 是否是原始Mapping */
private boolean originalMapping;
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ApiMetaBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ApiMetaBuilder.java
index a7fc4ca7..215db872 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ApiMetaBuilder.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ApiMetaBuilder.java
@@ -87,6 +87,7 @@ public class ApiMetaBuilder {
apiMeta.setIgnoreValidate(BooleanUtils.toInteger(apiMappingInfo.isIgnoreValidate()));
apiMeta.setMergeResult(BooleanUtils.toInteger(apiMappingInfo.isMergeResult()));
apiMeta.setPermission(BooleanUtils.toInteger(apiMappingInfo.isPermission()));
+ apiMeta.setNeedToken(BooleanUtils.toInteger(apiMappingInfo.isNeedToken()));
return apiMeta;
} else {
if (!ServiceContext.getCurrentContext().getBoolean(ServiceContext.RESTFUL_KEY, false)) {
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceRouteInfoBuilder.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceRouteInfoBuilder.java
index b85f32f4..bda9ba59 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceRouteInfoBuilder.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceRouteInfoBuilder.java
@@ -50,8 +50,8 @@ public class ServiceRouteInfoBuilder {
List apis = serviceApiInfo.getApis();
List routeDefinitionList = new ArrayList<>(apis.size());
for (ServiceApiInfo.ApiMeta apiMeta : apis) {
- RouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
- routeDefinitionList.add(gatewayRouteDefinition);
+ RouteDefinition routeDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta);
+ routeDefinitionList.add(routeDefinition);
}
ServiceRouteInfo serviceRouteInfo = new ServiceRouteInfo();
serviceRouteInfo.setServiceId(serviceApiInfo.getServiceId());
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingHandlerMapping.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingHandlerMapping.java
index 9ae43b00..506704b6 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingHandlerMapping.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingHandlerMapping.java
@@ -35,9 +35,10 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
method.setAccessible(true);
String name = null;
String version;
- boolean ignoreValidate = false;
- boolean mergeResult = true;
- boolean permission = false;
+ boolean ignoreValidate;
+ boolean mergeResult;
+ boolean permission;
+ boolean needToken = false;
ApiMapping apiMapping = method.getAnnotation(ApiMapping.class);
if (apiMapping != null) {
name = apiMapping.value()[0];
@@ -45,6 +46,7 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
ignoreValidate = apiMapping.ignoreValidate();
mergeResult = apiMapping.mergeResult();
permission = apiMapping.permission();
+ needToken = apiMapping.needToken();
} else {
ApiAbility apiAbility = this.findApiAbilityAnnotation(method);
if (apiAbility != null) {
@@ -52,6 +54,7 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
ignoreValidate = apiAbility.ignoreValidate();
mergeResult = apiAbility.mergeResult();
permission = apiAbility.permission();
+ needToken = apiAbility.needToken();
} else {
return super.getCustomMethodCondition(method);
}
@@ -75,6 +78,7 @@ public class ApiMappingHandlerMapping extends RequestMappingHandlerMapping imple
apiMappingInfo.setIgnoreValidate(ignoreValidate);
apiMappingInfo.setMergeResult(mergeResult);
apiMappingInfo.setPermission(permission);
+ apiMappingInfo.setNeedToken(needToken);
logger.info("注册接口,name:" + method + ", version:" + version);
return new ApiMappingRequestCondition(apiMappingInfo);
}
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingInfo.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingInfo.java
index f04248ef..cc2dfb3d 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingInfo.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/mapping/ApiMappingInfo.java
@@ -12,6 +12,7 @@ public class ApiMappingInfo {
private boolean ignoreValidate;
private boolean mergeResult;
private boolean permission;
+ private boolean needToken;
public ApiMappingInfo(String name, String version) {
this.name = name;
diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java
index 5cd8a343..265771c0 100644
--- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java
+++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/route/RouteDefinition.java
@@ -68,4 +68,9 @@ public class RouteDefinition {
* 是否需要授权才能访问
*/
private int permission;
+
+ /**
+ * 是否需要token
+ */
+ private int needToken;
}
\ No newline at end of file
diff --git a/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/TokenController.java b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/TokenController.java
new file mode 100644
index 00000000..f0da8211
--- /dev/null
+++ b/sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/TokenController.java
@@ -0,0 +1,28 @@
+package com.gitee.sop.storyweb.controller;
+
+import com.gitee.sop.servercommon.annotation.ApiMapping;
+import com.gitee.sop.servercommon.bean.OpenContext;
+import com.gitee.sop.servercommon.bean.ServiceContext;
+import com.gitee.sop.storyweb.controller.param.StoryParam;
+import com.gitee.sop.storyweb.controller.result.StoryResult;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author tanghc
+ */
+@RestController
+@Slf4j
+@Api(tags = "故事接口")
+public class TokenController {
+
+ @ApiMapping(value = "story.token.get", needToken = true/* 设置true,网关会校验token是否存在 */)
+ public StoryResult token(StoryParam story) {
+ OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
+ String appAuthToken = openContext.getAppAuthToken();
+ StoryResult result = new StoryResult();
+ result.setName("appAuthToken:" + appAuthToken);
+ return result;
+ }
+}
diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java
index d063844b..dbed6fa8 100644
--- a/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java
+++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/entity/ConfigServiceRoute.java
@@ -1,15 +1,13 @@
package com.gitee.sop.gateway.entity;
-import com.gitee.fastmybatis.core.annotation.LogicDelete;
import lombok.Data;
-import java.util.Date;
-
import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
+import java.util.Date;
/**
@@ -63,6 +61,9 @@ public class ConfigServiceRoute {
/** 是否需要授权才能访问, 数据库字段:permission */
private Byte permission;
+ /** 是否需要token, 数据库字段:need_token */
+ private Byte needToken;
+
/** 数据库字段:gmt_create */
private Date gmtCreate;
diff --git a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
index beab446e..5295c89e 100644
--- a/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
+++ b/sop-gateway/src/main/java/com/gitee/sop/gateway/manager/DbRoutesProcessor.java
@@ -54,6 +54,7 @@ public class DbRoutesProcessor implements RoutesProcessor {
configServiceRoute.setStatus((byte) routeDefinition.getStatus());
configServiceRoute.setPermission((byte) routeDefinition.getPermission());
configServiceRoute.setOrder(routeDefinition.getOrder());
+ configServiceRoute.setNeedToken((byte)routeDefinition.getNeedToken());
configServiceRoute.setServiceId(serviceId);
return configServiceRoute;
})
diff --git a/sop-test/src/main/java/com/gitee/sop/test/Client.java b/sop-test/src/main/java/com/gitee/sop/test/Client.java
index a4c9e5a7..398996c7 100644
--- a/sop-test/src/main/java/com/gitee/sop/test/Client.java
+++ b/sop-test/src/main/java/com/gitee/sop/test/Client.java
@@ -138,6 +138,7 @@ public class Client {
private Map header;
private boolean ignoreSign;
private boolean postJson;
+ private String appAuthToken;
private List uploadFileList;
private Callback callback;
@@ -229,6 +230,17 @@ public class Client {
return this;
}
+ /**
+ * 设置token
+ *
+ * @param appAuthToken 给定的token
+ * @return 返回RequestBuilder
+ */
+ public RequestBuilder appAuthToken(String appAuthToken) {
+ this.appAuthToken = appAuthToken;
+ return this;
+ }
+
/**
* 添加文件
*
@@ -310,6 +322,9 @@ public class Client {
if (version != null) {
params.put("version", version);
}
+ if (appAuthToken != null) {
+ params.put("app_auth_token", appAuthToken);
+ }
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
diff --git a/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java b/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
index 375734d6..793725c8 100644
--- a/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
+++ b/sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
@@ -287,6 +287,17 @@ public class AllInOneTest extends TestBase {
System.out.println("成功次数:" + success);
}
+ public void testToken() {
+ Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
+ .method("story.token.get")
+ .version("1.0")
+ .bizContent(new BizContent().add("id", "1").add("name", "葫芦娃"))
+ .appAuthToken("asdfasdfadsf")
+ .httpMethod(HttpTool.HTTPMethod.GET);
+
+ client.execute(requestBuilder);
+ }
+
class BizContent extends HashMap {
public BizContent add(String key, String value) {
this.put(key, value);