如何在会话无效后强制 Jetty 要求使用 BASIC 身份验证的凭据? [英] How to force Jetty to ask for credentials with BASIC authentication after invalidating the session?

查看:25
本文介绍了如何在会话无效后强制 Jetty 要求使用 BASIC 身份验证的凭据?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用带有 BASIC 身份验证的 jetty 6.1.22 作为我的登录机制.我第一次登录 Web 应用程序时,浏览器会要求输入用户名和密码.

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.

如果它尝试使用 session.invalidate() 注销,会话将失效,但凭据会被缓存.这意味着,如果我尝试连接到安全的 URL,我将看到不同的会话 ID,但没有用户名和密码对话框.

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.

推荐答案

(我意识到这个问题很老,但其他人可能想要答案)

BASIC Authentication 并不能真正满足您的要求,但是如果您愿意忍受一些怪癖",您可以获得一些与您想要的有点类似的东西.

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 身份验证链接到 Java 会话变得困难.这就是为什么备用登录机制(如 Java FORM 登录存在)

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 身份验证凭据链接到 cookie(它主要控制 java 会话)
会发生什么,浏览器将简单地随每个请求发送用户名+密码,直到它决定停止(通常是因为浏览器已关闭,并且创建了新的浏览器会话).
浏览器不知道服务器正在使用凭据做什么.它不知道是否不再需要它们,也不知道服务器端何时决定新会话"已经开始,它只是不断地随每个请求发送用户名+密码.

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 every request, 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 身份验证是客户端
用户/密码的对话框由客户端处理.服务器说的是您请求的 URL 需要用户名+密码,我们将此安全领域命名为‘xyz’".
服务器不知道用户是否每次都在输入密码,或者客户端是否已经缓存了它们.它不知道那里是否真的有用户,或者密码是否从文件中提取出来.
服务器唯一能做的就是说你需要在访问这个 URL 之前给我一个用户+密码"

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"

如何伪造
本质上,您需要检测(即做出有根据的猜测)浏览器何时发送旧凭据,然后再次发送请给我用户+密码"响应 (HTTP 401).

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.

最简单的方法是在您的应用程序中设置一个过滤器,用于检测用户第一次登录该会话的时间,并发送 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 401 response code. Something like:

    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;
    }

在那个例子中,我用会话的创建时间命名了领域(虽然它不是很漂亮 - 您可能希望以不同的方式对其进行格式化).这意味着每次使会话无效时,安全领域的名称都会更改,这有助于防止客户端感到困惑(为什么它再次要求我为同一个领域提供用户+密码,而我刚刚提供了一个?).
新的领域名称不会混淆 servlet 容器,因为它永远不会看到它 - 客户端响应不包含领域名称.

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.

诀窍在于,您不希望用户在第一次登录时被要求输入密码两次.而且,开箱即用,此解决方案将执行此操作 - 一次让容器请求它,然后当您的过滤器请求它时再次请求.

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.

如何避免出现 2 个登录框有 4 个选项.

  1. 在代码中完成所有工作.
  2. 使用辅助会话 cookie 尝试判断用户是否是第一次登录.
  3. 让你的根 URL 不安全(但什么都不做)
  4. 让您的所有应用不安全,除了 1 个页面,如果用户未登录,则将用户重定向到该页面.

在代码中实现

如果您乐于在自己的 servlet/过滤器中完成所有安全工作,并且没有从 servlet 容器 (Jetty) 获得帮助,那么这并不太难.您只需在 web.xml 中关闭 BASIC auth,并在代码中完成所有操作.(有很多工作要做,您需要确保不会留下任何安全漏洞,但这在概念上很容易)
大多数人不想这样做.

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.

辅助 Cookie
用户登录到您的应用程序后,您可以设置一个在浏览器会话结束时过期的 cookie(已验证").此 cookie 与浏览器会话相关联,与 Java 会话相关联.
当您使 Java 会话无效时 - 不要使 已验证" cookie 无效.
如果用户登录到全新 java 会话(即session.getAttribute("auth")==null),但仍然具有此已认证" 那么您就知道他们正在重用现有的浏览器会话,并且可能重用现有的 HTTP 身份验证凭据.在这种情况下,您可以使用 401 技巧来强制他们提供新凭据.

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 not to the Java session.
When you invalidate the Java session - do not invalidate the "authenticated" cookie.
If a user logs in to a fresh java 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 probably re-using existing HTTP authentication credentials. In that case you do the 401 trick to force them to give new credentials.

不安全的根 URL
如果您的根 URL 不安全,并且您知道这是您的用户始终登录的 URL,那么您只需在该 URL 上放置auth"/401 检查,它就会解决问题.但请确保您不会意外打开安全漏洞.
除了将用户重定向到真实"应用(受保护)外,此 URL 不应具有任何功能

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"/401 check 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)

单一安全网址
有 1 个受保护的 URL(例如/login").
除了您的auth"/401 过滤器外,在所有页面(登录除外)上还有另一个过滤器(或同一过滤器中的附加代码)来检查 request.getUserPrincipal() 已设置.如果没有,将用户重定向到/login".

Single secured URL
Have 1 URL that is secured (e.g. "/login").
As well as your "auth"/401 filter, 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".

更简单的解决方案
只需使用 FORM 登录方法而不是 BASIC 身份验证.它旨在解决这个问题.

The much easier solution
Just use a FORM login method instead of BASIC authentication. It's designed to solve this problem.

这篇关于如何在会话无效后强制 Jetty 要求使用 BASIC 身份验证的凭据?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