java - Jackson 和 Jersey 的条件属性序列化

标签 java spring jersey jackson

我未能成功尝试有条件地和动态地选择要序列化的属性以响应 Jersey(使用 Jackson)的每个请求。这背后的想法是在 REST API 中安全地访问对象的属性。

我在 API 调用中返回了几个对象,这些对象应该根据经过身份验证的用户显示/隐藏字段。

例如,假设我有一个对象 Car

public class Car implements Serializable {

    private Long id;
    private String VIN;
    private String color;

    ...
}

假设如果具有 ROLE_ADMIN 的用户通过身份验证,则应返回所有属性,但如果没有登录用户,则只需显示前两个。

我正在考虑构建一些基于注释的东西。像这样的东西:

public class Car implements Serializable {

    private Long id;
    private String VIN;

    @Secured({AccessRole.ROLE_ADMIN})
    private String color;

    ...
}

在这种情况下,只有当请求用户的访问角色与通过注释传递的访问角色匹配时,才应返回 color 属性。

但是我不知道应该在哪里实现这个逻辑。

我要实现的是一种 @JsonIgnore 但这是有条件的和动态的。到目前为止我找到的所有解决方案都是静态的。

这有可能吗?

最佳答案

Jersey 支持 Entity Filtering .除了一般过滤,它还支持Role-based Entity Filtering using (javax.annotation.security) annotations .

因此您可以在域模型属性上使用@RolesAllowed@PermitAll@DenyAll 注释

public static class Model {
    private String secured;

    @RolesAllowed({"ADMIN"})
    public String getSecured() { return this.secured; }
}

不过,要使其正常工作,您需要在请求过滤器中设置 SecurityContext。 Jersey 将查找 SecurityContext 来验证角色。您可以在 this post 中阅读更多相关信息(注意:实体过滤与该帖子中提到的任何实际授权是分开的。但该帖子确实解释了 SecurityContext)。

基本上你会有类似的东西(注意你设置 SecurityContext 的最后一行)。

@PreMatching
public static class SimpleAuthFilter implements ContainerRequestFilter {

    private static final Map<String, User> userStore = new ConcurrentHashMap<>();

    static {
        userStore.put("peeskillet", new User("peeskillet", Arrays.asList("ADMIN", "USER")));
        userStore.put("paulski", new User("paulski", Arrays.asList("USER")));
    }

    @Override
    public void filter(ContainerRequestContext request) throws IOException {
        final String authHeader = request.getHeaderString("Authorization");
        final String username = authHeader.split("=")[1];
        final User user = userStore.get(username);
        if (user == null) {
            throw new NotAuthorizedException("No good.");
        }
        request.setSecurityContext(new SimpleSecurityContext(user));
    }
}

其中 SimpleSecurityContext 只是您自己的一个类,您需要在其中覆盖 isUserInRole 方法并检查用户是否具有该角色

private static class SimpleSecurityContext implements SecurityContext {

    private final User user;

    SimpleSecurityContext(User user) {
        this.user = user;
    }

    @Override
    public Principal getUserPrincipal() {
        return new Principal() {
            @Override
            public String getName() {
                return user.getUsername();
            }
        };
    }

    @Override
    public boolean isUserInRole(String role) {
        return user.getRoles().contains(role);
    }

    @Override
    public boolean isSecure() {
        return false;
    }

    @Override
    public String getAuthenticationScheme() {
        return "simple";
    }
}

差不多就这些了。您还需要向应用程序注册 SecurityEntityFilteringFeature 以使其全部正常工作。

请参阅 this Gist 中的完整测试用例

关于java - Jackson 和 Jersey 的条件属性序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42519571/

相关文章:

java - JFrame 不显示图片

java - 如何将选项放在对象的变量上?

java - Spring 和 Hibernate3 的 @Embeddable 问题

java - 使用 Jersey 对查询数组参数进行排序

java - 当前正在运行的存储过程

java - 有没有办法在 get() set() 模型中不使用 get() 来拥有只读功能?

java - 无法设置 @Async 方法

java - Spring Bean Context 中有许多未使用的 bean 会浪费大量资源吗?

java - 如果我有两条相等的路径但其中一条有参数,Jersey 中的行为是什么?

java - Jersey RESTful 服务 500 错误(请求为空)