我正在使用Jersey在Java中编写RESTful应用程序,我需要对用户进行身份验证。我知道我可以使用注释@RolesAllowed在资源中指定角色,但我无法理解用户如何与特定角色相关联。客户端以这种方式发送用户名和密码
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(user, password);
Client client = ClientBuilder.newClient();
client.register(feature);
WebTarget target = client.target(baseUrl).path(urlString);
Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON);
Response response = invocationBuilder.get();
假设某些方法只能由超级用户和其他用户使用,当客户端发送用户名和密码时,如何区分它们?
答案 0 :(得分:9)
我知道我可以使用注释@RolesAllowed在资源中指定角色,但我无法理解用户如何与特定角色相关联
角色信息存储在DB中。假设你有一个User
来建模数据库中的USER和ROLES表
class User {
String username;
List<String> roles;
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public List<String> getRoles() { return roles; }
public void setRoles(List<String> roles) { this.roles = roles; }
}
你会在泽西过滤器中得到User
。这也是您要进行身份验证的地方。
@Provider
@Priority(Priorities.AUTHENTICATION) // needs to happen before authorization
class AuthenticationFilter implements ContainerRequestFilter {
@Inject
private UserService userService; // this is your own service
@Override
public void filter(ContainerRequestFilter filter) {
// note, this is a lazy implementation of Basic auth.
// it doesn't do ant error checking. Please see
// link at bottom for better imlementation
String authzHeader = filter.getHeaderString(HttpHeaders.AUTHORIZATION); // (1)
String decoded = Base64.decodeAsString(authzHeader);
String[] split = decoded.split(":");
User user = userService.getUser(split[0]); // (2)
if (user == null || !user.getPassword().equals(someHash(split[1])) { // (3)
throw new UnauthorizedException();
}
SecurityContext oldContext = filter.getSecurityContext(); // (4)
filter.setSecurityContext(new BasicSecurityConext(user, oldContext.isSecure()));
}
}
你在这里做的是:
User
SecurityContext
。 BasicSecurityContext
如下所示。您可以在此处将角色与用户关联。
static class BasicSecurityContext implements SecurityContext {
private final User user;
private final boolean secure;
public BasicSecurityContext(User user, boolean secure) {
this.user = user;
this.secure = secure;
}
@Override
public Principal getUserPrincipal() {
return new Principal() {
@Override
public String getName() {
return user.getUsername();
}
};
}
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
@Override
public boolean isSecure() { return secure; }
@Override
public boolean isUserInRole(String role) {
return user.getRoles().contains(role);
}
}
如果你看isUserInRole
的底部。会发生什么是Jersey会从资源方法或类中获取@RolesAllowed
注释,获取值,然后将它们传递给isUserInRole
。如果它返回true
,则用户被授权。在伪代码中
@RolesAllowed({"USER", "SUPER_USER"})
public Response get() {}
...
RolesAllowed annotation = resourceMethod.getAnnotation(RolesAllowed.class);
String roles = annotation.value();
SecurityContext context = getSecurityContext();
for (String role: roles) {
if (context.isUserInRole(role)) {
return;
}
}
throw new ForbiddenException();
这只是伪代码,但它显示了Jersey如何处理授权,使用@RolesAllowed
,SecurityContext
以及如何实现isUserInRole
。
此授权功能不会自动启用。你需要自己打开它。为此,只需注册RolesAllowedDynamicFeature
public JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(RolesAllowedDynamicFeature.class);
}
}
这里要注意的一点是,在上述所有方面,我们正在实施基本身份验证和安全上下文的设置。这没有什么不妥。但是如果您使用servlet容器身份验证机制,Jersey实际上会从HttpServletRequest
获取身份验证信息。 HttpServletRequest
具有getUserPrincipal()
方法和isUserInRole
方法。泽西岛将使用这些代表SecurityContext
。因此,如果您是用户的容器身份验证,那么您真的不需要实现任何东西。您只需注册RolesAllowedDynamicFeature
如果您想使用容器的身份验证机制,请查阅服务器的文档。在您的服务器中设置了一个领域后,您将需要使用安全信息配置web.xml
。以下链接中有一个示例。您还应该在Web安全性部分的Java EE文档中找到此信息。
另见:
答案 1 :(得分:2)
我们需要解决两件事
要进行身份验证和授权,我们需要一个存储的数据存储,它存储以下映射:
此处验证需要第一个映射,其他两个映射用于授权。
另请注意,我们需要为每个API调用执行身份验证和授权。所以我们将进行大量的读操作。
因此,通常使用目录服务器或Ldap服务器(如Apache DS)来存储这些映射,因为目录服务器是读取优化的数据存储。
在RESTful应用程序中,通常使用过滤器从请求标头中提取用户名和密码,并使用Ldap服务器进行身份验证。如果验证成功,则下一步是通过查询用户角色和角色权限映射来从Ldap服务器提取用户的权限。如果用户已获得授权,则只有在这种情况下,控制才会流向实际的API业务逻辑。
有关详细信息,请参阅this答案。
答案 2 :(得分:0)
HttpAuthenticationFeature类提供HttpBasic和Digest客户端身份验证功能。该功能以4种模式之一工作;
BASIC:这是抢占式身份验证方式,即始终随每个HTTP请求发送信息。此模式必须与SSL / TLS的使用相结合,因为密码仅发送BASE64编码。
BASIC NON-PREEMPTIVE:这是非抢占式身份验证方式,即仅当服务器拒绝具有401状态代码的请求时才添加身份验证信息,然后使用身份验证信息重复请求。
DIGEST: Http摘要身份验证。不需要使用SSL / TLS。
UNIVERSAL:非抢占模式下的基本身份验证和摘要身份验证的组合,即在401响应的情况下,将根据WWW-Authenticate HTTP标头中定义的请求进行适当的身份验证。< / p>
要使用HttpAuthenticationFeature,请构建它的实例并向客户端注册。例如;
1)基本身份验证模式
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic("username", "password");
final Client client = ClientBuilder.newClient();
client.register(feature);
2)基本认证:非优先模式
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basicBuilder()
.nonPreemptive()
.credentials("username", "password")
.build();
final Client client = ClientBuilder.newClient();
client.register(feature);
3)通用模式
//Universal builder having different credentials for different schemes
HttpAuthenticationFeature feature = HttpAuthenticationFeature.universalBuilder()
.credentialsForBasic("username1", "password1")
.credentials("username2", "password2").build();
final Client client = ClientBuilder.newClient();
client.register(feature);