After a bit of searching I found there are no ways offer for this problem in web. So I solved it with my own solution.
I wrote a filter that modify the response and remove the apis which the user has no access to them.
The filter is something like this:
@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);
}
To modify the response you should follow this link .
Then we need to refine the generated 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();
}
In my case I have a AccessHandler
which handles the access control with the url. You should write this section on your logic.
For the spring security roles you can use something like this:
request.isUserInRole("Role_A");
@ApiIgnore
be useful to your use case? It just prevents some resources from being displayed... If you're using Spring Security model with tables to store the roles, users, permissions, etc. you can create an endpoint to retrieve this data. Create all this as resources as a Swagger mapping wouldn't work bacause you need truly Rest annotations around them. – Diacid