Jelajahi Sumber

feat: 开放接口增加生成签名,方便用于测试

许俊杰 2 tahun lalu
induk
melakukan
067bd57f2c

+ 18 - 6
Admin.NET/Admin.NET.Core/Service/OpenAccess/Dto/OpenAccessInput.cs

@@ -48,18 +48,18 @@ public class DeleteOpenAccessInput : BaseIdInput
 
 public class GenerateSignatureInput
 {
-    /// <summary>
-    /// 密钥
-    /// </summary>
-    [Required(ErrorMessage = "密钥不能为空")]
-    public string AppSecret { get; set; }
-
     /// <summary>
     /// 身份标识
     /// </summary>
     [Required(ErrorMessage = "身份标识不能为空")]
     public string AccessKey { get; set; }
 
+    /// <summary>
+    /// 密钥
+    /// </summary>
+    [Required(ErrorMessage = "密钥不能为空")]
+    public string AccessSecret { get; set; }
+
     /// <summary>
     /// 请求方法
     /// </summary>
@@ -70,4 +70,16 @@ public class GenerateSignatureInput
     /// </summary>
     [Required(ErrorMessage = "请求接口地址不能为空")]
     public string Url { get; set; }
+    
+    /// <summary>
+    /// 时间戳
+    /// </summary>
+    [Required(ErrorMessage = "时间戳不能为空")]
+    public long Timestamp { get; set; }
+    
+    /// <summary>
+    /// 随机数
+    /// </summary>
+    [Required(ErrorMessage = "随机数不能为空")]
+    public string Nonce { get; set; }
 }

+ 7 - 13
Admin.NET/Admin.NET.Core/Service/OpenAccess/SysOpenAccessService.cs

@@ -29,24 +29,18 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
     }
 
     /// <summary>
-    /// 获取生成签名
+    /// 生成签名
     /// </summary>
     /// <param name="input"></param>
     /// <returns></returns>
-    [DisplayName("获取生成签名")]
-    public string GetGenerateSignature([FromQuery] GenerateSignatureInput input)
+    [DisplayName("生成签名")]
+    public string GenerateSignature(GenerateSignatureInput input)
     {
         // 密钥
-        var appSecretByte = Encoding.UTF8.GetBytes(input.AppSecret);
-        // 时间戳,精确到秒
-        DateTimeOffset currentTime = DateTimeOffset.UtcNow;
-        var timestamp = currentTime.ToUnixTimeSeconds();
-        // 唯一随机数,可以使用guid或者雪花id,以下是sqlsugar提供获取雪花id的方法
-        var nonce = YitIdHelper.NextId();
-        //// 请求方式
-        //var sMethod = method.ToString();
+        var appSecretByte = Encoding.UTF8.GetBytes(input.AccessSecret);
+
         // 拼接参数
-        var parameter = $"{input.Method}&{input.Url}&{input.AccessKey}&{timestamp}&{nonce}";
+        var parameter = $"{input.Method.ToString().ToUpper()}&{input.Url}&{input.AccessKey}&{input.Timestamp}&{input.Nonce}";
         // 使用 HMAC-SHA256 协议创建基于哈希的消息身份验证代码 (HMAC),以appSecretByte 作为密钥,对上面拼接的参数进行计算签名,所得签名进行 Base-64 编码
         using HMAC hmac = new HMACSHA256();
         hmac.Key = appSecretByte;
@@ -173,7 +167,7 @@ public class SysOpenAccessService : IDynamicApiController, ITransient
                 }
                 catch (Exception ex)
                 {
-                    logger.LogError(ex, "开接口身份验证");
+                    logger.LogError(ex, "开接口身份验证");
                     return Task.FromResult("");
                 }
             },

+ 20 - 56
Web/src/api-services/apis/sys-open-access-api.ts

@@ -21,7 +21,7 @@ import { AddOpenAccessInput } from '../models';
 import { AdminResultSqlSugarPagedListOpenAccessOutput } from '../models';
 import { AdminResultString } from '../models';
 import { DeleteOpenAccessInput } from '../models';
