Java 昂首阔步+;Spring安全性-基于角色隐藏方法

Java 昂首阔步+;Spring安全性-基于角色隐藏方法,java,spring-security,swagger,spring-rest,springfox,Java,Spring Security,Swagger,Spring Rest,Springfox,我有一个API,它有不同的使用者。我希望他们根据在Spring Security中的角色获得相关文档 例如 API操作A被压缩为角色A和角色B API操作B被限制为角色B API操作C对所有人开放 我用的是SpringFox,Spring4,SpringRest,安全 我知道有一个名为@apignore的注释,也许可以利用它 这可能吗?您可能已经看到了这一点,但SpringFox本身提供了配置安全性的机制。请参见SpringFox官方文档中的,并获取一个示例(注意第14点和第15点) 如果您允许

我有一个API,它有不同的使用者。我希望他们根据在Spring Security中的角色获得相关文档

例如

API操作A被压缩为角色A和角色B

API操作B被限制为角色B

API操作C对所有人开放

我用的是SpringFox,Spring4,SpringRest,安全

我知道有一个名为
@apignore
的注释,也许可以利用它


这可能吗?

您可能已经看到了这一点,但SpringFox本身提供了配置安全性的机制。请参见SpringFox官方文档中的,并获取一个示例(注意第14点和第15点)

如果您允许不同的用户查看API,但仍然无法执行API,您可以考虑在适当的角色上添加注释。 例如:

@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)
,以便@securited正常工作

大宗报价 您可以在安全配置文件中使用下面的代码段,并且需要扩展GlobalMethodSecurity配置

@自动连线 auth2server配置auth2server配置

 @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
  }

经过一段时间的搜索,我发现在网络上没有办法解决这个问题。所以我用我自己的解决方案解决了它

我编写了一个过滤器来修改响应并删除用户无权访问的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);
}
request.isUserInRole("Role_A");
要修改响应,应遵循以下步骤

然后我们需要细化生成的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();
}

我已经发布了类似的问题,并在一点时间内找到了解决方案。因为我在stackoverflow上发现了3个类似的问题,我不知道是应该在所有问题中复制粘贴答案,还是提供指向我答案的链接

解决方案由两部分组成:

  • 通过
    OperationBuilderPlugin
    扩展控制器扫描逻辑,以保留Swagger供应商扩展中的角色
  • 重写
    ServiceModelToSwigger2Mapperimpl
    bean以根据当前安全上下文筛选出操作

  • 详细信息可在此处找到:

    谢谢您的回答,但这并不能解决我的问题。我已经用角色保护了我的应用程序,我希望文档能够考虑到这一点。@EspenSchulstad您找到解决问题的方法了吗?ThanksHow对您的用例是否有用?它只是阻止显示某些资源。。。如果您使用带表的Spring安全模型来存储角色、用户、权限等,则可以创建一个端点来检索此数据。将所有这些作为资源创建为一个大摇大摆的映射将不起作用,因为您需要真正的Rest注释围绕它们。我的意思是,也许我可以编写自己的注释,在角色不存在的地方使用它们。或者类似的。你找到解决方案了吗?我们最终为每个集成器提供了不同的文档。因为这只是一个生成大摇大摆文档的问题,所以做了一些创造性的脚本编写。可怕的解决方案,但至少解决了。这能回答你的问题吗?还有一种替代方法不涉及修改json对象。看看这个答案: