Rest call on expired session: HTTP 401 response ca

2019-03-25 06:20发布

问题:

I have written a HTML 5 application that uses AngularJS and interfaces with a Java REST backend running on Tomcat. I use Spring Security to handle login and security.

When the user enters the website he is forwarded to a login page which creates a session and redirects to the index page. The REST calls that loads further data from the server then uses that session to authenticate. If there is no session in place, I fallback to basic authentication over HTTP, such that it is possible to invoke the REST endpoints separately from the web application.

The problem I have now is when the session expires. This causes a HTTP 401 Unauthenticated response from the server. I thought I can catch that error and redirect the user back to the login page with Javascript. However before my error handler is called, the browser first shows a login window, only if I click cancel my Javascript error handler can handle the response.

My question is, is there a way to prevent the browser from showing this login window? Or is this a general problem with my application design?

The alternative might be not to use a session at all and to cache username and password in the application. Then I need to send it with every REST call using basic authentication, would that be a better approach?

The following is the HTTP response from the server:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Basic realm="Spring Security Application"
Content-Type: text/html;charset=utf-8
Content-Length: 999
Date: Mon, 30 Sep 2013 11:00:34 GMT

Update: It appears that the reason for this is the WWW-Authenticate header, which causes the browser to display the login dialog.

回答1:

I finally found the solution for this. As I mentioned in my update the reason is, that the response contains the WWW-Authenticate header field. My solution was then to change the configuration of spring security to return a different header:

WWW-Authenticate: FormBased

To do this I had to implement the AuthenticaitonEntryPoint interface and manually set the header and status code in the response:

@Component( "restAuthenticationEntryPoint" )
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence( HttpServletRequest request, HttpServletResponse response,
                          AuthenticationException authException ) throws IOException {
        response.setHeader("WWW-Authenticate", "FormBased");
        response.setStatus( HttpServletResponse.SC_UNAUTHORIZED );
    }
}

then I changed the configuration of spring-security and set the entry-point-ref to point to the new class:

<http pattern="/rest/**" create-session="never" entry-point-ref="restAuthenticationEntryPoint">
    <intercept-url pattern="/rest/**" access="ROLE_USER" />
    <http-basic />
    <session-management />
</http>


回答2:

If you want to avoid changing the server and make it return WWW-Authenticate header for all other callers, you can change your client to send its request with X-Requested-With header with XMLHttpRequest value. By default, Spring Security will not to send WWW-Authenticate for such requests. (see Spring source)