-import { HttpMethodEnum } from '../models';
+import { GenerateSignatureInput } from '../models';
 import { OpenAccessInput } from '../models';
 import { UpdateOpenAccessInput } from '../models';
 /**
@@ -128,27 +128,12 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
         },
         /**
          * 
-         * @summary 获取生成的签名
-         * @param {string} appSecret 密钥
-         * @param {string} accessKey 身份标识
-         * @param {string} url 请求接口地址
-         * @param {HttpMethodEnum} [method] 请求方法
+         * @summary 生成签名
+         * @param {GenerateSignatureInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        apiSysOpenAccessGenerateSignatureGet: async (appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
-            // verify required parameter 'appSecret' is not null or undefined
-            if (appSecret === null || appSecret === undefined) {
-                throw new RequiredError('appSecret','Required parameter appSecret was null or undefined when calling apiSysOpenAccessGenerateSignatureGet.');
-            }
-            // verify required parameter 'accessKey' is not null or undefined
-            if (accessKey === null || accessKey === undefined) {
-                throw new RequiredError('accessKey','Required parameter accessKey was null or undefined when calling apiSysOpenAccessGenerateSignatureGet.');
-            }
-            // verify required parameter 'url' is not null or undefined
-            if (url === null || url === undefined) {
-                throw new RequiredError('url','Required parameter url was null or undefined when calling apiSysOpenAccessGenerateSignatureGet.');
-            }
+        apiSysOpenAccessGenerateSignaturePost: async (body?: GenerateSignatureInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/api/sysOpenAccess/generateSignature`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, 'https://example.com');
@@ -156,7 +141,7 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
             if (configuration) {
                 baseOptions = configuration.baseOptions;
             }
-            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
             const localVarHeaderParameter = {} as any;
             const localVarQueryParameter = {} as any;
 
@@ -169,21 +154,7 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
                 localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
             }
 
-            if (appSecret !== undefined) {
-                localVarQueryParameter['AppSecret'] = appSecret;
-            }
-
-            if (accessKey !== undefined) {
-                localVarQueryParameter['AccessKey'] = accessKey;
-            }
-
-            if (method !== undefined) {
-                localVarQueryParameter['Method'] = method;
-            }
-
-            if (url !== undefined) {
-                localVarQueryParameter['Url'] = url;
-            }
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
 
             const query = new URLSearchParams(localVarUrlObj.search);
             for (const key in localVarQueryParameter) {
@@ -195,6 +166,8 @@ export const SysOpenAccessApiAxiosParamCreator = function (configuration?: Confi
             localVarUrlObj.search = (new URLSearchParams(query)).toString();
             let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
             localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
 
             return {
                 url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
@@ -379,16 +352,13 @@ export const SysOpenAccessApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
-         * @summary 获取生成的签名
-         * @param {string} appSecret 密钥
-         * @param {string} accessKey 身份标识
-         * @param {string} url 请求接口地址
-         * @param {HttpMethodEnum} [method] 请求方法
+         * @summary 生成签名
+         * @param {GenerateSignatureInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysOpenAccessGenerateSignatureGet(appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultString>>> {
-            const localVarAxiosArgs = await SysOpenAccessApiAxiosParamCreator(configuration).apiSysOpenAccessGenerateSignatureGet(appSecret, accessKey, url, method, options);
+        async apiSysOpenAccessGenerateSignaturePost(body?: GenerateSignatureInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultString>>> {
+            const localVarAxiosArgs = await SysOpenAccessApiAxiosParamCreator(configuration).apiSysOpenAccessGenerateSignaturePost(body, options);
             return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
                 const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
                 return axios.request(axiosRequestArgs);
@@ -466,16 +436,13 @@ export const SysOpenAccessApiFactory = function (configuration?: Configuration,
         },
         /**
          * 
-         * @summary 获取生成的签名
-         * @param {string} appSecret 密钥
-         * @param {string} accessKey 身份标识
-         * @param {string} url 请求接口地址
-         * @param {HttpMethodEnum} [method] 请求方法
+         * @summary 生成签名
+         * @param {GenerateSignatureInput} [body] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async apiSysOpenAccessGenerateSignatureGet(appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultString>> {
-            return SysOpenAccessApiFp(configuration).apiSysOpenAccessGenerateSignatureGet(appSecret, accessKey, url, method, options).then((request) => request(axios, basePath));
+        async apiSysOpenAccessGenerateSignaturePost(body?: GenerateSignatureInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultString>> {
+            return SysOpenAccessApiFp(configuration).apiSysOpenAccessGenerateSignaturePost(body, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -540,17 +507,14 @@ export class SysOpenAccessApi extends BaseAPI {
     }
     /**
      * 
-     * @summary 获取生成的签名
-     * @param {string} appSecret 密钥
-     * @param {string} accessKey 身份标识
-     * @param {string} url 请求接口地址
-     * @param {HttpMethodEnum} [method] 请求方法
+     * @summary 生成签名
+     * @param {GenerateSignatureInput} [body] 
      * @param {*} [options] Override http request option.
      * @throws {RequiredError}
      * @memberof SysOpenAccessApi
      */
