openapi全量功能

pull/7564/head
EightMonth 2024-12-20 17:53:45 +08:00
parent 00252f392f
commit f7f6d0ff88
34 changed files with 1636 additions and 4 deletions

View File

@ -180,6 +180,8 @@ public class ShiroConfig {
// 企业微信证书排除
filterChainDefinitionMap.put("/WW_verify*", "anon");
filterChainDefinitionMap.put("/openapi/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】

View File

@ -0,0 +1,112 @@
package org.jeecg.modules.openapi.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.generator.AKSKGenerator;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @date 2024/12/10 9:54
*/
@RestController
@RequestMapping("/openapi/auth")
public class OpenApiAuthController extends JeecgController<OpenApiAuth, OpenApiAuthService> {
/**
*
*
* @param openApiAuth
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@GetMapping(value = "/list")
public Result<?> queryPageList(OpenApiAuth openApiAuth, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<OpenApiAuth> queryWrapper = QueryGenerator.initQueryWrapper(openApiAuth, req.getParameterMap());
Page<OpenApiAuth> page = new Page<>(pageNo, pageSize);
IPage<OpenApiAuth> pageList = service.page(page, queryWrapper);
return Result.ok(pageList);
}
/**
*
*
* @param openApiAuth
* @return
*/
@PostMapping(value = "/add")
public Result<?> add(@RequestBody OpenApiAuth openApiAuth) {
service.save(openApiAuth);
return Result.ok("添加成功!");
}
/**
*
*
* @param openApiAuth
* @return
*/
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody OpenApiAuth openApiAuth) {
service.updateById(openApiAuth);
return Result.ok("修改成功!");
}
/**
* id
*
* @param id
* @return
*/
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
service.removeById(id);
return Result.ok("删除成功!");
}
/**
*
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.service.removeByIds(Arrays.asList(ids.split(",")));
return Result.ok("批量删除成功!");
}
/**
* id
*
* @param id
* @return
*/
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
OpenApiAuth openApiAuth = service.getById(id);
return Result.ok(openApiAuth);
}
/**
* AKSK
* @return
*/
@GetMapping("genAKSK")
public Result<String[]> genAKSK() {
return Result.ok(AKSKGenerator.genAKSKPair());
}
}

View File

