feat(app): 实现应用 Token 校验功能
- 服务端: fetchAllConfigs 和 fetchChangedConfigs 添加 Token 校验 - 前端: 应用列表添加 Token 列和生成/删除 Token 按钮 - 客户端: Python/Node.js/Java 客户端传递 token 参数 - Java: 简化 buildTokenParameter 直接传递 token 值
This commit is contained in:
+2
-4
@@ -483,12 +483,10 @@ public class ConfigcenterContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String buildTokenParameter(String url) {
|
public String buildTokenParameter(String url) {
|
||||||
if (token == null || token.length() < 8) {
|
if (token == null || token.isEmpty()) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
String str = md5Short(token).concat(String.valueOf(new Date().getTime()));
|
return url + (url.contains("?") ? "&" : "?") + "token=" + token;
|
||||||
String authtoken = desEncrypt(token.substring(0, 8), str).toLowerCase();
|
|
||||||
return url + (url.contains("?") ? "&" : "?") + "authtoken=" + authtoken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|||||||
+2
-4
@@ -355,12 +355,10 @@ public class ConfigcenterContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String buildTokenParameter(String url) {
|
public String buildTokenParameter(String url) {
|
||||||
if (token == null || token.length() < 8) {
|
if (token == null || token.isEmpty()) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
String str = md5Short(token).concat(String.valueOf(new Date().getTime()));
|
return url + (url.contains("?") ? "&" : "?") + "token=" + token;
|
||||||
String authtoken = desEncrypt(token.substring(0, 8), str).toLowerCase();
|
|
||||||
return url + (url.contains("?") ? "&" : "?") + "authtoken=" + authtoken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export class ConfigClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetchAll() {
|
async fetchAll() {
|
||||||
const url = `${this.config.baseUrl}/api/fetchAllConfigs?appName=${encodeURIComponent(this.config.appName)}&env=${encodeURIComponent(this.config.env)}&version=${encodeURIComponent(this.config.version)}&ignoreGlobal=${this.config.ignoreGlobal}`;
|
const url = `${this.config.baseUrl}/api/fetchAllConfigs?appName=${encodeURIComponent(this.config.appName)}&env=${encodeURIComponent(this.config.env)}&version=${encodeURIComponent(this.config.version)}&ignoreGlobal=${this.config.ignoreGlobal}&token=${encodeURIComponent(this.config.token)}`;
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(url, {
|
||||||
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {},
|
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {},
|
||||||
signal: AbortSignal.timeout(10000),
|
signal: AbortSignal.timeout(10000),
|
||||||
@@ -101,6 +101,7 @@ export class ConfigClient {
|
|||||||
version: this.config.version,
|
version: this.config.version,
|
||||||
lastTime: this.lastTime,
|
lastTime: this.lastTime,
|
||||||
ignoreGlobal: this.config.ignoreGlobal,
|
ignoreGlobal: this.config.ignoreGlobal,
|
||||||
|
token: this.config.token,
|
||||||
};
|
};
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(url, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ class ConfigClient:
|
|||||||
f"&env={self._config.env}"
|
f"&env={self._config.env}"
|
||||||
f"&version={self._config.version}"
|
f"&version={self._config.version}"
|
||||||
f"&ignoreGlobal={str(self._config.ignore_global).lower()}"
|
f"&ignoreGlobal={str(self._config.ignore_global).lower()}"
|
||||||
|
f"&token={self._config.token}"
|
||||||
)
|
)
|
||||||
resp = requests.get(url, timeout=10)
|
resp = requests.get(url, timeout=10)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
@@ -98,6 +99,7 @@ class ConfigClient:
|
|||||||
"version": self._config.version,
|
"version": self._config.version,
|
||||||
"lastTime": self._last_time,
|
"lastTime": self._last_time,
|
||||||
"ignoreGlobal": self._config.ignore_global,
|
"ignoreGlobal": self._config.ignore_global,
|
||||||
|
"token": self._config.token,
|
||||||
}
|
}
|
||||||
resp = requests.post(url, json=payload, timeout=10)
|
resp = requests.post(url, json=payload, timeout=10)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
|
|||||||
env := c.Query("env")
|
env := c.Query("env")
|
||||||
version := c.DefaultQuery("version", "latest")
|
version := c.DefaultQuery("version", "latest")
|
||||||
ignoreGlobal := c.DefaultQuery("ignoreGlobal", "false") == "true"
|
ignoreGlobal := c.DefaultQuery("ignoreGlobal", "false") == "true"
|
||||||
|
token := c.Query("token")
|
||||||
|
|
||||||
if appName == "" || env == "" {
|
if appName == "" || env == "" {
|
||||||
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数缺失"})
|
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数缺失"})
|
||||||
@@ -40,6 +41,12 @@ func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate token if app has token configured
|
||||||
|
if app.Token != "" && app.Token != token {
|
||||||
|
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: "Token 无效"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
result := make(map[string]interface{})
|
result := make(map[string]interface{})
|
||||||
|
|
||||||
// load global configs
|
// load global configs
|
||||||
@@ -101,6 +108,7 @@ type FetchChangedRequest struct {
|
|||||||
LastTime int64 `json:"lastTime" binding:"required"`
|
LastTime int64 `json:"lastTime" binding:"required"`
|
||||||
IgnoreGlobal bool `json:"ignoreGlobal"`
|
IgnoreGlobal bool `json:"ignoreGlobal"`
|
||||||
FetchAll bool `json:"fetchAll"`
|
FetchAll bool `json:"fetchAll"`
|
||||||
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ConfigApiHandler) FetchChangedConfigs(c *gin.Context) {
|
func (h *ConfigApiHandler) FetchChangedConfigs(c *gin.Context) {
|
||||||
@@ -117,6 +125,12 @@ func (h *ConfigApiHandler) FetchChangedConfigs(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate token if app has token configured
|
||||||
|
if app.Token != "" && app.Token != req.Token {
|
||||||
|
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: "Token 无效"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
version := req.Version
|
version := req.Version
|
||||||
if version == "" {
|
if version == "" {
|
||||||
version = "latest"
|
version = "latest"
|
||||||
|
|||||||
@@ -51,3 +51,17 @@ export function getAppOptions() {
|
|||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateToken(id) {
|
||||||
|
return request({
|
||||||
|
url: `/admin/app/generateToken/${id}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteToken(id) {
|
||||||
|
return request({
|
||||||
|
url: `/admin/app/deleteToken/${id}`,
|
||||||
|
method: 'post'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,14 +10,23 @@
|
|||||||
<el-table-column prop="code" label="应用编码"></el-table-column>
|
<el-table-column prop="code" label="应用编码"></el-table-column>
|
||||||
<el-table-column prop="groupName" label="所属业务组" width="120"></el-table-column>
|
<el-table-column prop="groupName" label="所属业务组" width="120"></el-table-column>
|
||||||
<el-table-column prop="ownerName" label="负责人" width="100"></el-table-column>
|
<el-table-column prop="ownerName" label="负责人" width="100"></el-table-column>
|
||||||
|
<el-table-column prop="token" label="Token" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.token" style="color: #67C23A">已设置</span>
|
||||||
|
<span v-else style="color: #909399">未设置</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="enabled" label="状态" width="100">
|
<el-table-column prop="enabled" label="状态" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-switch v-model="scope.row.enabled" @change="handleSwitch(scope.row)"></el-switch>
|
<el-switch v-model="scope.row.enabled" @change="handleSwitch(scope.row)"></el-switch>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="150">
|
<el-table-column label="操作" width="200">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" @click="showEdit(scope.row)">编辑</el-button>
|
<el-button type="text" @click="showEdit(scope.row)">编辑</el-button>
|
||||||
|
<el-button type="text" @click="handleToken(scope.row)">
|
||||||
|
{{ scope.row.token ? '删除Token' : '生成Token' }}
|
||||||
|
</el-button>
|
||||||
<el-button type="text" style="color: #F56C6C" @click="handleDelete(scope.row)">删除</el-button>
|
<el-button type="text" style="color: #F56C6C" @click="handleDelete(scope.row)">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -62,7 +71,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { getAppList, addApp, updateApp, deleteApp } from '../../api/app'
|
import { getAppList, addApp, updateApp, deleteApp, generateToken, deleteToken } from '../../api/app'
|
||||||
import request from '../../api/request'
|
import request from '../../api/request'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -148,6 +157,23 @@ export default {
|
|||||||
row.enabled = !row.enabled
|
row.enabled = !row.enabled
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async handleToken(row) {
|
||||||
|
if (row.token) {
|
||||||
|
try {
|
||||||
|
await this.$confirm('确定删除此应用的 Token?删除后客户端将无法通过 Token 校验。')
|
||||||
|
await deleteToken(row.id)
|
||||||
|
this.$message.success('Token 已删除')
|
||||||
|
this.loadData()
|
||||||
|
} catch (e) {}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
await this.$confirm('确定为此应用生成 Token?生成后客户端需要配置此 Token 才能拉取配置。')
|
||||||
|
const res = await generateToken(row.id)
|
||||||
|
this.$message.success('Token 已生成: ' + res.data)
|
||||||
|
this.loadData()
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
},
|
||||||
handlePageChange(page) {
|
handlePageChange(page) {
|
||||||
this.query.pageNo = page
|
this.query.pageNo = page
|
||||||
this.loadData()
|
this.loadData()
|
||||||
|
|||||||
Reference in New Issue
Block a user