我一直在花时间评估可用于在 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>
优点
这是一种REST友好的身份验证方式。您可以通过AJAX调用发送授权凭据。用户经过身份验证后,浏览器将随附任何请求以正确的
Authorization: Basic/Digest QWxhZGRpbjpvcGVuIHNlc2FtZQ==
标头。如果凭据不正确,用户将被呈现出丑陋的浏览器登录屏幕 - 如果您可以接受的话,那么BASIC/DIGEST auth就是适合您的方式。在Digest的情况下,传递给服务器的字符串是经过MD5加密的字符串,比Basic更安全(Basic是'user:password'字符串的Base64编码),但仍然可解密。因此,在安全性方面,BASIC与FORM身份验证相当安全,DIGEST则是其中最安全的。总之,如果您的网站完全使用HTTPS(我是指完全,因为如果一些资源通过HTTP获取,例如您的授权标头,它们将对第三方可见),您可以放心使用BASIC/DIGEST。
- 易于设置。
缺点
- 注销很难实现。请参见这里和这里。确保您有一个好的AJAX请求来验证用户,但是您还需要有一个“AJAX”请求来注销用户 - 触发浏览器登录窗口再次出现)。顺便说一句,不错的servlet 3.0 request.logout()方法在这种情况下无法正常工作。
- 会话超时非常难以实现。会话过期确实会发生(这是Servlet容器的工作),但浏览器将在下一次请求中发送授权标头,从而触发重新身份验证。
- 没有个性化登录页面。完全没有。
- 难以跟踪已验证的会话。
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永久移动)到错误页面。
优点
- 个性化的登录页面-似乎这是最受欢迎的:)
- 注销很容易实现。只需要使HttpSession无效或调用request.logout()方法(Servlet 3.0)。
- 会话超时
- 仅当您接受拥有单独的登录页面时,这才是适合您的解决方案。
缺点
- 不友好于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());
优点
- 个性化登录页面。
- AJAX/REST兼容。
- 注销URL(如果设置了URL)。
- 会话超时(容器管理)。
- 可以在响应中返回登录数据(用户名、电子邮件、角色、组等),非常棒,因为您不必在成功登录后进行另一个调用。
缺点
- 需要编写一些代码。
- 需要应用程序能够处理401/403响应并显示登录窗口。
总之,最好的可行选项:
- 如果您不关心会话超时或注销--> DIGEST
- 如果上述方法对您无效,并且您不需要嵌入式登录页面(或类似模态面板的页面),并且您可以接受一个单独的身份验证页面--> FORM
- 如果上述方法对您无效,并且您想要所有灵活性和全球兼容性,请选择编程方法。您必须定义登录/注销URL,还要确保客户端代码能够处理401/403响应(不容易)。
真的很期待你们能提出可行的替代方案。因为现在我讨厌采用编程方法。