@ -0,0 +1,463 @@
package org.jeecg.modules.openapi.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeBindings;
import com.fasterxml.classmate.TypeResolver;
import com.fasterxml.classmate.types.ResolvedObjectType;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.google.common.collect.Lists;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
import org.jeecg.modules.openapi.entity.OpenApiParam;
import org.jeecg.modules.openapi.generator.PathGenerator;
import org.jeecg.modules.openapi.service.OpenApiHeaderService;
import org.jeecg.modules.openapi.service.OpenApiParamService;
import org.jeecg.modules.openapi.service.OpenApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import springfox.documentation.schema.*;
import springfox.documentation.service.*;
import springfox.documentation.spring.web.DocumentationCache;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* @date 2024/12/10 9:11
*/
@RestController
@RequestMapping("/openapi")
public class OpenApiController extends JeecgController<OpenApi, OpenApiService> {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OpenApiParamService openApiParamService;
@Autowired
private OpenApiHeaderService openApiHeaderService;
@Autowired
private DocumentationCache documentationCache;
@Autowired
private TypeResolver typeResolver;
/**
*
*
* @param openApi
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@GetMapping(value = "/list")
public Result<?> queryPageList(OpenApi openApi, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<OpenApi> queryWrapper = QueryGenerator.initQueryWrapper(openApi, req.getParameterMap());
Page<OpenApi> page = new Page<>(pageNo, pageSize);
IPage<OpenApi> pageList = service.page(page, queryWrapper);
for (OpenApi api : pageList.getRecords()) {
api.setParams(openApiParamService.findByApiId(api.getId()));
api.setHeaders(openApiHeaderService.findByApiId(api.getId()));
}
return Result.ok(pageList);
}
/**
*
*
* @param openApi
* @return
*/
@PostMapping(value = "/add")
public Result<?> add(@RequestBody OpenApi openApi) {
if (service.save(openApi)) {
if (!CollectionUtils.isEmpty(openApi.getHeaders())) {
openApiHeaderService.saveBatch(openApi.getHeaders());
}
if (!CollectionUtils.isEmpty(openApi.getParams())) {
openApiParamService.saveBatch(openApi.getParams());
}
}
return Result.ok("添加成功!");
}
/**
*
*
* @param openApi
* @return
*/
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody OpenApi openApi) {
if (service.updateById(openApi)) {
openApiHeaderService.deleteByApiId(openApi.getId());
openApiParamService.deleteByApiId(openApi.getId());
if (!CollectionUtils.isEmpty(openApi.getHeaders())) {
openApiHeaderService.saveBatch(openApi.getHeaders());
}
if (!CollectionUtils.isEmpty(openApi.getParams())) {
openApiParamService.saveBatch(openApi.getParams());
}
}
return Result.ok("修改成功!");
}
/**
* id
*
* @param id
* @return
*/
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
service.removeById(id);
return Result.ok("删除成功!");
}
/**
*
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.service.removeByIds(Arrays.asList(ids.split(",")));
return Result.ok("批量删除成功!");
}
/**
* id
*
* @param id
* @return
*/
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
OpenApi OpenApi = service.getById(id);
return Result.ok(OpenApi);
}
/**
*
* @param path
* @return
*/
@RequestMapping(value = "/call/{path}", method = {RequestMethod.GET,RequestMethod.POST})
public Result<?> call(@PathVariable String path, @RequestBody(required = false) String json, HttpServletRequest request) {
OpenApi openApi = service.findByPath(path);
if (Objects.isNull(openApi)) {
Map<String, Object> result = new HashMap<>();
result.put("code", 404);
result.put("data", null);
return Result.error("失败", result);
}
List<OpenApiHeader> headers = openApiHeaderService.findByApiId(openApi.getId());
String url = openApi.getOriginUrl();
String method = openApi.getRequestMethod();
HttpHeaders httpHeaders = new HttpHeaders();
for (OpenApiHeader header : headers) {
httpHeaders.put(header.getKey(), Lists.newArrayList(request.getHeader(header.getKey())));
}
HttpEntity<String> httpEntity = new HttpEntity<>(json, httpHeaders);
return restTemplate.exchange(url, HttpMethod.resolve(method), httpEntity, Result.class, request.getParameterMap()).getBody();
}
@GetMapping("/call/add")
public void addSwagger() {
Documentation documentation = documentationCache.documentationByGroup("default");
List<ApiListing> apis = new ArrayList<>();
Set<String> produces = new HashSet<>();
produces.add("application/json");
Set<String> consumes = new HashSet<>();
consumes.add("application/json");
Set<String> tags = new HashSet<>();
Tag tag = new Tag("openapi", "openapi");
tags.add("openapi");
Set<Tag> apiTags = new HashSet<>();
apiTags.add(tag);
documentation.getTags().add(tag);
Model resultModel = documentation.getApiListings().get("login-controller").get(0).getModels().get("接口返回对象«JSONObject»");
ModelRef stringModelRef = new ModelRef("string");
ResolvedType stringResolvedType = typeResolver.resolve(String.class);
HashMap<String, ModelProperty> propertyMap = new HashMap<>();
ModelProperty modelProperty = new ModelProperty("abc", stringResolvedType, "java.lang.String", 0, true, false, false, false, "姓名", null, null, null, null, null, new ArrayList<>());
propertyMap.put("abc", modelProperty);
ModelProperty modelProperty1 = new ModelProperty("bcd", stringResolvedType, "java.lang.String", 0, true, false, false, false, "姓名", null, null, null, null, null, new ArrayList<>());
propertyMap.put("bcd", modelProperty1);
ResolvedType mapResolvedType = typeResolver.resolve(HashMap.class);
List<ModelReference> subTypes = new ArrayList<>();
subTypes.add(stringModelRef);
Model bodyModel = new Model("path_body", "body", mapResolvedType, "java.util.HashMap", propertyMap, "请求体结构", "", "", subTypes, null, null);
ModelRef bodyRef = new ModelRef("bodyModel", "bodyModel", null, null, "path_body");
Set<ResponseMessage> responseMessages = documentation.getApiListings().get("login-controller").get(0).getApis().get(2).getOperations().get(0).getResponseMessages();
List<Parameter> parameters = new ArrayList<>();
// header-->请求参数的获取:@RequestHeader()
// query-->请求参数的获取:@RequestParam()
// path-->请求参数的获取:@PathVariable()
// body-->请求参数的获取:@RequestBody()
// form不常用
Parameter body = new Parameter("body",
"请求体",
"",
true,
false,
true,
bodyRef,
Optional.of(mapResolvedType),
null,
"body",
null,
false,
null,
null,
2147483647,
null,
new HashMap<>(),
new ArrayList<>());
parameters.add(body);
Parameter parameter = new Parameter("name",
"姓名",
"张三",
false,
false,
true,
stringModelRef,
Optional.empty(),
null,
"query",
null,
false,
null,
null,
2147483647,
null,
new HashMap<>(),
new ArrayList<>());
parameters.add(parameter);
ModelRef modelRef = new ModelRef(resultModel.getType().getTypeName(),
resultModel.getQualifiedType(),
null,
null,
false,
resultModel.getId());
Operation operation = new Operation(HttpMethod.resolve("GET"),
"模拟第一个openapi接口",
"模拟第一个openapi接口",
modelRef,
"abcUsingGET",
0, tags, produces, consumes,
new LinkedHashSet<>(),
new ArrayList<>(),
parameters,
responseMessages,
"false",
false,
new ArrayList<>());
List<Operation> operations = new ArrayList<>();
operations.add(operation);
ApiDescription apiDescription = new ApiDescription("openapi",
"/jeecg-boot/openapi/call/abc", "openapi", operations, false);
List<ApiDescription> apiList = new ArrayList<>();
apiList.add(apiDescription);
Map<String, Model> responseModel = new HashMap<>();
responseModel.put("接口返回对象«JSONObject»", resultModel);
responseModel.put("bodyModel", bodyModel);
ApiListing api = new ApiListing("1.0",
"/",
"/openapi/call/abc",
produces, consumes,
"", new HashSet<>(), new ArrayList<>(), apiList, responseModel, "abc", 0, apiTags);
apis.add(api);
documentation.getApiListings().put("openapi", apis);
}
/**
*
* @return
*/
@GetMapping("genPath")
public Result<String> genPath() {
Result<String> r = new Result<String>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(PathGenerator.genPath());
return r;
}
public void resetOpenapiSwagger() {
List<OpenApi> openapis = service.list();
Documentation documentation = documentationCache.documentationByGroup("default");
List<ApiListing> apis = new ArrayList<>();
// --------------------- swagger common --------------
// http参数
Set<String> produces = new HashSet<>();
produces.add("application/json");
Set<String> consumes = new HashSet<>();
consumes.add("application/json");
// 标题栏
Set<String> tags = new HashSet<>();
Tag tag = new Tag("openapi", "openapi");
tags.add("openapi");
Set<Tag> apiTags = new HashSet<>();
apiTags.add(tag);
documentation.getTags().add(tag);
ModelRef stringModelRef = new ModelRef("string", null, false);
// 响应文档
Model resultModel = documentation.getApiListings().get("login-controller").get(0).getModels().get("接口返回对象«JSONObject»");
Set<ResponseMessage> responseMessages = documentation.getApiListings().get("login-controller").get(0).getApis().get(2).getOperations().get(0).getResponseMessages();
ModelRef modelRef = new ModelRef(resultModel.getType().getTypeName(),
resultModel.getQualifiedType(),
null,
null,
false,
resultModel.getId());
// --------------------- swagger common --------------
for (OpenApi openapi : openapis) {
openapi.setParams(openApiParamService.findByApiId(openapi.getId()));
openapi.setHeaders(openApiHeaderService.findByApiId(openapi.getId()));
// 参数包含header\query\path\body\form 五类数据
List<Parameter> parameters = new ArrayList<>();
// header-->请求参数的获取:@RequestHeader()
// query-->请求参数的获取:@RequestParam()
// path-->请求参数的获取:@PathVariable()
// body-->请求参数的获取:@RequestBody()
// form不常用
for (OpenApiHeader openApiHeader : openapi.getHeaders()) {
Parameter parameter = new Parameter(openApiHeader.getKey(),
openApiHeader.getNote(),
openApiHeader.getDefaultValue(),
openApiHeader.getIsBlank() == 1,
false,
true,
stringModelRef,
Optional.empty(),
null,
"header",
null,
false,
null,
null,
2147483647,
null,
new HashMap<>(),
new ArrayList<>());
parameters.add(parameter);
}
for (OpenApiParam openApiParam : openapi.getParams()) {
Parameter parameter = new Parameter(openApiParam.getKey(),
openApiParam.getNote(),
openApiParam.getDefaultValue(),
openApiParam.getIsBlank() == 1,
false,
true,
stringModelRef,
Optional.empty(),
null,
"query",
null,
false,
null,
null,
2147483647,
null,
new HashMap<>(),
new ArrayList<>());
parameters.add(parameter);
}
Operation operation = new Operation(HttpMethod.resolve("GET"),
"模拟第一个openapi接口",
"模拟第一个openapi接口",
modelRef,
"abcUsingGET",
0, tags, produces, consumes,
new LinkedHashSet<>(),
new ArrayList<>(),
parameters,
responseMessages,
"false",
false,
new ArrayList<>());
List<Operation> operations = new ArrayList<>();
operations.add(operation);
ApiDescription apiDescription = new ApiDescription("openapi",
"/jeecg-boot/openapi/call/abc", "openapi", operations, false);
List<ApiDescription> apiList = new ArrayList<>();
apiList.add(apiDescription);
Map<String, Model> responseModel = new HashMap<>();
responseModel.put("接口返回对象«JSONObject»", resultModel);
ApiListing api = new ApiListing("1.0",
"/",
"/openapi/call/abc",
produces, consumes,
"", new HashSet<>(), new ArrayList<>(), apiList, responseModel, "abc", 0, apiTags);
apis.add(api);
}
documentation.getApiListings().put("openapi", apis);
}
}

View File

@ -0,0 +1,26 @@
package org.jeecg.modules.openapi.controller;
import org.jeecg.common.api.vo.Result;
import org.jeecg.config.shiro.IgnoreAuth;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @date 2024/12/20 14:04
*/
@RestController
@RequestMapping("/openapi/demo")
public class OpenApiIndexController {
@GetMapping("index")
@IgnoreAuth
public Result<Map<String, String>> index() {
Map<String, String> result = new HashMap<>();
result.put("first", "Hello World");
return Result.ok(result);
}
}

View File

@ -0,0 +1,102 @@
package org.jeecg.modules.openapi.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
import org.jeecg.modules.openapi.service.OpenApiRecordService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @date 2024/12/10 9:57
*/
@RestController
@RequestMapping("/openapi/record")
public class OpenApiRecordController extends JeecgController<OpenApiRecord, OpenApiRecordService> {
/**
*
*
* @param openApiRecord
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@GetMapping(value = "/list")
public Result<?> queryPageList(OpenApiRecord openApiRecord, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<OpenApiRecord> queryWrapper = QueryGenerator.initQueryWrapper(openApiRecord, req.getParameterMap());
Page<OpenApiRecord> page = new Page<>(pageNo, pageSize);
IPage<OpenApiRecord> pageList = service.page(page, queryWrapper);
return Result.ok(pageList);
}
/**
*
*
* @param openApiRecord
* @return
*/
@PostMapping(value = "/add")
public Result<?> add(@RequestBody OpenApiRecord openApiRecord) {
service.save(openApiRecord);
return Result.ok("添加成功!");
}
/**
*
*
* @param openApiRecord
* @return
*/
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody OpenApiRecord openApiRecord) {
service.updateById(openApiRecord);
return Result.ok("修改成功!");
}
/**
* id
*
* @param id
* @return
*/
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
service.removeById(id);
return Result.ok("删除成功!");
}
/**
*
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.service.removeByIds(Arrays.asList(ids.split(",")));
return Result.ok("批量删除成功!");
}
/**
* id
*
* @param id
* @return
*/
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
OpenApiRecord openApiRecord = service.getById(id);
return Result.ok(openApiRecord);
}
}

