java 如何在会话无效后强制 Jetty 要求使用 BASIC 身份验证的凭据?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2180206/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to force Jetty to ask for credentials with BASIC authentication after invalidating the session?
提问by Tim
I'm using jetty 6.1.22 with BASIC authentication as my login mechanism. The first time I log into the web app, the browser requests the username and password.
我使用带有 BASIC 身份验证的 jetty 6.1.22 作为我的登录机制。我第一次登录 Web 应用程序时,浏览器会要求输入用户名和密码。
If it try to log out using a session.invalidate(), the session is invalidated but the credentials are cached. This means that if I try to connect to a secured URL, I will see a different session id but no dialog for username and password.
如果它尝试使用 session.invalidate() 注销,会话将失效,但凭据会被缓存。这意味着,如果我尝试连接到安全 URL,我将看到不同的会话 ID,但没有用户名和密码对话框。
回答by Tim
(I realise this question is old, but it's something that other people might want an answer to)
(我意识到这个问题已经过时了,但其他人可能想要回答这个问题)
BASIC Authentication doesn't really allow what you're asking for, but you can get something that works a bit like what you want, if you're willing to live with some "quirks".
BASIC Authentication 并不能真正满足您的要求,但是如果您愿意忍受一些“怪癖”,您可以获得一些与您想要的有点类似的东西。
BASIC Authentication has 2 aspects that make it hard to control in this way
BASIC Authentication 有两个方面使得这种方式难以控制
- It is stateless
- It is client-side
- 它是无国籍的
- 它是客户端
Both of those aspects are features, but they make it hard to link BASIC authentication to Java sessions. That is why alternate login mechanisms (like Java FORM login exist)
这两个方面都是特性,但它们使得将 BASIC 身份验证链接到 Java 会话变得困难。这就是备用登录机制(如 Java FORM 登录存在)的原因
BASIC Authentication is stateless
That means that the client has absolutely no idea what is going on on the server-side, and it certainly has no way of linking a set of BASIC Authentication credentials to a cookie (which is what mostly controls the java session)
What happens in that the browser will simply send the username+password with everyrequest, until it decides to stop (generally because the browser was closed, and a new browser session was created).
The browser doesn't know what the server is doing with the credentials. It doesn't know whether they're needed any more, or not, and it doesn't know when the server side has decided that a "new session" has started, it just keeps on sending that username+password with each request.
BASIC Authentication 是无状态的
这意味着客户端完全不知道服务器端发生了什么,它当然无法将一组 BASIC Authentication 凭据链接到 cookie(它主要控制 java 会话)
会发生什么,浏览器将简单地随每个请求发送用户名+密码,直到它决定停止(通常是因为浏览器已关闭,并且创建了新的浏览器会话)。
浏览器不知道服务器正在使用凭据做什么。它不知道是否不再需要它们,也不知道服务器端何时决定“新会话”已经开始,它只是不断地随每个请求发送用户名+密码。
BASIC Authentication is Client-Side
That dialog for the user/password is handled by the client. All the server says is "The URL you requested needs a username+password, and we've named this security realm 'xyz' ".
The server doesn't know whether the user is typing in a password each time, or whether the client has cached them. It doesn't know if there really is a user there, or whether the password was pulled out of a file.
The only thing the server can do is say "You need to give me a user+password before accessing this URL"
基本身份验证是客户端
用户/密码的对话框由客户端处理。服务器说的是“您请求的 URL 需要用户名+密码,我们将此安全领域命名为‘xyz’”。
服务器不知道用户是否每次都在输入密码,或者客户端是否已经缓存了它们。它不知道那里是否真的有用户,或者密码是否从文件中提取出来。
服务器唯一能做的就是说“你需要在访问这个 URL 之前给我一个用户+密码”
How to fake it
Essentially, you need to detect (i.e. make an educated guess) when the browser is sending old credentials and then send the "Please give me a user+password" response (HTTP 401) again.
如何伪造
本质上,您需要检测(即进行有根据的猜测)浏览器何时发送旧凭据,然后再次发送“请给我一个用户+密码”响应(HTTP 401)。
The simplest way to do that is to have a filter in your application that detects the first time the user has logged in for that session, and sends a 401response code. Something like:
最简单的方法是在您的应用程序中设置一个过滤器,用于检测用户第一次登录该会话的时间,并发送401响应代码。就像是:
if(session.getAttribute("auth") == null)
{
response.setStatus(401);
response.setHeader("WWW-Authenticate", "basic realm=\"Auth (" + session.getCreationTime() + ")\"" );
session.setAttribute("auth", Boolean.TRUE);
writer.println("Login Required");
return;
}
In that example I've named the realm with the creation time of the session (although it's not very pretty - you'd probably want to format it differently). That means that the name of the security realm changes each time you invalidate the session, which helps prevent the client from getting confused (why is it asking me for a user+password again, for the same realm, when I just gave one?).
The new realm name will not confuse the servlet container, because it will never see it - the client response does not include the realm name.
在那个例子中,我用会话的创建时间命名了领域(虽然它不是很漂亮 - 您可能希望以不同的方式对其进行格式化)。这意味着每次使会话无效时,安全领域的名称都会更改,这有助于防止客户端感到困惑(为什么它再次要求我为同一个领域提供用户+密码,而我刚刚提供了一个?) .
新的领域名称不会混淆 servlet 容器,因为它永远不会看到它 - 客户端响应不包含领域名称。
The trick though, it that you don't want the user to get asked for their password twice the first time they logon. And, out of the box, this solution will do that - once for the container to ask for it, and then again when your filter does.
诀窍是,您不希望用户在第一次登录时被要求输入密码两次。而且,开箱即用,此解决方案将执行此操作 - 一次让容器请求它,然后当您的过滤器请求它时再次请求。
How to avoid getting 2 login boxesThere are 4 options.
如何避免获得 2 个登录框有 4 个选项。
- Do it all in code.
- Use a secondary session cookie to try and tell whether the user is logging in for the first time.
- Have your root URL be unsecured (but have it do nothing)
- Have all your app be unsecured, except for 1 page, and redirect users to that page if they are not logged in.
- 在代码中完成所有操作。
- 使用辅助会话 cookie 尝试判断用户是否是第一次登录。
- 让你的根 URL 不安全(但让它什么都不做)
- 让您的所有应用程序不安全,除了 1 个页面,如果用户未登录,则将用户重定向到该页面。
Do it in code
用代码做
If you're happy to do all of the security inside your own servlet/filter, and get no help from the servlet container (Jetty) then that's not too hard. You simply turn off BASIC auth in your web.xml, and do it all in code. (There's a fair bit of work, and you need to make sure you don't leave any security holes open, but it's conceptually quite easy)
Most people don't want to do that.
如果您乐于在自己的 servlet/过滤器中完成所有安全工作,并且没有从 servlet 容器 (Jetty) 获得帮助,那么这并不太难。您只需在 web.xml 中关闭 BASIC auth,并在代码中完成所有操作。(有相当多的工作,您需要确保不会留下任何安全漏洞,但从概念上讲这很容易)
大多数人不想这样做。
Secondary Cookies
After a user logins in to your application, you can set a cookie ("authenticated") that expires at the end of the browser session. This cookie is tied to the browser session and notto the Java session.
When you invalidate the Java session - do notinvalidate the "authenticated"cookie.
If a user logs in to a freshjava session (i.e. session.getAttribute("auth")==null), but still has this "authenticated"then you know that they're re-using an existing browser session and are probablyre-using existing HTTP authentication credentials.
In that case you do the 401trick to force them to give new credentials.
次要 Cookie
用户登录到您的应用程序后,您可以设置一个在浏览器会话结束时过期的 cookie(“已验证”)。此 cookie 与浏览器会话相关,而不与 Java 会话相关。
当您使 Java 会话无效时 -不要使“经过身份验证的”cookie无效。
如果用户登录到一个新的java 会话(即session.getAttribute("auth")==null),但仍然具有此“已认证”,那么您知道他们正在重新使用现有的浏览器会话,并且可能正在重新使用现有的 HTTP 身份验证凭据。在这种情况下,您可以使用401技巧来强迫他们提供新凭据。
Unsecured root URL
If your root URL is unsecured, and you know that this is the URL that your users always login to, then you can simply put your "auth"/401check at that URL, and it will solve the problem.
But make sure you don't accidentally open a security hole.
This URL should not have any functionality other than redirecting users to the "real" app (that is secured)
不安全的根 URL
如果您的根 URL 不安全,并且您知道这是您的用户始终登录的 URL,那么您只需401在该 URL 上放置“身份验证”/检查,它就会解决问题。但请确保您不会意外打开安全漏洞。
除了将用户重定向到“真实”应用程序(即受保护的)之外,此 URL 不应具有任何功能
Single secured URL
Have 1 URL that is secured (e.g. "/login").
As well as your "auth"/401filter, have another filter (or additional code in the same filter) on all pages (other than login) that checks whether request.getUserPrincipal()is set. If not, redirect the user to "/login".
单个受保护的 URL
有 1 个受保护的 URL(例如“/login”)。
除了您的“身份验证”/401过滤器外,在所有页面(登录除外)上还有另一个过滤器(或同一过滤器中的附加代码)来检查是否request.getUserPrincipal()已设置。如果没有,将用户重定向到“/login”。
The mucheasier solution
Just use a FORM login method instead of BASIC authentication. It's designed to solve this problem.
在 很多简单的解决方案
只需使用表单登录方法,而不是基本身份验证。它旨在解决这个问题。

