java - 有没有办法根据用户的角色和@RolesAllowed 获取用户有权访问的所有 Jersey 网址的列表?

标签 java rest jersey-2.0

我希望向客户端提供休息 API 调用(通过普通 Jersey ,而不是 spring),以根据他们在 header 中发送的 JWT 返回特定用户允许的所有端点的列表。我在 stackoverflow(感谢贡献者!)示例代码中找到了获取所有端点的示例代码,无论角色如何,但不是基于角色的子集。我也找到了如何获取每个方法的注释,但希望避免重新发明“if @PermitAll 而不是 @DenyAll,或 RolesAllowed 中的角色等......”的轮子。

Jersey 2.0 是否有一个我可以调用的方法,该方法将在给定 SecurityContext 和 url 端点或方法的情况下解析为 true/false?

boolean allowed = isMethodAllowed(SecurityContext ctx, String url);

或者

boolean allowed = isMethodAllowed(SecurityContext ctx, 类方法);

最佳答案

感谢这篇文章:Listing all deployed rest endpoints (spring-boot, jersey)

特别是 Johanne Jander 的帖子(谢谢!),我想出了下面的代码,它似乎适用于我的情况,一个简单的 Jersey 用例。在此提供,以防对其他人有用。

@Path("/v1/userinterface")
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public class MyUI extends MyRestApi {

    private final static Logger log = LoggerFactory.getLogger(MyUI.class);

    @Context
    private Configuration configuration;

    @Context
    private SecurityContext security;

    @Path("/allowedendpoints")
    @GET
    @Operation(summary = "List API access points allowed for the currently authenticated user", tags = {
        "ui" }, description = "Returns a list of urls", responses = {})
    public Response showAll(@Context UriInfo ui) {

    log.debug("Get list of all allowed endpoints for user: " + security.getUserPrincipal().getName());

    HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();

    for (Class<?> c : configuration.getClasses()) {
        // Since all of my endpoint classes extend MyRestApi,
        // only scan them, not all classes
        if (MyRestApi.class.isAssignableFrom(c)) {
        scanClass(c, map);
        }
    }

    return Response.ok().entity(map).build();
    }

    public void scanClass(Class<?> baseClass, HashMap<String, ArrayList<String>> map) {
    Builder builder = Resource.builder(baseClass);
    if (null != builder) {
        Resource resource = builder.build();
        String uriPrefix = "";
        process(uriPrefix, resource, map);
    }
    }

    private void process(String uriPrefix, Resource resource, HashMap<String, ArrayList<String>> map) {

    // recursive method

    String pathPrefix = uriPrefix;
    List<Resource> resources = new ArrayList<>();
    resources.addAll(resource.getChildResources());

    if (resource.getPath() != null) {
        pathPrefix = (pathPrefix + "/" + resource.getPath()).replaceAll("//", "/");
    }

    for (ResourceMethod method : resource.getAllMethods()) {
        if (method.getType().equals(ResourceMethod.JaxrsType.SUB_RESOURCE_LOCATOR)) {
        resources.add(Resource
            .from(resource.getResourceLocator().getInvocable().getDefinitionMethod().getReturnType()));
        } else {
        if (isPathAllowed(security, method.getInvocable().getDefinitionMethod())) {
            if (map.containsKey(pathPrefix))
            map.get(pathPrefix).add(method.getHttpMethod());
            else
            map.put(pathPrefix, new ArrayList<String>(Arrays.asList(method.getHttpMethod())));
        }
        }
    }

    for (Resource childResource : resources) {
        process(pathPrefix, childResource, map);
    }
    }

    public boolean isPathAllowed(SecurityContext ctx, Method method) {

    // @DenyAll on the method takes precedence over @RolesAllowed and @PermitAll
    if (method.isAnnotationPresent(DenyAll.class)) {
        return (false);
    }

    // @RolesAllowed on the method takes precedence over @PermitAll
    RolesAllowed rolesAllowed = method.getAnnotation(RolesAllowed.class);
    if (rolesAllowed != null) {
        return (hasRole(ctx, rolesAllowed.value()));
    }

    // @PermitAll on the method takes precedence over @RolesAllowed on the class
    if (method.isAnnotationPresent(PermitAll.class)) {
        return (true);
    }

    // @DenyAll can't be attached to classes

    // @RolesAllowed on the class takes precedence over @PermitAll on the class
    rolesAllowed = method.getDeclaringClass().getAnnotation(RolesAllowed.class);
    if (rolesAllowed != null) {
        return (hasRole(ctx, rolesAllowed.value()));
    }

    // @PermitAll on the class
    if (method.getDeclaringClass().isAnnotationPresent(PermitAll.class)) {
        return (true);
    }

    return (false); // default
    }

    private boolean hasRole(SecurityContext ctx, String[] rolesAllowed) {

    for (final String role : rolesAllowed) {
        if (ctx.isUserInRole(role)) {
        return (true);
        }
    }

    return (false);
    }

它根据带有 @DenyAll、@PermitAll 和 @RolesAllowed 注释的 SecurityContext 标记返回当前经过身份验证的用户有权访问的端点。

完全公开,我的应用程序很简单,在端点处只有基本的类和方法注释。 ymmv。

示例输出:

{
   "/v1/resources" : [
      "POST",
      "GET"
   ],
   "/v1/resources/{id}" : [
      "DELETE",
      "GET"
   ]
}

关于java - 有没有办法根据用户的角色和@RolesAllowed 获取用户有权访问的所有 Jersey 网址的列表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55882717/

相关文章:

java - 构建 SNAPSHOT 依赖项时如何触发具有多个分支的 jenkins maven 作业?

java - Jersey 2 + HK2 - @ApplicationScoped 不工作

java - 如何从 ContainerRequestContext 获取表单数据参数

java - Android Activity 未完成并且不启动之前的 Activity

java - 什么是供应商在谈论 java 时?

java - 在不使用断言的情况下对此方法进行 Junit 测试?

api - 当 URI 可以动态解析时,路由器有什么好处?

python - Rest API 应用程序在 'Sandbox' 但不是 'Live' 工作

json - 如何修改我的代码以返回自定义 JSON 而不仅仅是状态 200?

Jersey 预匹配过滤器 : Adding parameters to modified URI