View File

@ -0,0 +1,103 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
*
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApi implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
*
*/
private String name;
/**
* POSTGET
*/
private String requestMethod;
/**
*
*/
private String requestUrl;
/**
* IP
*/
private String blackList;
/**
*
*/
@TableField(exist = false)
private List<OpenApiHeader> headers;
/**
*
*/
@TableField(exist = false)
private List<OpenApiParam> params;
/**
* json
*/
private String body;
/**
*
*/
private String originUrl;
/**
* (1 2
*/
private Integer status;
/**
* 01
*/
@TableLogic
private Integer delFlag;
/**
*
*/
private String createBy;
/**
*
*/
private Date createTime;
/**
*
*/
private String updateBy;
/**
*
*/
private Date updateTime;
}

View File

@ -0,0 +1,63 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
*
* @date 2024/12/10 9:38
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiAuth implements Serializable {
private static final long serialVersionUID = -5933153354153738498L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
*
*/
private String name;
/**
* access key
*/
private String ak;
/**
* secret key
*/
private String sk;
/**
*
*/
private String createBy;
/**
*
*/
private Date createTime;
/**
*
*/
private String updateBy;
/**
*
*/
private Date updateTime;
}

View File

@ -0,0 +1,51 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
*
* @date 2024/12/10 14:37
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiHeader implements Serializable {
private static final long serialVersionUID = 5032708503120184683L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
private String apiId;
/**
* key
*/
private String key;
/**
* (0:1)
*/
private Integer isBlank;
/**
*
*/
private String defaultValue;
/**
*
*/
private String note;
}

