api接口文档支持用户权限控制

This commit is contained in:
暮光:城中城
2021-12-11 22:36:05 +08:00
parent a2553097bd
commit 9dfb8f9ac6
59 changed files with 1701 additions and 479 deletions

View File

@@ -0,0 +1,130 @@
package com.zyplayer.doc.api.controller;
import com.zyplayer.doc.api.controller.vo.DocUserAuthVo;
import com.zyplayer.doc.api.service.ApiDocAuthJudgeService;
import com.zyplayer.doc.core.annotation.AuthMan;
import com.zyplayer.doc.core.json.DocResponseJson;
import com.zyplayer.doc.core.json.ResponseJson;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.DocUserUtil;
import com.zyplayer.doc.data.repository.manage.entity.*;
import com.zyplayer.doc.data.repository.support.consts.ApiAuthType;
import com.zyplayer.doc.data.repository.support.consts.DocSysModuleType;
import com.zyplayer.doc.data.repository.support.consts.DocSysType;
import com.zyplayer.doc.data.service.manage.ApiDocService;
import com.zyplayer.doc.data.service.manage.AuthInfoService;
import com.zyplayer.doc.data.service.manage.UserAuthService;
import com.zyplayer.doc.data.service.manage.UserInfoService;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
/**
* api权限控制器
*
* @author 暮光:城中城
* @since 2021年12月12日
*/
@AuthMan
@RestController
@RequestMapping("/doc-api/doc/auth")
public class ApiDocAuthController {
private static Logger logger = LoggerFactory.getLogger(ApiDocAuthController.class);
@Resource
UserAuthService userAuthService;
@Resource
AuthInfoService authInfoService;
@Resource
UserInfoService userInfoService;
@Resource
ApiDocService apiDocService;
@Resource
ApiDocAuthJudgeService apiDocAuthJudgeService;
@PostMapping("/list")
public ResponseJson<Object> list(Long docId) {
if (!apiDocAuthJudgeService.haveManageAuth(docId)) {
return DocResponseJson.warn("没有此文档的操作权限");
}
List<UserAuth> authList = userAuthService.getModuleAuthList(DocSysType.API.getType(), DocSysModuleType.Api.DOC.getType(), docId);
if (CollectionUtils.isEmpty(authList)) {
return DocResponseJson.ok();
}
// 权限ID对应的权限名
Collection<AuthInfo> authInfoList = authInfoService.listByIds(authList.stream().map(UserAuth::getAuthId).collect(Collectors.toSet()));
Map<Long, String> authInfoMap = authInfoList.stream().collect(Collectors.toMap(AuthInfo::getId, AuthInfo::getAuthName));
Collection<UserInfo> userInfoList = userInfoService.listByIds(authList.stream().map(UserAuth::getUserId).collect(Collectors.toSet()));
Map<Long, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, val -> val));
// 返回结果组装
List<DocUserAuthVo> authVoList = new LinkedList<>();
for (UserAuth userAuth : authList) {
UserInfo userInfo = userInfoMap.get(userAuth.getUserId());
String authCode = authInfoMap.get(userAuth.getAuthId());
DocUserAuthVo authVo = new DocUserAuthVo();
authVo.setAuthType(ApiAuthType.typeOf(authCode).getType());
authVo.setUserId(userAuth.getUserId());
authVo.setUserNo(userInfo.getUserNo());
authVo.setUserName(userInfo.getUserName());
authVo.setEmail(userInfo.getEmail());
authVo.setPhone(userInfo.getPhone());
authVo.setSex(userInfo.getSex());
authVoList.add(authVo);
}
return DocResponseJson.ok(authVoList);
}
@PostMapping("/assign")
public ResponseJson<Object> assign(Long docId, Long userId, Integer authType) {
if (!apiDocAuthJudgeService.haveManageAuth(docId)) {
return DocResponseJson.warn("没有此文档的操作权限");
}
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
String authCode = ApiAuthType.typeOf(authType).getCode();
AuthInfo authInfo = authInfoService.getByCode(authCode);
List<UserAuth> userModuleAuthList = userAuthService.getUserModuleAuthList(userId, DocSysType.API.getType(), DocSysModuleType.Api.DOC.getType(), docId);
if (CollectionUtils.isNotEmpty(userModuleAuthList)) {
UserAuth userAuth = userModuleAuthList.remove(0);
// 错误数据兼容移除
if (userModuleAuthList.size() > 0) {
List<Long> authIdList = userModuleAuthList.stream().map(UserAuth::getId).collect(Collectors.toList());
userAuthService.removeByIds(authIdList);
}
userAuth.setAuthId(authInfo.getId());
userAuth.setUpdateTime(new Date());
userAuth.setUpdateUid(currentUser.getUserId());
userAuthService.updateById(userAuth);
} else {
UserAuth userAuth = new UserAuth();
userAuth.setUserId(userId);
userAuth.setSysType(DocSysType.API.getType());
userAuth.setSysModuleType(DocSysModuleType.Api.DOC.getType());
userAuth.setSysModuleId(docId);
userAuth.setAuthId(authInfo.getId());
userAuth.setCreationTime(new Date());
userAuth.setCreateUid(currentUser.getUserId());
userAuth.setUpdateTime(new Date());
userAuth.setUpdateUid(currentUser.getUserId());
userAuth.setDelFlag(0);
userAuthService.save(userAuth);
}
return DocResponseJson.ok();
}
@PostMapping("/delete")
public ResponseJson<Object> delete(Long docId, Long userId) {
if (!apiDocAuthJudgeService.haveManageAuth(docId)) {
return DocResponseJson.warn("没有此文档的操作权限");
}
userAuthService.deleteUserModuleAuth(userId, DocSysType.API.getType(), DocSysModuleType.Api.DOC.getType(), docId);
return DocResponseJson.ok();
}
}

