我想将Jersey的DeclarativeLinking功能与其RolesAllowed功能结合起来。

我能够将链接成功地注入到响应中,但是该注入没有注意该端点的RolesAllowed注释。

例如,假设有两个用户。称他们为admin和user。两者都可以访问GET资源,但是只有管理员可以访问DELETE。

如果资源方法受@RolesAllowed保护,那么我希望没有该角色的用户不会注入该端点。事实并非如此。

我遵循了发现here的示例。

以下是出于说明目的而正在研究的项目的简化版本。

对于管理员,我希望使用以下json,

{
    "id" : 1
    "value" : "someValue",
    "links" : [
        {
            "href": "http://localhost/context/model/1",
            "rel": "self",
            "type": "GET"
        },
        {
            "href": "http://localhost/context/model/1",
            "rel": "delete",
            "type": "DELETE"
        },
    },
}

这正是我得到的。不幸的是,对于没有访问该端点所需的正确角色的用户,我也会收到此响应。仍然会向用户提供指向删除端点的链接,但是由于RolesAllowed,该用户将收到403。

模型
public class Model {

private int id;
private String value;
//constructor/getters/setters

模型表示
public class ModelRep {
    @XmlPath(".") //Allows me to flatten json so that links and document are at the same level
    private Model model;

    @InjectLinks({
        @InjectLink(
            rel = "self",
            type = "GET",
            resource = ModelResource.class,
            method = "getModel",
            bindings = @Binding(name = "id", value = "${instance.id}"),
            style = Style.ABSOLUTE
        ),
        @InjectLink(
            rel = "delete",
            type = "DELETE",
            resource = ModelResource.class,
            method = "deleteModel",
            bindings = @Binding(name = "id", value = "${instance.id}"),
            style = Style.ABSOLUTE
        )
    })
    @XmlJavaTypeAdapter(LinkAdapter.class)
    private List<Link> links;
    //constructor/getters/setters

模型资源
@Path("/model")
@Produces(MediaType.APPLICATION_JSON)
public class ModelResource {

    @GET
    @Path("/{id}")
    public Response getModel(@PathParam("id") int id) {
        Model m = dao.get(id);
        ModelRep mr = new modelRep(m);
        GenericEntity<ModelRep> ent = new GenericEntity<ModelRep>(mr) {};
        return Response.status(Status.OK).entity(ent);
    }

    @DELETE
    @Path("/{id}")
    @RolesAllowed(Roles.ADMIN)
    public Response deleteModel(@PathParam("id") int id) {
        dao.delete(id);
        return Response.status(Status.NO_CONTENT);
    }

问题
有什么方法可以基于RolesAllowed实现条件声明性链接?

我知道InjectLink批注存在一个条件子句,但是允许的值是实例(ModelRep),实体(我认为是响应对象),资源(ModelResource)。

我不知道使用任何一种方法来检查权限的方法。

非常感谢任何建议

最佳答案

所以我最终找到了一个有点棘手但很好用的解决方案。

每个资源都扩展了基本资源类别

@Path("/model")
@Produces(MediaType.APPLICATION_JSON)
public class ModelResource extends BaseResource {

BaseResource定义一个公共方法,该方法返回是否使用RolesAllowed注释限制当前资源的特定方法。
public abstract class BaseResource {

    @Context
    private SecurityContext security;

    public boolean isMethodRoleProtected(String sMethod) {
        Method[] methods = this.getClass().getDeclaredMethods();
        for (Method m : methods) {
            if (m.getName().equalsIgnoreCase(sMethod)) {
                Annotation[] annotations = m.getAnnotations();
                for (Annotation a : annotations) {
                    if (a instanceof RolesAllowed) {
                        String[] roles = ((RolesAllowed) a).value;
                        return userHasRole(roles);
                    }
                }
            }
        }
        return false;
    }

    protected boolean userHasRole(String... roles) {
        for (String role : roles) {
            if (security.isUserInRole(role)) {
                return true;
            }
        }
        return false;
    }
}

最后,InjectLink批注将如下所示:
@InjectLink(
    rel = "self",
    type = "GET",
    resource = SomeResource.class,
    method = "someMethod",
    bindings = @Binding(name = "id", value = "${instance.id}"),
    style = Style.ABSOLUTE,
    condition = "${resource.isMethodRoleProtected(someMethod)}"
),

“资源”是扩展BaseResource的当前端点类的实例。因此,仅当当前用户具有该方法的RolesAllowed注释中存在的角色时,才会注入此链接。

此解决方案需要SecurityContext的实现。

如果还有其他人有更好/更干净的解决方案,或者有一种方法可以改善我找到的解决方案,我仍然很乐意听到,并将相应地调整接受的答案。干杯

10-05 23:09