View File

@ -0,0 +1,50 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* query
* @date 2024/12/10 14:37
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiParam implements Serializable {
private static final long serialVersionUID = -6174831468578022357L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
private String apiId;
/**
* key
*/
private String key;
/**
* (0:1)
*/
private Integer isBlank;
/**
*
*/
private String defaultValue;
/**
*
*/
private String note;
}

View File

@ -0,0 +1,55 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
*
* @date 2024/12/19 17:41
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiPermission implements Serializable {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
private String apiId;
/**
* ID
*/
private String apiAuthId;
/**
*
*/
private String createBy;
/**
*
*/
private Date createTime;
/**
*
*/
private String updateBy;
/**
*
*/
private Date updateTime;
}

View File

@ -0,0 +1,52 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
*
* @date 2024/12/10 9:41
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiRecord implements Serializable {
private static final long serialVersionUID = -5870384488947863579L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* ID
*/
private String apiId;
/**
* ID
*/
private String callAuthId;
/**
*
*/
private Date callTime;
/**
*
*/
private Long usedTime;
/**
*
*/
private Date responseTime;
}

View File

@ -0,0 +1,198 @@
package org.jeecg.modules.openapi.filter;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.jeecg.modules.openapi.service.OpenApiPermissionService;
import org.jeecg.modules.openapi.service.OpenApiRecordService;
import org.jeecg.modules.openapi.service.OpenApiService;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* @date 2024/12/19 16:55
*/
@Slf4j
public class ApiAuthFilter implements Filter {
private OpenApiRecordService openApiRecordService;
private OpenApiAuthService openApiAuthService;
private OpenApiPermissionService openApiPermissionService;
private OpenApiService openApiService;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
Date callTime = new Date();
HttpServletRequest request = (HttpServletRequest)servletRequest;
String ip = request.getRemoteAddr();
String appkey = request.getHeader("appkey");
String signature = request.getHeader("signature");
String timestamp = request.getHeader("timestamp");
OpenApi openApi = findOpenApi(request);
// IP 黑名单核验
checkBlackList(openApi, ip);
// 签名核验
checkSignValid(appkey, signature, timestamp);
OpenApiAuth openApiAuth = openApiAuthService.getByAppkey(appkey);
// 认证信息核验
checkSignature(appkey, signature, timestamp, openApiAuth);
// 业务核验
checkPermission(openApi, openApiAuth);
filterChain.doFilter(servletRequest, servletResponse);
long endTime = System.currentTimeMillis();
OpenApiRecord record = new OpenApiRecord();
record.setApiId(openApi.getId());
record.setCallAuthId(openApiAuth.getId());
record.setCallTime(callTime);
record.setUsedTime(endTime - startTime);
record.setResponseTime(new Date());
openApiRecordService.save(record);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ServletContext servletContext = filterConfig.getServletContext();
WebApplicationContext applicationContext = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
this.openApiService = applicationContext.getBean(OpenApiService.class);
this.openApiRecordService = applicationContext.getBean(OpenApiRecordService.class);
this.openApiAuthService = applicationContext.getBean(OpenApiAuthService.class);
this.openApiPermissionService = applicationContext.getBean(OpenApiPermissionService.class);
}
/**
* IP
* @param openApi
* @param ip
*/
protected void checkBlackList(OpenApi openApi, String ip) {
if (!StringUtils.hasText(openApi.getBlackList())) {
return;
}
List<String> blackList = Arrays.asList(openApi.getBlackList().split(","));
if (blackList.contains(ip)) {
throw new JeecgBootException("目标接口限制IP[" + ip + "]进行访问IP已记录请停止访问");
}
}
/**
*
* @param appkey
* @param signature
* @param timestamp
* @return
*/
protected void checkSignValid(String appkey, String signature, String timestamp) {
if (!StringUtils.hasText(appkey)) {
throw new JeecgBootException("appkey为空");
}
if (!StringUtils.hasText(signature)) {
throw new JeecgBootException("signature为空");
}
if (!StringUtils.hasText(timestamp)) {
throw new JeecgBootException("timastamp时间戳为空");
}
if (!timestamp.matches("[0-9]*")) {
throw new JeecgBootException("timastamp时间戳不合法");
}
if (System.currentTimeMillis() - Long.parseLong(timestamp) > 5 * 60 * 1000) {
throw new JeecgBootException("signature签名已过期(超过五分钟)");
}
}
/**
*
* @param appKey
* @param signature
* @param timestamp
* @param openApiAuth
* @return
* @throws Exception
*/
protected void checkSignature(String appKey, String signature, String timestamp, OpenApiAuth openApiAuth) {
if(openApiAuth==null){
throw new JeecgBootException("不存在认证信息");
}
if(!appKey.equals(openApiAuth.getAk())){
throw new JeecgBootException("appkey错误");
}
if (!signature.equals(md5(appKey + openApiAuth.getSk() + timestamp))) {
throw new JeecgBootException("signature签名错误");
}
}
protected void checkPermission(OpenApi openApi, OpenApiAuth openApiAuth) {
List<OpenApiPermission> permissionList = openApiPermissionService.findByAuthId(openApiAuth.getId());
boolean hasPermission = false;
for (OpenApiPermission permission : permissionList) {
if (permission.getApiId().equals(openApi.getId())) {
hasPermission = true;
break;
}
}
if (!hasPermission) {
throw new JeecgBootException("该appKey未授权当前接口");
}
}
/**
* @return String
* @Title: MD5
* @Description: MD5
*/
protected static String md5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes("utf-8"));
byte[] hash = md.digest();
int i;
StringBuffer buf = new StringBuffer(32);
for (int offset = 0; offset < hash.length; offset++) {
i = hash[offset];
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
result = buf.toString();
} catch (Exception e) {
log.error("sign签名错误", e);
}
return result;
}
protected OpenApi findOpenApi(HttpServletRequest request) {
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/") + 1);
return openApiService.findByPath(path);
}
}