View File

@@ -2,9 +2,9 @@ package com.zyplayer.doc.api.controller;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zyplayer.doc.api.framework.utils.SwaggerDocUtil;
import com.zyplayer.doc.api.service.ApiDocAuthJudgeService;
import com.zyplayer.doc.api.service.SwaggerHttpRequestService;
import com.zyplayer.doc.core.annotation.AuthMan;
import com.zyplayer.doc.core.json.DocResponseJson;
@@ -12,6 +12,7 @@ import com.zyplayer.doc.core.json.ResponseJson;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.DocUserUtil;
import com.zyplayer.doc.data.repository.manage.entity.ApiDoc;
import com.zyplayer.doc.data.repository.manage.vo.ApiDocVo;
import com.zyplayer.doc.data.service.manage.ApiDocService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
@@ -41,7 +42,9 @@ public class ApiDocumentController {
private static Logger logger = LoggerFactory.getLogger(ApiDocumentController.class);
@Resource
private ApiDocService swaggerDocService;
ApiDocAuthJudgeService apiDocAuthJudgeService;
@Resource
private ApiDocService apiDocService;
@Resource
private SwaggerHttpRequestService swaggerHttpRequestService;
@@ -54,8 +57,8 @@ public class ApiDocumentController {
*/
@ResponseBody
@PostMapping(value = "/list")
public ResponseJson<List<ApiDoc>> list(ApiDoc apiDoc, Integer pageNum, Integer pageSize) {
IPage<ApiDoc> docList = swaggerDocService.getApiDocList(apiDoc, pageNum, pageSize);
public ResponseJson<List<ApiDocVo>> list(ApiDoc apiDoc, Integer pageNum, Integer pageSize) {
IPage<ApiDocVo> docList = apiDocService.getApiDocList(apiDoc, pageNum, pageSize);
return DocResponseJson.ok(docList);
}
@@ -69,7 +72,10 @@ public class ApiDocumentController {
@ResponseBody
@PostMapping(value = "/detail")
public ResponseJson<List<ApiDoc>> detail(Long id) {
ApiDoc apiDoc = swaggerDocService.getById(id);
ApiDoc apiDoc = apiDocService.getById(id);
if (!apiDocAuthJudgeService.haveDevelopAuth(apiDoc)) {
return DocResponseJson.warn("没有此文档的查看权限");
}
return DocResponseJson.ok(apiDoc);
}
@@ -91,10 +97,13 @@ public class ApiDocumentController {
if (apiDoc.getId() == null) {
apiDoc.setShareUuid(IdUtil.simpleUUID());
} else {
ApiDoc apiDocSel = swaggerDocService.getById(apiDoc.getId());
ApiDoc apiDocSel = apiDocService.getById(apiDoc.getId());
if (apiDocSel == null) {
return DocResponseJson.warn("未找到指定的文档记录信息");
}
if (!apiDocAuthJudgeService.haveManageAuth(apiDocSel)) {
return DocResponseJson.warn("没有此文档的操作权限");
}
if (StringUtils.isBlank(apiDocSel.getShareUuid())) {
apiDoc.setShareUuid(IdUtil.simpleUUID());
}
@@ -118,7 +127,7 @@ public class ApiDocumentController {
}
// 删除原有文档
if (apiDoc.getId() != null) {
swaggerDocService.removeById(apiDoc.getId());
apiDocService.removeById(apiDoc.getId());
}
// 存明细地址
for (SwaggerResource resource : resourceList) {
@@ -126,13 +135,13 @@ public class ApiDocumentController {
apiDoc.setDocUrl(swaggerDomain + resource.getUrl());
apiDoc.setName(resource.getName());
apiDoc.setShareUuid(IdUtil.simpleUUID());
swaggerDocService.save(apiDoc);
apiDocService.save(apiDoc);
}
} else {
swaggerDocService.saveOrUpdate(apiDoc);
apiDocService.saveOrUpdate(apiDoc);
}
} else if (Objects.equals(apiDoc.getDocType(), 2) || Objects.equals(apiDoc.getDocType(), 4)) {
swaggerDocService.saveOrUpdate(apiDoc);
apiDocService.saveOrUpdate(apiDoc);
} else {
return DocResponseJson.warn("暂不支持的文档类型");
}
@@ -152,32 +161,40 @@ public class ApiDocumentController {
if (apiDoc.getId() == null) {
return DocResponseJson.warn("请指定修改的记录ID");
}
// 基本信息可以改,删除需要管理员权限
if (Objects.equals(apiDoc.getYn(), 0)) {
if (!apiDocAuthJudgeService.haveManageAuth(apiDoc.getId())) {
return DocResponseJson.warn("没有此文档的删除权限");
}
} else {
if (!apiDocAuthJudgeService.haveDevelopAuth(apiDoc.getId())) {
return DocResponseJson.warn("没有此文档的编辑权限");
}
}
ApiDoc swaggerDocUp = new ApiDoc();
swaggerDocUp.setId(apiDoc.getId());
swaggerDocUp.setDocStatus(apiDoc.getDocStatus());
swaggerDocUp.setShareInstruction(apiDoc.getShareInstruction());
swaggerDocUp.setYn(apiDoc.getYn());
swaggerDocService.updateById(swaggerDocUp);
apiDocService.updateById(swaggerDocUp);
return DocResponseJson.ok();
}
@RequestMapping("/apis")
public ResponseJson<List<ApiDoc>> resources() {
QueryWrapper<ApiDoc> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("yn", 1);
queryWrapper.eq("doc_status", 1);
queryWrapper.orderByAsc("id");
queryWrapper.select("id", "name", "doc_type", "doc_url", "rewrite_domain", "open_visit", "doc_status");
List<ApiDoc> docList = swaggerDocService.list(queryWrapper);
List<ApiDoc> docList = apiDocService.getApiDocList();
return DocResponseJson.ok(docList);
}
@RequestMapping("/apis/detail")
public ResponseJson<Object> detail(HttpServletRequest request, Long id) {
ApiDoc apiDoc = swaggerDocService.getById(id);
ApiDoc apiDoc = apiDocService.getById(id);
if (apiDoc == null) {
return DocResponseJson.warn("文档不存在");
}
if (!apiDocAuthJudgeService.haveDevelopAuth(apiDoc)) {
return DocResponseJson.warn("没有此文档的查看权限");
}
if (Objects.equals(apiDoc.getDocType(), 1)) {
try {
String docsDomain = SwaggerDocUtil.getV2ApiDocsDomain(apiDoc.getDocUrl());

View File

@@ -1,17 +1,10 @@
package com.zyplayer.doc.api.controller;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.zyplayer.doc.api.framework.utils.SwaggerDocUtil;
import com.zyplayer.doc.api.service.SwaggerHttpRequestService;
import com.zyplayer.doc.core.annotation.AuthMan;
import com.zyplayer.doc.core.json.DocResponseJson;
import com.zyplayer.doc.core.json.ResponseJson;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.DocUserUtil;
import com.zyplayer.doc.data.repository.manage.entity.ApiDoc;
import com.zyplayer.doc.data.repository.manage.entity.ApiGlobalParam;
import com.zyplayer.doc.data.service.manage.ApiDocService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -19,11 +12,9 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.swagger.web.SwaggerResource;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
import java.util.Objects;

View File

@@ -47,6 +47,7 @@ public class ApiSwaggerProxyController {
QueryWrapper<ApiDoc> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("yn", 1);
queryWrapper.eq("doc_status", 1);
queryWrapper.eq("open_visit", 1);
queryWrapper.in("doc_type", 1, 2);
queryWrapper.orderByAsc("id");
queryWrapper.select("id", "name", "rewrite_domain");
@@ -66,7 +67,7 @@ public class ApiSwaggerProxyController {
@RequestMapping(value = "/v2/api-docs", produces = {MimeTypeUtils.APPLICATION_JSON_VALUE, HAL_MEDIA_TYPE})
public ResponseEntity<Object> content(HttpServletRequest request, Long id) {
ApiDoc swaggerDoc = swaggerDocService.getById(id);
if (swaggerDoc == null) {
if (swaggerDoc == null || !Objects.equals(swaggerDoc.getOpenVisit(), 1)) {
throw new ConfirmException("文档不存在");
}
if (Objects.equals(swaggerDoc.getDocType(), 1)) {

View File

@@ -0,0 +1,211 @@
package com.zyplayer.doc.api.controller.vo;
import java.io.Serializable;
import java.util.Date;
/**
* api文档地址Vo
*
* @author 暮光:城中城
* @since 2021-11-25
*/
public class ApiDocVo implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
/**
* 文档名称
*/
private String name;
/**
* 文档类型 1=swagger url 2=swagger json 3=openapi url 4=openapi json 5=自建API分组
*/
private Integer docType;
/**
* 文档URL地址
*/
private String docUrl;
/**
* 文档json内容
*/
private String jsonContent;
/**
* 重写的域名
*/
private String rewriteDomain;
/**
* 是否开放访问 0=否 1=是
*/
private Integer openVisit;
/**
* 状态 1=启用 2=禁用
*/
private Integer docStatus;
/**
* 开放文档UUID
*/
private String shareUuid;
/**
* 开放文档使用说明
*/
private String shareInstruction;
/**
* 创建人ID
*/
private Long createUserId;
/**
* 创建人名字
*/
private String createUserName;
/**
* 创建时间
*/
private Date createTime;
/**
* 是否有效 0=无效 1=有效
*/
private Integer yn;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getDocType() {
return docType;
}
public void setDocType(Integer docType) {
this.docType = docType;
}
public String getDocUrl() {
return docUrl;
}
public void setDocUrl(String docUrl) {
this.docUrl = docUrl;
}
public String getJsonContent() {
return jsonContent;
}
public void setJsonContent(String jsonContent) {
this.jsonContent = jsonContent;
}
public String getRewriteDomain() {
return rewriteDomain;
}
public void setRewriteDomain(String rewriteDomain) {
this.rewriteDomain = rewriteDomain;
}
public Integer getOpenVisit() {
return openVisit;
}
public void setOpenVisit(Integer openVisit) {
this.openVisit = openVisit;
}
public Integer getDocStatus() {
return docStatus;
}
public void setDocStatus(Integer docStatus) {
this.docStatus = docStatus;
}
public Long getCreateUserId() {
return createUserId;
}
public void setCreateUserId(Long createUserId) {
this.createUserId = createUserId;
}
public String getCreateUserName() {
return createUserName;
}
public void setCreateUserName(String createUserName) {
this.createUserName = createUserName;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Integer getYn() {
return yn;
}
public void setYn(Integer yn) {
this.yn = yn;
}
@Override
public String toString() {
return "ApiDoc{" +
"id=" + id +
", name=" + name +
", docType=" + docType +
", docUrl=" + docUrl +
", jsonContent=" + jsonContent +
", rewriteDomain=" + rewriteDomain +
", openVisit=" + openVisit +
", docStatus=" + docStatus +
", createUserId=" + createUserId +
", createUserName=" + createUserName +
", createTime=" + createTime +
", yn=" + yn +
"}";
}
public String getShareUuid() {
return shareUuid;
}
public void setShareUuid(String shareUuid) {
this.shareUuid = shareUuid;
}
public String getShareInstruction() {
return shareInstruction;
}
public void setShareInstruction(String shareInstruction) {
this.shareInstruction = shareInstruction;
}
}

View File

@@ -0,0 +1,101 @@
package com.zyplayer.doc.api.controller.vo;
/**
* 用户权限返回值对象
*
* @author 暮光:城中城
* @since 2021年12月12日
*/
public class DocUserAuthVo {
/**
* 权限类型
*/
private Integer authType;
/**
* 用户ID
*/
private Long userId;
/**
* 用户编号,用于登录等
*/
private String userNo;
/**
* 用户名
*/
private String userName;
/**
* 邮箱
*/
private String email;
/**
* 手机号
*/
private String phone;
/**
* 性别 0=女 1=男
*/
private Integer sex;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserNo() {
return userNo;
}
public void setUserNo(String userNo) {
this.userNo = userNo;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Integer getAuthType() {
return authType;
}
public void setAuthType(Integer authType) {
this.authType = authType;
}
}

View File

@@ -0,0 +1,99 @@
package com.zyplayer.doc.api.service;
import com.zyplayer.doc.data.config.security.DocUserDetails;
import com.zyplayer.doc.data.config.security.DocUserUtil;
import com.zyplayer.doc.data.repository.manage.entity.ApiDoc;
import com.zyplayer.doc.data.repository.manage.entity.AuthInfo;
import com.zyplayer.doc.data.repository.manage.entity.UserAuth;
import com.zyplayer.doc.data.repository.support.consts.ApiAuthType;
import com.zyplayer.doc.data.repository.support.consts.DocSysModuleType;
import com.zyplayer.doc.data.repository.support.consts.DocSysType;
import com.zyplayer.doc.data.service.manage.ApiDocService;
import com.zyplayer.doc.data.service.manage.AuthInfoService;
import com.zyplayer.doc.data.service.manage.UserAuthService;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
/**
* 判断文档权限
*
* @author 暮光:城中城
* @since 2021-12-12
*/
@Service
public class ApiDocAuthJudgeService {
@Resource
UserAuthService userAuthService;
@Resource
AuthInfoService authInfoService;
@Resource
ApiDocService apiDocService;
/**
* 判断当前用户是否有管理员权限
*
* @author 暮光:城中城
* @since 2021-12-12
*/
public boolean haveManageAuth(Long docId) {
ApiDoc apiDoc = apiDocService.getById(docId);
return haveManageAuth(apiDoc);
}
/**
* 判断当前用户是否有管理员权限
*
* @author 暮光:城中城
* @since 2021-12-12
*/
public boolean haveManageAuth(ApiDoc apiDoc) {
if (apiDoc == null) {
return false;
}
// 创建者
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
if (Objects.equals(apiDoc.getCreateUserId(), currentUser.getUserId())) {
return true;
}
// 管理员
AuthInfo authInfo = authInfoService.getByCode(ApiAuthType.MANAGE.getCode());
List<UserAuth> userModuleAuthList = userAuthService.getUserModuleAuthList(currentUser.getUserId(), DocSysType.API.getType(), DocSysModuleType.Api.DOC.getType(), apiDoc.getId());
return userModuleAuthList.stream().anyMatch(auth -> Objects.equals(auth.getAuthId(), authInfo.getId()));
}
/**
* 判断当前用户是否有查看权限
*
* @author 暮光:城中城
* @since 2021-12-12
*/
public boolean haveDevelopAuth(Long docId) {
ApiDoc apiDoc = apiDocService.getById(docId);
return haveDevelopAuth(apiDoc);
}
/**
* 判断当前用户是否有查看权限
*
* @author 暮光:城中城
* @since 2021-12-12
*/
public boolean haveDevelopAuth(ApiDoc apiDoc) {
if (apiDoc == null) {
return false;
}
// 创建者
DocUserDetails currentUser = DocUserUtil.getCurrentUser();
if (Objects.equals(apiDoc.getCreateUserId(), currentUser.getUserId())) {
return true;
}
// 开发人员,存在则说明肯定是管理员或开发人员
List<UserAuth> userModuleAuthList = userAuthService.getUserModuleAuthList(currentUser.getUserId(), DocSysType.API.getType(), DocSysModuleType.Api.DOC.getType(), apiDoc.getId());
return CollectionUtils.isNotEmpty(userModuleAuthList);
}
}