-    public async apiSysOpenAccessGenerateSignatureGet(appSecret: string, accessKey: string, url: string, method?: HttpMethodEnum, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultString>> {
-        return SysOpenAccessApiFp(this.configuration).apiSysOpenAccessGenerateSignatureGet(appSecret, accessKey, url, method, options).then((request) => request(this.axios, this.basePath));
+    public async apiSysOpenAccessGenerateSignaturePost(body?: GenerateSignatureInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultString>> {
+        return SysOpenAccessApiFp(this.configuration).apiSysOpenAccessGenerateSignaturePost(body, options).then((request) => request(this.axios, this.basePath));
     }
     /**
      * 

+ 69 - 0
Web/src/api-services/models/generate-signature-input.ts

@@ -0,0 +1,69 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET 通用权限开发平台
+ * 让 .NET 开发更简单、更通用、更流行。整合最新技术,模块插件式开发,前后端分离,开箱即用。<br/><u><b><font color='FF0000'> 👮不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!</font></b></u>
+ *
+ * OpenAPI spec version: 1.0.0
+ * 
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+
+import { HttpMethodEnum } from './http-method-enum';
+ /**
+ * 
+ *
+ * @export
+ * @interface GenerateSignatureInput
+ */
+export interface GenerateSignatureInput {
+
+    /**
+     * 身份标识
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    accessKey: string;
+
+    /**
+     * 密钥
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    accessSecret: string;
+
+    /**
+     * @type {HttpMethodEnum}
+     * @memberof GenerateSignatureInput
+     */
+    method?: HttpMethodEnum;
+
+    /**
+     * 请求接口地址
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    url: string;
+
+    /**
+     * 时间戳
+     *
+     * @type {number}
+     * @memberof GenerateSignatureInput
+     */
+    timestamp?: number;
+
+    /**
+     * 随机数
+     *
+     * @type {string}
+     * @memberof GenerateSignatureInput
+     */
+    nonce?: string | null;
+}

+ 1 - 0
Web/src/api-services/models/index.ts

@@ -144,6 +144,7 @@ export * from './enum-type-output';
 export * from './file-input';
 export * from './gen-auth-url-input';
 export * from './gender-enum';
+export * from './generate-signature-input';
 export * from './http-method-enum';
 export * from './iaction-result';
 export * from './jtoken';

+ 153 - 0
Web/src/views/system/openAccess/component/generateSign.vue

@@ -0,0 +1,153 @@
+<template>
+	<div class="sys-open-access-container">
+		<el-dialog v-model="state.isShowDialog" draggable :close-on-click-modal="false" width="600px">
+			<template #header>
+				<div style="color: #fff">
+					<el-icon size="16" style="margin-right: 3px; display: inline; vertical-align: middle"> <ele-Key /> </el-icon>
+					<span> 生成签名 </span>
+				</div>
+			</template>
+			<el-form :model="state.ruleForm" ref="ruleFormRef" label-width="auto">
+				<el-row :gutter="35">
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="身份标识" prop="accessKey">
+							<el-input v-model="state.ruleForm.accessKey" placeholder="身份标识" readonly />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="密钥" prop="accessSecret">
+							<el-input v-model="state.ruleForm.accessSecret" placeholder="密钥" readonly> </el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="接口请求地址" prop="url">
+							<el-input v-model="state.ruleForm.url" placeholder="接口请求地址" class="input-with-select" clearable>
+								<template #prepend>
+									<el-select v-model="state.ruleForm.method" placeholder="请求方法" style="width: 100px">
+										<el-option label="Get" :value="HttpMethodEnum.NUMBER_0" />
+										<el-option label="Post" :value="HttpMethodEnum.NUMBER_1" />
+										<el-option label="Put" :value="HttpMethodEnum.NUMBER_2" />
+										<el-option label="Delete" :value="HttpMethodEnum.NUMBER_3" />
+										<el-option label="Patch" :value="HttpMethodEnum.NUMBER_4" />
+										<el-option label="Head" :value="HttpMethodEnum.NUMBER_5" />
+										<el-option label="Options" :value="HttpMethodEnum.NUMBER_6" />
+										<el-option label="Trace" :value="HttpMethodEnum.NUMBER_7" />
+										<el-option label="Connect" :value="HttpMethodEnum.NUMBER_8" />
+									</el-select>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="时间戳" prop="timestamp">
+							<el-input v-model="state.ruleForm.timestamp" placeholder="输入或获取时间戳" clearable>
+								<template #append>
+									<el-button @click="getTimeStamp">获取</el-button>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="随机数" prop="nonce">
+							<el-input v-model="state.ruleForm.nonce" placeholder="输入或获取随机数" clearable>
+								<template #append>
+									<el-button @click="getNonce">获取</el-button>
+								</template>
+							</el-input>
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="签名" prop="sign">
+							<el-input v-model="state.sign" placeholder="填写信息后自动生成" readonly> </el-input>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts" setup name="sysOpenAccessEdit">
+import { reactive, ref, watch } from 'vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysOpenAccessApi } from '/@/api-services/api';
+import { GenerateSignatureInput, HttpMethodEnum } from '/@/api-services/models';
+
+const props = defineProps({
+	title: String,
+});
+const emits = defineEmits(['handleQuery']);
+const ruleFormRef = ref();
+const state = reactive({
+	isShowDialog: false,
+	ruleForm: {} as GenerateSignatureInput,
+	sign: '', // 生成的签名
+});
+
+watch([() => state.ruleForm.method, () => state.ruleForm.url, () => state.ruleForm.timestamp, () => state.ruleForm.nonce], () => {
+	if (
+		state.ruleForm.method == undefined ||
+		state.ruleForm.method == null ||
+		!state.ruleForm.url ||
+		!state.ruleForm.timestamp ||
+		!state.ruleForm.nonce ||
+		/^\d+$/.test(state.ruleForm.timestamp as unknown as string) == false // 时间戳必须为数字
+	) {
+		state.sign = '';
+		return;
+	}
+
+	generateSign();
+});
+
+// 打开弹窗
+const openDialog = (row: any) => {
+	state.ruleForm = {
+		accessKey: row?.accessKey,
+		accessSecret: row?.accessSecret,
+		method: HttpMethodEnum.NUMBER_0,
+		url: '',
+	};
+	state.isShowDialog = true;
+	ruleFormRef.value?.resetFields();
+};
+
+/** 生成密钥 */
+const createSecret = async () => {
+	var res = await getAPI(SysOpenAccessApi).apiSysOpenAccessSecretPost();
+	state.ruleForm.accessSecret = res.data.result!;
+};
+
+/** 获取当前时间戳(精确到秒) */
+const getTimeStamp = () => {
+	const timestamp = Math.floor(Date.now() / 1000);
+	state.ruleForm.timestamp = timestamp;
+};
+
+/** 获取随机数 */
+const getNonce = () => {
+	var nonce = '';
+	for (var i = 0; i < 6; i++) {
+		nonce += Math.floor(Math.random() * 10);
+	}
+	state.ruleForm.nonce = nonce;
+};
+
+/** 生成签名 */
+const generateSign = async () => {
+	var res = await getAPI(SysOpenAccessApi).apiSysOpenAccessGenerateSignaturePost(state.ruleForm);
+	state.sign = res.data.result!;
+};
+
+// 导出对象
+defineExpose({ openDialog });
+</script>
+
+<style lang="scss" scoped>
+:deep(.input-with-select) {
+	.el-input-group__prepend {
+		background-color: var(--el-fill-color-blank);
+	}
+}
+</style>

+ 0 - 5
Web/src/views/system/openAccess/component/helpView.vue

@@ -7,11 +7,6 @@
 					<span> 说明 </span>
 				</div>
 			</template>
-			<!-- <template #footer>
-				<span class="dialog-footer">
-					<el-button @click="close">关 闭</el-button>
-				</span>
-			</template> -->
 			<div class="text-content">
 				<h2>OpenAPI 使用</h2>
 				<ul>

+ 9 - 0
Web/src/views/system/openAccess/index.vue

@@ -34,6 +34,7 @@
 					<template #default="scope">
 						<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditOpenAccess(scope.row)" v-auth="'sysOpenAccess:update'" :disabled="scope.row.status === 1"> 编辑 </el-button>
 						<el-button icon="ele-Delete" size="small" text type="danger" @click="delOpenAccess(scope.row)" v-auth="'sysOpenAccess:delete'" :disabled="scope.row.status === 1"> 删除 </el-button>
+						<el-button size="small" text @click="openGenerateSign(scope.row)"> 生成签名 </el-button>
 					</template>
 				</el-table-column>
 			</el-table>
@@ -52,6 +53,7 @@
 
 		<EditOpenAccess ref="editOpenAccessRef" :title="state.editOpenAccessTitle" @handleQuery="handleQuery" />
 		<HelpView ref="helpViewRef" />
+		<GenerateSign ref="generateSignRef" />
 	</div>
 </template>
 
@@ -61,6 +63,7 @@ import { ElMessageBox, ElMessage } from 'element-plus';
 import EditOpenAccess from '/@/views/system/openAccess/component/editOpenAccess.vue';
 import HelpView from '/@/views/system/openAccess/component/helpView.vue';
 import ModifyRecord from '/@/components/table/modifyRecord.vue';
+import GenerateSign from '/@/views/system/openAccess/component/generateSign.vue';
 
 import { getAPI } from '/@/utils/axios-utils';
 import { SysOpenAccessApi } from '/@/api-services/api';
@@ -68,6 +71,7 @@ import { OpenAccessOutput } from '/@/api-services/models';
 
 const editOpenAccessRef = ref<InstanceType<typeof EditOpenAccess>>();
 const helpViewRef = ref<InstanceType<typeof HelpView>>();
+const generateSignRef = ref<InstanceType<typeof GenerateSign>>();
 const state = reactive({
 	loading: false,
 	openAccessData: [] as Array<OpenAccessOutput>,
@@ -145,4 +149,9 @@ const handleCurrentChange = (val: number) => {
 const openHelp = () => {
 	helpViewRef.value?.openDialog();
 };
+
+// 打开生成签名
+const openGenerateSign = (row: any) => {
+	generateSignRef.value?.openDialog(row);
+};
 </script>