View File

@ -0,0 +1,25 @@
package org.jeecg.modules.openapi.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @date 2024/12/19 17:09
*/
@Configuration
public class ApiFilterConfig {
/**
*
* @Description: api
*/
@Bean
public FilterRegistrationBean<ApiAuthFilter> authFilter() {
FilterRegistrationBean<ApiAuthFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new ApiAuthFilter());
registration.setName("apiAuthFilter");
registration.addUrlPatterns("/openapi/call/*");
return registration;
}
}

View File

@ -0,0 +1,36 @@
package org.jeecg.modules.openapi.generator;
import java.security.SecureRandom;
/**
* AK/SK
*/
public class AKSKGenerator {
private static final String CHAR_POOL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final int AK_LENGTH = 16; // Adjust as per requirements
private static final int SK_LENGTH = 32;
public static String[] genAKSKPair() {
return new String[]{genAK(), genSK()};
}
public static String genAK() {
return "ak-" + generateRandomString(AK_LENGTH);
}
public static String genSK() {
return generateRandomString(SK_LENGTH);
}
private static String generateRandomString(int length) {
SecureRandom random = new SecureRandom();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append(CHAR_POOL.charAt(random.nextInt(CHAR_POOL.length())));
}
return sb.toString();
}
}

View File

@ -0,0 +1,28 @@
package org.jeecg.modules.openapi.generator;
import lombok.experimental.UtilityClass;
import java.util.Random;
/**
* @date 2024/12/10 10:00
*/
@UtilityClass
public class PathGenerator {
// Base62字符集
private static final String BASE62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/**
*
* @return
*/
public static String genPath() {
StringBuilder result = new StringBuilder();
Random random = new Random();
for (int i=0; i<8; i++) {
result.append(BASE62.charAt(random.nextInt(62)));
}
return result.toString();
}
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
/**
* @date 2024/12/10 9:49
*/
@Mapper
public interface OpenApiAuthMapper extends BaseMapper<OpenApiAuth> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
/**
* @date 2024/12/10 14:47
*/
@Mapper
public interface OpenApiHeaderMapper extends BaseMapper<OpenApiHeader> {
}

View File

@ -0,0 +1,9 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApi;
@Mapper
public interface OpenApiMapper extends BaseMapper<OpenApi> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiParam;
/**
* @date 2024/12/10 14:48
*/
@Mapper
public interface OpenApiParamMapper extends BaseMapper<OpenApiParam> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
/**
* @date 2024/12/19 17:43
*/
@Mapper
public interface OpenApiPermissionMapper extends BaseMapper<OpenApiPermission> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
/**
* @date 2024/12/10 9:50
*/
@Mapper
public interface OpenApiRecordMapper extends BaseMapper<OpenApiRecord> {
}

View File

@ -0,0 +1,11 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
/**
* @date 2024/12/10 9:50
*/
public interface OpenApiAuthService extends IService<OpenApiAuth> {
OpenApiAuth getByAppkey(String appkey);
}

View File

@ -0,0 +1,16 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
import java.util.List;
/**
* @date 2024/12/10 14:48
*/
public interface OpenApiHeaderService extends IService<OpenApiHeader> {
boolean deleteByApiId(String apiId);
List<OpenApiHeader> findByApiId(String apiId);
}

View File

@ -0,0 +1,15 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiParam;
import java.util.List;
/**
* @date 2024/12/10 14:49
*/
public interface OpenApiParamService extends IService<OpenApiParam> {
boolean deleteByApiId(String apiId);
List<OpenApiParam> findByApiId(String apiId);
}

View File

@ -0,0 +1,13 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import java.util.List;
/**
* @date 2024/12/19 17:44
*/
public interface OpenApiPermissionService extends IService<OpenApiPermission> {
List<OpenApiPermission> findByAuthId(String authId);
}

View File

@ -0,0 +1,10 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
/**
* @date 2024/12/10 9:51
*/
public interface OpenApiRecordService extends IService<OpenApiRecord> {
}

View File

@ -0,0 +1,8 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApi;
public interface OpenApiService extends IService<OpenApi> {
OpenApi findByPath(String path);
}

View File

@ -0,0 +1,19 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.mapper.OpenApiAuthMapper;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.springframework.stereotype.Service;
/**
* @date 2024/12/10 9:51
*/
@Service
public class OpenApiAuthServiceImpl extends ServiceImpl<OpenApiAuthMapper, OpenApiAuth> implements OpenApiAuthService {
@Override
public OpenApiAuth getByAppkey(String appkey) {
return baseMapper.selectOne(Wrappers.lambdaUpdate(OpenApiAuth.class).eq(OpenApiAuth::getAk, appkey), false);
}
}

View File

@ -0,0 +1,26 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
import org.jeecg.modules.openapi.mapper.OpenApiHeaderMapper;
import org.jeecg.modules.openapi.service.OpenApiHeaderService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @date 2024/12/10 14:49
*/
@Service
public class OpenApiHeaderServiceImpl extends ServiceImpl<OpenApiHeaderMapper, OpenApiHeader> implements OpenApiHeaderService {
@Override
public boolean deleteByApiId(String apiId) {
return baseMapper.delete(Wrappers.lambdaUpdate(OpenApiHeader.class).eq(OpenApiHeader::getApiId, apiId)) > 0;
}
@Override
public List<OpenApiHeader> findByApiId(String apiId) {
return baseMapper.selectList(Wrappers.lambdaQuery(OpenApiHeader.class).eq(OpenApiHeader::getApiId, apiId));
}
}

View File

@ -0,0 +1,27 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiParam;
import org.jeecg.modules.openapi.mapper.OpenApiParamMapper;
import org.jeecg.modules.openapi.service.OpenApiParamService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @date 2024/12/10 14:50
*/
@Service
public class OpenApiParamServiceImpl extends ServiceImpl<OpenApiParamMapper, OpenApiParam> implements OpenApiParamService {
@Override
public boolean deleteByApiId(String apiId) {
return baseMapper.delete(Wrappers.lambdaUpdate(OpenApiParam.class).eq(OpenApiParam::getApiId, apiId)) > 0;
}
@Override
public List<OpenApiParam> findByApiId(String apiId) {
return baseMapper.selectList(Wrappers.lambdaQuery(OpenApiParam.class).eq(OpenApiParam::getApiId, apiId));
}
}

View File

@ -0,0 +1,22 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import org.jeecg.modules.openapi.mapper.OpenApiPermissionMapper;
import org.jeecg.modules.openapi.service.OpenApiPermissionService;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
/**
* @date 2024/12/19 17:44
*/
@Service
public class OpenApiPermissionServiceImpl extends ServiceImpl<OpenApiPermissionMapper, OpenApiPermission> implements OpenApiPermissionService {
@Override
public List<OpenApiPermission> findByAuthId(String authId) {
return baseMapper.selectList(Wrappers.lambdaQuery(OpenApiPermission.class).eq(OpenApiPermission::getApiAuthId, authId));
}
}

View File

@ -0,0 +1,14 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
import org.jeecg.modules.openapi.mapper.OpenApiRecordMapper;
import org.jeecg.modules.openapi.service.OpenApiRecordService;
import org.springframework.stereotype.Service;
/**
* @date 2024/12/10 9:53
*/
@Service
public class OpenApiRecordServiceImpl extends ServiceImpl<OpenApiRecordMapper, OpenApiRecord> implements OpenApiRecordService {
}

View File

@ -0,0 +1,16 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.mapper.OpenApiMapper;
import org.jeecg.modules.openapi.service.OpenApiService;
import org.springframework.stereotype.Service;
@Service
public class OpenApiServiceImpl extends ServiceImpl<OpenApiMapper, OpenApi> implements OpenApiService {
@Override
public OpenApi findByPath(String path) {
return baseMapper.selectOne(Wrappers.lambdaQuery(OpenApi.class).eq(OpenApi::getRequestUrl, path), false);
}
}

View File

@ -163,9 +163,9 @@ spring:
slow-sql-millis: 5000
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://172.16.10.227:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: root
password: root@2023
driver-class-name: com.mysql.cj.jdbc.Driver
# 多数据源配置
#multi-datasource1:
@ -176,9 +176,9 @@ spring:
#redis 配置
redis:
database: 0
host: 127.0.0.1
host: 172.16.10.227
port: 6379
password: ''
password: 'Whdsj@redis0523'
#mybatis plus 设置
mybatis-plus:
mapper-locations: classpath*:org/jeecg/**/xml/*Mapper.xml