Java EE的RESTful身份验证

19

我一直在花时间评估可用于在 Java EE 应用程序中进行 RESTful 用户身份验证的选项。

因此,请建议下面列出的选项是否有效,以及有关优缺点的陈述。可能是我忽略了使身份验证方法可行的详细信息。或者可能是我错过了另一个选项(再次强调,我们严格讲述 Java EE,因此没有查询身份验证等,除非可以以符合 EE 标准的方式执行)

1. DIGEST/BASIC 身份验证

 <security-constraint>
     <web-resource-collection>
        <web-resource-name>admin</web-resource-name>
        <url-pattern>/protected/*</url-pattern>
     </web-resource-collection>
     <auth-constraint>
        <role-name>admin</role-name>
     </auth-constraint>
</security-constraint>
<login-config>
    <auth-method>DIGEST/BASIC</auth-method>
    <realm-name>as-defined-secuity-realm</realm-name>
</login-config>

优点

  1. 这是一种REST友好的身份验证方式。您可以通过AJAX调用发送授权凭据。用户经过身份验证后,浏览器将随附任何请求以正确的Authorization: Basic/Digest QWxhZGRpbjpvcGVuIHNlc2FtZQ==标头。如果凭据不正确,用户将被呈现出丑陋的浏览器登录屏幕 - 如果您可以接受的话,那么BASIC/DIGEST auth就是适合您的方式。

  2. 在Digest的情况下,传递给服务器的字符串是经过MD5加密的字符串,比Basic更安全(Basic是'user:password'字符串的Base64编码),但仍然可解密。因此,在安全性方面,BASIC与FORM身份验证相当安全,DIGEST则是其中最安全的。总之,如果您的网站完全使用HTTPS(我是指完全,因为如果一些资源通过HTTP获取,例如您的授权标头,它们将对第三方可见),您可以放心使用BASIC/DIGEST。

  3. 易于设置。

缺点

  1. 注销很难实现。请参见这里这里。确保您有一个好的AJAX请求来验证用户,但是您还需要有一个“AJAX”请求来注销用户 - 触发浏览器登录窗口再次出现)。顺便说一句,不错的servlet 3.0 request.logout()方法在这种情况下无法正常工作
  2. 会话超时非常难以实现。会话过期确实会发生(这是Servlet容器的工作),但浏览器将在下一次请求中发送授权标头,从而触发重新身份验证。
  3. 没有个性化登录页面。完全没有。
  4. 难以跟踪已验证的会话。

2. 基于表单的身份验证

 <security-constraint>
     <web-resource-collection>
        <web-resource-name>admin</web-resource-name>
        <url-pattern>/protected/*</url-pattern>
     </web-resource-collection>
     <auth-constraint>
        <role-name>admin</role-name>
     </auth-constraint>
</security-constraint>
<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>as-defined-security-realm</realm-name>
    <form-login-config>
        <form-login-page>/auth/login.html</form-login-page>
        <form-error-page>/auth/error.html</form-error-page>
    </form-login-config>
</login-config>

简而言之,如果用户访问一个protected/*的url,响应中会包含登录页面,而不是用户期望看到的内容,这与form-login-page标签中配置的登录页面相对应。如果密码正确,用户将被重定向(302永久移动)到最初请求的protected/* url。如果密码错误,用户将被重定向(302永久移动)到错误页面。

优点

  1. 个性化的登录页面-似乎这是最受欢迎的:)
  2. 注销很容易实现。只需要使HttpSession无效或调用request.logout()方法(Servlet 3.0)。
  3. 会话超时
  4. 仅当您接受拥有单独的登录页面时,这才是适合您的解决方案。

缺点

  1. 不友好于REST(我不会深入探讨rest的哲学,保持服务器端状态是否符合RESTful的辩论。我们正在以JAVA EE的方式分析REST身份验证,对于任何经过身份验证的主体,服务器端状态始终得到维护)。使用FORM身份验证真正糟糕的是,人们无法在各种浏览器上实现一致的行为。这全部归因于302重定向,一些浏览器在AJAX响应函数中处理它,而其他浏览器会重定向整个页面(更改导航栏中的URL)。更多细节在这里在这里。您无法规避302重定向,所以对于您,先生,没有FORM和REST身份验证!!

3.编程身份验证

为身份验证设置一个URL。在该URL后面,您可以有一个servlet,该servlet实例化一个登录模块(JAAS方式),并调用HttpServletRequest.login(user,pass)方法以及凭据。如果登录失败,它应生成401/403响应。

您可以通过在web.xml中指定security-constraints来实现它:

<security-constraint>
     <web-resource-collection>
        <web-resource-name>admin</web-resource-name>
        <url-pattern>/protected/*</url-pattern>
     </web-resource-collection>
     <auth-constraint>
        <role-name>admin</role-name>
     </auth-constraint>
</security-constraint>

在服务器端,您只需要设置一个RESTful服务,用于验证调用者身份。以下是一些示例代码:

@Path("/auth")
@ApplicationPath("/rest")
public class AuthenticationRestFacade {

@POST
@Path("/login")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public User login(User loginInfo, @Context HttpServletRequest request) throws LoginException, ServletException {

    // nasty work-around for Catalina AuthenticatorBase to be able to 
    // change/create the session cookie 
    request.getSession();
    request.login(loginInfo.getName(), loginInfo.getPassword());

优点

  1. 个性化登录页面。
  2. AJAX/REST兼容。
  3. 注销URL(如果设置了URL)。
  4. 会话超时(容器管理)。
  5. 可以在响应中返回登录数据(用户名、电子邮件、角色、组等),非常棒,因为您不必在成功登录后进行另一个调用。

缺点

  1. 需要编写一些代码。
  2. 需要应用程序能够处理401/403响应并显示登录窗口。

总之,最好的可行选项:

  1. 如果您不关心会话超时或注销--> DIGEST
  2. 如果上述方法对您无效,并且您不需要嵌入式登录页面(或类似模态面板的页面),并且您可以接受一个单独的身份验证页面--> FORM
  3. 如果上述方法对您无效,并且您想要所有灵活性和全球兼容性,请选择编程方法。您必须定义登录/注销URL,还要确保客户端代码能够处理401/403响应(不容易)。

真的很期待你们能提出可行的替代方案。因为现在我讨厌采用编程方法。


1
也许你没有提到的一个Java EE选项是JASPIC。它在完整的Java EE 6配置文件中可用,并且可以让你在安排身份验证方面拥有很大的自由度。 - Arjan Tijms
说实话,我不会选择 JASPIC,因为它还不够成熟。顺便说一句,Arjan 写的 JASPIC 教程很好。 - victor
哇,这个问题好像没人关注。我也想知道更多。不过这确实是一个非常好的问题。 - Michael K
2个回答

3
根据我的经验,使用Java EE身份验证和授权服务实现同时适用于REST服务和服务器端MVC(如JSP或JSF)的系统很困难。我的所有经验都倾向于在MVC部分使用基于表单的身份验证,并为REST服务使用某种令牌身份验证(OAuth、Kerberos、LTPA)。对于REST服务而言,使用基于表单或基本身份验证通常很具有挑战性,尽管我们在两个项目中实现了它并且运行良好。这也取决于首选的服务器实现。

0

可能有争议这些是否符合RESTful,但至少需要讨论以下内容:

那么 Keberos 呢? 使用身份验证服务器如Windows AD...

那么 公钥证书 呢?依赖于客户端提供的证书来识别用户...

那么 Tokens 呢?第三方令牌发行人如OpenID...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接