How to trim Swagger docs based on current User Role in Java Spring?(如何根据 Java Spring 中的当前用户角色修剪 Swagger 文档?)
问题描述
我正在使用 Spring Boot 开发应用程序,我正在使用 Swagger 自动生成 API 文档,并且我还使用 swagger-ui.html
与这些 API 进行交互.
I'm developing application using Spring Boot, and I'm using Swagger to auto-generate API docs and also I use swagger-ui.html
to interact with those APIs.
我也启用了 Spring Security,并且我有不同角色的用户.不同的角色可以使用不同的 REST API.
I have Spring Security enabled too, and I have Users with different roles. Different REST APIs are available to different roles.
问题:如何配置 Swagger 以尊重 Spring 的 @Secured
注释并修剪 swagger-ui.html
显示的操作,以便只有当前用户可用的操作可用?
Question: how do I configure Swagger to respect Spring's @Secured
annotation and trim operations displayed by swagger-ui.html
so that only operations available to current user are available?
即想象下面的控制器
@RestController
@Secured(ROLE_USER)
public void SomeRestController {
@GetMapping
@Secured(ROLE_USER_TOP_MANAGER)
public String getInfoForTopManager() { /*...*/ }
@GetMapping
@Secured(ROLE_USER_MIDDLE_MANAGER)
public String getInfoForMiddleManager() { /*...*/ }
@GetMapping
public String getInfoForAnyUser() { /*...*/ }
}
Swagger 将同时显示操作 getInfoForTopManager
和 getInfoForMiddleManager
而不管当前用户角色如何.如果当前经过身份验证的用户角色是 ROLE_USER_MIDDLE_MANAGER
,我希望 Swagger 中只提供 getInfoForMiddleManager
和 getInfoForAnyUser
操作.
Swagger will show both operations getInfoForTopManager
and getInfoForMiddleManager
regardless of current user role. In case currently authenticated user role is ROLE_USER_MIDDLE_MANAGER
, I want only getInfoForMiddleManager
and getInfoForAnyUser
operations to be available in the Swagger.
推荐答案
好的,我认为找到了解决该问题的好方法.解决方案由两部分组成:
Ok, I think found good solution to that question. Solution consists of 2 parts:
- 通过
OperationBuilderPlugin
扩展控制器扫描逻辑以保留 Swagger 供应商扩展中的角色 - 覆盖
ServiceModelToSwagger2MapperImpl
bean 以根据当前安全上下文过滤掉操作
- Extend controllers scanning logic through
OperationBuilderPlugin
to retain roles in the Swagger's vendor extensions - Override
ServiceModelToSwagger2MapperImpl
bean to filter out actions based on current security context
在您的项目中,这可能看起来有点不同(即您很可能没有像 securityContextResolver
这样的东西),但我相信您会从以下代码中了解此解决方案的要点:
In your project this might look a bit different (i.e. most likely you don't have thing like securityContextResolver
), but I believe you'll get the gist of this solution from following code:
第 1 部分:扩展控制器扫描逻辑以保留 Swagger 供应商扩展中的角色
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class OperationBuilderPluginSecuredAware implements OperationBuilderPlugin {
@Override
public void apply(OperationContext context) {
Set<String> roles = new HashSet<>();
Secured controllerAnnotation = context.findControllerAnnotation(Secured.class).orNull();
if (controllerAnnotation != null) {
roles.addAll(List.of(controllerAnnotation.value()));
}
Secured methodAnnotation = context.findAnnotation(Secured.class).orNull();
if (methodAnnotation != null) {
roles.addAll(List.of(methodAnnotation.value()));
}
if (!roles.isEmpty()) {
context.operationBuilder().extensions(List.of(new TrimToRoles(roles.toArray(new String[0]))));
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
}
第 2 部分:根据当前安全上下文过滤掉操作
@Primary
@Component
public class ServiceModelToSwagger2MapperImplEx extends ServiceModelToSwagger2MapperImpl {
@Autowired
private SecurityContextResolver<User> securityContextResolver;
@Override
protected io.swagger.models.Operation mapOperation(Operation from) {
if (from == null) {
return null;
}
if (!isPermittedForCurrentUser(findTrimToRolesExtension(from.getVendorExtensions()))) {
return null;
}
return super.mapOperation(from);
}
private boolean isPermittedForCurrentUser(TrimToRoles trimToRoles) {
if (trimToRoles == null) {
return true;
}
if (securityContextResolver.hasAnyRole(trimToRoles.getValue())) {
return true;
}
return false;
}
private TrimToRoles findTrimToRolesExtension(@SuppressWarnings("rawtypes") List<VendorExtension> list) {
if (CollectionUtils.isEmpty(list)) {
return null;
}
return list.stream().filter(x -> x instanceof TrimToRoles).map(TrimToRoles.class::cast).findFirst()
.orElse(null);
}
@Override
protected Map<String, Path> mapApiListings(Multimap<String, ApiListing> apiListings) {
Map<String, Path> paths = super.mapApiListings(apiListings);
return paths.entrySet().stream().filter(x -> !x.getValue().isEmpty())
.collect(Collectors.toMap(x -> x.getKey(), v -> v.getValue()));
}
@Override
public Swagger mapDocumentation(Documentation from) {
Swagger ret = super.mapDocumentation(from);
Predicate<? super Tag> hasAtLeastOneOperation = tag -> ret.getPaths().values().stream()
.anyMatch(x -> x.getOperations().stream().anyMatch(y -> y.getTags().contains(tag.getName())));
ret.setTags(ret.getTags().stream().filter(hasAtLeastOneOperation).collect(Collectors.toList()));
return ret;
}
}
附言这些 impl 效率不高,但考虑到它们的使用场景,我更喜欢简单的 impl
p.s. these impls are not efficient, but given their usage scenarios I preferred simple impl
这篇关于如何根据 Java Spring 中的当前用户角色修剪 Swagger 文档?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何根据 Java Spring 中的当前用户角色修剪 Swagger 文档?
基础教程推荐
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01