I have an API that has different consumers. I'd like them to get relevant documentation based on their roles in Spring Security.
E.g
API operation A is constricted to Role A and Role B
API operation B is constricted to Role B
API operation C is open for all
I'm using SpringFox, Spring 4, Spring Rest, Security
I know there is an annotation called @ApiIgnore
, which could perhaps be utilized.
Is this at all possible?
By adding this attribute on a controller or action and specifying IgnoreApi = true , it gets hidden from auto-generated documentation. However, this user has to apply this to around 80 controllers.
6. Using @ApiParam. @ApiParam is also a Swagger annotation that we can use to specify metadata related to request parameters. We can set the hidden property to true in order to hide any property.
To hide the "Models" section, add defaultModelsExpandDepth: -1 to the Swagger UI configuration code in your index. html . Note the option name uses plural Model*s* not Model . Swagger UI also has many other configuration options that control API documentation rendering.
Swagger2 is an open source project used to generate the REST API documents for RESTful web services. It provides a user interface to access our RESTful web services via the web browser. To enable the Swagger2 in Spring Boot application, you need to add the following dependencies in our build configurations file.
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");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With