17

I retrofitted my GWT/GXT application with basic LDAP Authorization using basic HTTP authentication. It works well when I start new browser - I get the prompt and get authorized against corporate LDAP. My problem - I can't logout unless I close/reopen the browser. I can debug and see how SecurityContextLogoutHandler#logout is called and the following code is executed

    if (invalidateHttpSession) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
    }

    SecurityContextHolder.clearContext();

However it seemingly has no effect as site is reloaded and I never get another HTTP auth prompt unless I restart the browser (even clearing the cache/cookies won't help). Here's relevant portion of applicationContext.xml

<security:http auto-config='true'>
    <security:intercept-url pattern="/reports/**" access="ROLE_USER" />
    <security:http-basic />
    <security:logout logout-url="/reports/logout" 
              logout-success-url="/reports/Application.html" />       
</security:http>

I tried to define custom LogoutSuccessHandler and do authentication.setAuthenticated(false); but that also has no effect

Anything here I'm missing here? Your help will be much appreciated

skaffman
  • 398,947
  • 96
  • 818
  • 769
Bostone
  • 36,858
  • 39
  • 167
  • 227

6 Answers6

30

OK. after spending way too much time with this I think I have the answer. It's simple - one cannot bail out of basic HTTP authentication using server-side technology. Basically authorization string is base-64 decoded in the HTTP header and when protected page is loaded to the browser the security token gets repopulated so no matter how often you erase it on the server it gets resurrected every time the page is called. I suppose it is possible to play some clever tricks on the browser side but that would be brittle and unreliable

For my case I will be switching to form-based authentication which gives much better control over login/logout process anyways.

I will hold on accepting my own answer in favor someone coming out with acceptable solution

Bostone
  • 36,858
  • 39
  • 167
  • 227
  • 1
    I'm finding the same thing (after waaay to much time), but find it really hard to believe there is nothing that can be done. – Snekse Jul 03 '12 at 22:02
4

Bostone is basically correct that you cannot handle this server-side. There is, however, a bit of a hack that's available.

The short answer is to redirect the user to the URL you want them to land on, but prefix it with a bad username followed by @. So as an ugly example,

<security:logout logout-url="/reports/logout" 
    logout-success-url="http://BADNAME@localhost/reports/Application.html" />

Ideally you'd implement a handler that would do this for you, but for illustration purposes, I think that gets the concept across.

Community
  • 1
  • 1
Snekse
  • 15,474
  • 10
  • 62
  • 77
  • 1
    Unfortunately, this no longer seem to work reliably in recent browsers, presumably due to the use of that sort of URL in phishing emails. Chrome simply ignores the username, Firefox pops up a warning that you're trying to log into a site that doesn't need a login, and Safari displays a big red Phishing Alert. – Zorac Sep 27 '16 at 14:06
3

Uhm... strange. I don't see anything wrong with your configuration. You really don't need to define any custom logout handler because it should be handled by Spring Security.

Try this, instead of defining your logout-url, use the default logout link:-

...
<security:logout logout-success-url="/reports/Application.html" />      
...

Then, use /j_spring_security_logout to logout.

Does this work at all?

limc
  • 39,366
  • 20
  • 100
  • 145
  • Not surprisingly this has the same effect. As I said - it goes through LogoutHandler logic it's just that it seemingly does nothing – Bostone Feb 17 '11 at 01:03
  • There's nothing wrong with my wiring. The truth is - basic HTTP does not support logout – Bostone Feb 17 '11 at 18:40
1

One way I've found to do this is to return HTTP 401 responses. Some quick testing shows this works in the macOS versions of Safari, Chrome & Firefox. The code I use is essentially equivalent to this:

@RequestMapping(value="/logout",method=RequestMethod.POST)
public void logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    session.setAttribute(LOGOUT_SESSION_KEY, true);
    response.setStatus(303);
    response.addHeader("Location", URL_OF_APPLICATION_HOME_PAGE);
}

@RequestMapping("/")
public void home(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
    if (Boolean.TRUE.equals(session.getAttribute(LOGOUT_SESSION_KEY))) {
        if (request.getHeader("Authorization") != null) {
            session.invalidate();
        }

        response.setStatus(401);
        response.addHeader("WWW-Authenticate", "Basic realm=\"" + HTTP_BASIC_REALM + "\"");
        return;
    }

    /* Generate home page */
}

The trick is that you usually need to return the 401 header twice - after the first one, the browser will retry with its cached credentials (sending the "Authorization" header, at which point we destroy the user's old session), the second one seems to clear them and prompts for a login.

Zorac
  • 339
  • 4
  • 7
1

I strongly believe that you are correct. You cannot invalidate a Basic or Digest authentication w/o doing it in the browser. Even there it is going to be extremely difficult if not impossible. Frankly I can't even think of a way to do it there (other than attempting to close the browser).

As you noted, the problem is that even though you invalidate everything on the server side, you have given the browser everything that it needs to automatically generate a new authentication session on its own. The only way to clear that is to clear the browser. Even restarting your server won't clear disable the ability of the browser to create a new authentication which is a pretty good indication that you're not going to be able to accomplish this on the server side without invalidating the Basic or Digest authentication contract.

Frankly this seems like a pretty substantial security hole in browsers in that you should never leave a browser running on an open access server after authenticating into a site with basic or digest authentication.

Form authentication may be your best bet because there you are not giving the browser information on how to resurrect an authentication unless you allow a rememberme cookie. Even there, you can clear the cookie on logout so that's easier.

hockey_dave
  • 524
  • 1
  • 7
  • 18
0
<logout invalidate-session="true" logout-url="/reports/logout"
        logout-success-url="/reports/Application.html" />

just set invalidate session to 'true' and see does it works.

check if this listner is there in your web.xml:

<!-- Security: to listen session requests -->
<listener>
     <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
 </listener>

If above things do not work then you can try following solution. I think problem is with your following line,

<security:intercept-url pattern="/reports/**" access="ROLE_USER" />

Above line means that you added access ROLE_USER to yout '/reports/**' link. But after logout you are giving same url for logout success. then how it will work? Either change location of your logout success page or you can add followin line in http tag.

<intercept-url pattern="/reports/Application.html" filters="none" />
Sagar
  • 1,242
  • 7
  • 22
  • 49
  • Sorry, none of your suggestions worked. invalidate-session=true is by default, I did add listener and logout page. However I think logout page is irrelevant (it's / by default) because even if it's missing since I'm using basic HTTP login as soon as I logout I should be hit with the login prompt and that's exactly what is not happening. I can step through the logout code it is executed but it makes no difference. Unless I kill the browser I will stay logged in – Bostone Feb 17 '11 at 16:37
  • I don't know who gave this solution a positive count but it is fundamentally off the point. See my own answer – Bostone Feb 17 '11 at 18:40