作者:易中天我最爱 | 来源:互联网 | 2023-08-29 08:46
I'm new to Restlet and REST in general and want to implement a RESTful API for a running server / database. Routing and addressing seems to work fine so far but I'll need a few hints at how to handle authentication and authorization.
我一般都是Restlet和REST的新手,并希望为正在运行的服务器/数据库实现RESTful API。到目前为止,路由和寻址似乎工作正常,但我需要一些关于如何处理身份验证和授权的提示。
The situation: There are some resources with which only some users can interact in only some ways. For example, User1 might be able to GET a resource, but not PUT anything, while User2 can do both, User3 might not even read it, and User4 ist the only one allowed to use DELETE.
情况:有些资源只有部分用户可以通过某种方式进行交互。例如,User1可能能够获取资源,但不能PUT任何东西,而User2可以同时执行这两项操作,User3甚至可能不会读取它,而User4是唯一允许使用DELETE的用户。
There is, of course, the MethodAuthorizer that sounded promising but it seems that it only discriminates between anonymous and (all) authenticated users. A RoleAuthorizer, on the other claw, won't distinguish between GET, PUT or other request methods, only between resources.
当然,MethodAuthorizer听起来很有希望,但它似乎只能区分匿名用户和(所有)经过身份验证的用户。另一方面,RoleAuthorizer不会仅在资源之间区分GET,PUT或其他请求方法。
How would I go about authorizing only certain users to do only certain tasks? Is there a way to combine Authorizers, or have them execute multiple tests? Do I have to write a custom Authorizer (how would I do that)?
我如何仅授权某些用户只执行某些任务?有没有办法组合授权人,或让他们执行多个测试?我是否必须编写自定义授权程序(我该怎么做)?
Also, would it be possible to use the credentials given to an Authenticator somewhere else, for example by propagating them as Strings to another method? (How) can you get the Identifier and Secret of a current request?
此外,是否可以使用在其他地方为Authenticator提供的凭据,例如将它们作为字符串传播到另一个方法? (如何)你能获得当前请求的标识符和秘密吗?
1 个解决方案
In fact, I think that you should leverage the role support of Restlet. In fact, Restlet provides two additional elements regarding security:
事实上,我认为你应该利用Restlet的角色支持。事实上,Restlet提供了两个有关安全性的附加元素:
Here is a sample for a basic authentication:
以下是基本身份验证的示例:
@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;
}
The implementation of the Verifier
looks like this:
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;
}
}
}
The implementation of the Enroler
looks like this:
Enroler的实现如下:
public class MyEnroler implements Enroler {
public void enrole(ClientInfo clientInfo) {
Request request = Request.getCurrent();
User user = request.getClientInfo().getUser();
if (user!=null) {
List roles = user.getRoles();
if (roles!=null) {
for (UserRole userRole : roles) {
// example of role creation
Role role = new Role(userRole.getName(), "");
clientInfo.getRoles().add(role);
}
}
}
}
}
Then within the resources, you can check the roles available within the Restlet request to determine if the authenticated user is allowed to execute the method:
然后在资源中,您可以检查Restlet请求中可用的角色,以确定是否允许经过身份验证的用户执行该方法:
public MyServerResource extends ServerResource {
private boolean hasRole(String expectedRole) {
List 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");
}
}
This approach is a bit intrusive. You could also have something more general but based on the HTTP method used and roles. For this, we need to implement a custom Authorizer
and register it like this:
这种方法有点干扰。您还可以使用更一般的东西,但基于使用的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;
}
The implementation of this Authorizer
could be something like that:
这个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 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;
}
}
Hope it helps you, Thierry
希望它对你有帮助,蒂埃里