按方法和用户的 Restlet 授权

Restlet Authorization by Method AND User

我是 Restlet 和 REST 的新手,想为 运行 服务器/数据库实现 RESTful API。到目前为止,路由和寻址似乎工作正常,但我需要一些有关如何处理身份验证和授权的提示。

情况:有些资源只有一些用户只能以某些方式与之交互。例如,User1 可能能够获取资源,但不能 PUT 任何东西,而 User2 可以同时执行这两项操作,而 User3 甚至可能不会读取它,而 User4 是唯一允许使用 DELETE 的人。

当然有 MethodAuthorizer 听起来很有希望,但它似乎只区分匿名用户和(所有)经过身份验证的用户。另一方面,RoleAuthorizer 不会区分 GET、PUT 或其他请求方法,只会区分资源。

我如何才能只授权某些用户只执行某些任务?有没有办法组合授权者,或者让他们执行多个测试?我是否必须编写自定义授权器(我该怎么做)?

此外,是否可以在其他地方使用提供给 Authenticator 的凭据,例如通过将它们作为字符串传播到另一种方法? (如何)获取当前请求的Identifier和Secret?

事实上,我认为您应该利用 Restlet 的角色支持。事实上,Restlet 提供了两个额外的安全元素:

  • Verifier 根据请求中提供的凭据实际对用户进行身份验证
  • Enroler 加载经过身份验证的用户的角色。

这是基本身份验证的示例:

@Override
public Restlet createInboundRoot() {
    Router router = (...)

    Verifier verify = new MyVerifier(...);
    Enroler enroler = new MyEnroler(...);

    ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(),
            ChallengeScheme.HTTP_BASIC, "connector");
    guard.setVerifier(verifier);
    guard.serEnrole(enroler);
    guard.setNext(router);

    return guard;
}

Verifier 的实现如下所示:

public class MyVerifier extends SecretVerifier {
    public int verify(String identifier, char[] secret)
                     throws IllegalArgumentException {
        ApplicationUser user = loadUser(identifier);
        //user contains both user hints and roles
        if (user!=null
              && compare(user.getPassword().toCharArray(), secret)) {
            Request request = Request.getCurrent();
            request.getClientInfo().setUser(user);
            return SecretVerifier.RESULT_VALID;
        } else {
            return SecretVerifier.RESULT_INVALID;
        }
    }
}

Enroler 的实现如下所示:

public class MyEnroler implements Enroler {
    public void enrole(ClientInfo clientInfo) {
        Request request = Request.getCurrent();
        User user = request.getClientInfo().getUser();
        if (user!=null) {
            List<UserRole> roles = user.getRoles();
            if (roles!=null) {
                for (UserRole userRole : roles) {
                    // example of role creation
                    Role role = new Role(userRole.getName(), "");
                    clientInfo.getRoles().add(role);
                }
            }
        }
    }
}

然后在资源中,您可以检查 Restlet 请求中可用的角色,以确定是否允许经过身份验证的用户执行该方法:

public MyServerResource extends ServerResource {
    private boolean hasRole(String expectedRole) {
        List<Role> roles = request.getClientInfo().getRoles();
        for (Role role : roles) {
            if (role.getName().equals(expectedRole)) {
                return true;
            }
        }
        return false;
    }

    private void checkRole(String role) {
        if (!hasRole(role)) {
            throw new ResourceException(
               Status.CLIENT_ERROR_FORBIDDEN);
        }
    }

    @Get
    public Representation getElement() {
        checkRole("read");
    }

    @Put
    public void updateElement(Representation repr) {
        checkRole("update");
    }

    @Delete
    public void deleteElement() {
        checkRole("delete");
    }
}

这种方法有点侵入性。您还可以拥有更通用的内容,但基于所使用的 HTTP 方法和角色。为此,我们需要实现一个自定义 Authorizer 并像这样注册它:

    Router router = (...)

    Authorizer authorizer = new MyAuthorizer();
    authorizer.setNext(router);

    Verifier verify = new MyVerifier(...);
    Enroler enroler = new MyEnroler(...);

    ChallengeAuthenticator guard = new ChallengeAuthenticator(getContext(),
            ChallengeScheme.HTTP_BASIC, "connector");
    guard.setVerifier(verifier);
    guard.serEnrole(enroler);
    guard.setNext(authorizer);

    return guard;
}

这个 Authorizer 的实现可能是这样的:

public class MyAuthorizer extends Authorizer {
    private String[] getRoles = new String[] { "read"};
    private String[] putRoles = new String[] { "update"};
    private String[] deleteRoles = new String[] { "delete"};

    private boolean hasRoles(String[] expectedRoles) {
        List<Role> roles = request.getClientInfo().getRoles();
        for (String expectedRole : expectedRoles) {
            for (Role role : roles) {
                if (role.getName().equals(expectedRole)) {
                    return true;
                }
            }
        }
        return false;
    }

    private void checkRoles(String[] roles) {
        if (!hasRole(roles)) {
            throw new ResourceException(
               Status.CLIENT_ERROR_FORBIDDEN);
        }
    }

    public boolean authorize(Request request, Response response) {
        if (!request.getClientInfo().isAuthenticated()) {
            throw new ResourceException(
               Status.CLIENT_ERROR_FORBIDDEN);
        }

        if ("GET".equals(request.getMethod().getName())) {
            checkRoles(getRoles);
        } else if ("PUT".equals(request.getMethod().getName())) {
            checkRoles(putRoles);
        } else if ("DELETE".equals(request.getMethod().getName())) {
            checkRoles(deleteRoles);
        }

        return false;
    }
}

希望对你有帮助, 蒂埃里