[英]Swagger + Spring security - Hide methods based on roles
我有一个具有不同使用者的 API。 我希望他们根据他们在 Spring Security 中的角色获得相关文档。
例如
API 操作 A 被限制为角色 A 和角色 B
API 操作 B 被限制为角色 B
API 操作 C 对所有人开放
我正在使用 SpringFox、Spring 4、Spring Rest、Security
我知道有一个名为@ApiIgnore
的注释,也许可以利用它。
这是可能吗?
经过一番搜索,我发现在网络中没有办法解决这个问题。 所以我用我自己的解决方案解决了它。
我写了一个过滤器来修改响应并删除用户无法访问的 api。
过滤器是这样的:
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String url = httpServletRequest.getRequestURI();
if (url.contains("v2/api-docs")) {
CharResponseWrapper wrapper = new CharResponseWrapper((HttpServletResponse) response);
chain.doFilter(httpServletRequest, wrapper);
refineApiBaseOnACL(wrapper);
return;
}
chain.doFilter(httpServletRequest, response);
}
要修改响应,您应该点击此链接。
然后我们需要对生成的api进行细化:
private List<String> httpCommands = List.of("get", "head", "post", "put", "delete", "options", "patch");
public void refineApiBaseOnACL(CharResponseWrapper wrapper) {
try {
byte[] bytes = wrapper.getByteArray();
if (wrapper.getContentType().contains("application/json")) {
String out = refineContentBaseOnACL(new String(bytes));
wrapper.getResponse().getOutputStream().write(out.getBytes());
} else {
wrapper.getResponse().getOutputStream().write(bytes);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String refineContentBaseOnACL(String originalContent) {
JSONObject object = new JSONObject(originalContent);
JSONObject paths = object.getJSONObject("paths");
JSONArray tags = object.getJSONArray("tags");
Iterator keys = paths.keys();
Set<String> toRemovePath = new HashSet<>();
Set<Integer> toRemoveTags = new HashSet<>();
Set<String> tagSet = new HashSet<>();
while (keys.hasNext()) {
String key = (String) keys.next();
String[] split = key.split("/");
if (!getAccessHandler().checkAccessRest(split[1], split[2]))
toRemovePath.add(key);
else {
for (String httpCommand : httpCommands)
if (paths.getJSONObject(key).has(httpCommand)) {
JSONObject command = paths.getJSONObject(key).getJSONObject(httpCommand);
JSONArray tagsArray = command.getJSONArray("tags");
for (int i = 0; i < tagsArray.length(); i++)
tagSet.add(tagsArray.getString(i));
}
}
}
for (String key : toRemovePath)
paths.remove(key);
for (int i = 0; i < tags.length(); i++)
if (!tagSet.contains(tags.getJSONObject(i).getString("name")))
toRemoveTags.add(i);
List<Integer> sortedTags = new ArrayList<>(toRemoveTags);
sortedTags.sort(Collections.reverseOrder());
for (Integer key : sortedTags)
tags.remove(key);
Pattern modelPattern = Pattern.compile("\"#/definitions/(.*?)\"");
Set<String> modelSet = new HashSet<>();
Matcher matcher = modelPattern.matcher(object.toString());
while (matcher.find())
modelSet.add(matcher.group(1));
JSONObject definitions = object.getJSONObject("definitions");
Set<String> toRemoveModel = new HashSet<>();
Iterator definitionModel = definitions.keys();
while (definitionModel.hasNext()) {
String definition = (String) definitionModel.next();
boolean found = false;
for (String model : modelSet)
if (definition.equals(model)) {
found = true;
break;
}
if (!found)
toRemoveModel.add(definition);
}
for (String model : toRemoveModel) {
definitions.remove(model);
}
return object.toString();
}
在我的例子中,我有一个AccessHandler
来处理带有 url 的访问控制。 你应该在你的逻辑上写这个部分。 对于 spring 安全角色,您可以使用以下内容:
request.isUserInRole("Role_A");
我已经发布了类似的问题,并很快找到了解决方案。 由于我在 stackoverflow 上发现了 3 个类似的问题,因此我不知道是否应该在所有这些问题中复制粘贴答案,或者提供指向我的答案的链接。
解决方案由两部分组成:
OperationBuilderPlugin
扩展控制器扫描逻辑以保留 Swagger 供应商扩展中的角色ServiceModelToSwagger2MapperImpl
bean 以根据当前安全上下文过滤掉操作详细信息可以在这里找到: https : //stackoverflow.com/a/61860729/285060
Blockquote 您可以在安全配置文件中使用以下代码片段,并且需要扩展 GlobalMethodSecurityConfiguration。
@Autowired Auth2ServerConfiguration auth2ServerConfiguration;
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
在API的使用下面的代码如下
@PreAuthorize("hasRole('ROLE_ADMIN') and hasRole('ROLE_USER')")
@Transactional(readOnly = true)
public @ResponseBody ModelAndView abc() {
//do something
}
您可能已经看到了这一点,但 SpringFox 本身提供了配置安全性的机制。 请参见本节的官方SpringFox文档中,并且这部分的一个例子(注意点#14和#15)。
如果您愿意允许不同的消费者查看 API,但仍然无法执行 API,则可以考虑在具有适当角色的 API 上添加@Secured注释。
例如:
@Secured ({"ROLE_A", "ROLE_B")
@RequestMapping ("/open/to/both")
public String operationA() {
// do something
}
@Secured ("ROLE_B")
@RequestMapping ("/open/to/b/only")
public String operationB() {
// do something
}
// No @Secured annotation here
@RequestMapping ("/open/to/all")
public String operationC() {
// do something
}
确保您已在SecurityConfig
类(或您拥有的任何类@EnableGlobalMethodSecurity (securedEnabled = true)
中添加了@EnableGlobalMethodSecurity (securedEnabled = true)
以使 @Secured 工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.