如何验证Jersey中的用户

时间:2017-02-21 17:06:20

标签: java rest jersey

我正在使用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();

假设某些方法只能由超级用户和其他用户使用,当客户端发送用户名和密码时,如何区分它们?

3 个答案:

答案 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()));
    }
}

你在这里做的是:

  1. 解析基本身份验证授权标头
  2. 使用用户名
  3. 获取User
  4. 进行身份验证
  5. 设置新的SecurityContext
  6. 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如何处理授权,使用@RolesAllowedSecurityContext以及如何实现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)

我们需要解决两件事

  1. 身份验证 - 检查用户是否真的是他声称的那个
  2. 授权 - 如果经过身份验证的用户有权访问给定方法
  3. 要进行身份验证和授权,我们需要一个存储的数据存储,它存储以下映射:

    1. 用户及其密码之间的映射
    2. 角色和用户之间的映射
    3. 角色和权限之间的映射
    4. 此处验证需要第一个映射,其他两个映射用于授权。

      另请注意,我们需要为每个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);