wake-up-neo.com

Wie gehe ich mit einer abgelaufenen Sitzung mit spring-security und jQuery um?

Ich verwende spring-security und jQuery in meiner Anwendung. Die Hauptseite verwendet das dynamische Laden von Inhalten in Registerkarten über Ajax. Und alles ist in Ordnung, aber manchmal habe ich die Anmeldeseite in meinem Tab und wenn ich Anmeldeinformationen eingebe, werde ich zur Inhaltsseite ohne Tabs weitergeleitet.

Ich würde gerne mit dieser Situation umgehen. Ich weiß, dass einige Leute Ajax-Authentifizierung verwenden, aber ich bin nicht sicher, ob es für mich geeignet ist, da es für mich ziemlich kompliziert aussieht und meine Anwendung keinen Zugriff ohne vorherige Anmeldung zulässt. Ich möchte nur einen globalen Handler für alle Ajax-Antworten schreiben, die window.location.reload() ausführen, wenn wir authentifizieren müssen. Ich denke, in diesem Fall ist es besser, 401 error statt standard login form zu bekommen, weil es einfacher zu handhaben ist.

So,

1) Ist es möglich, einen globalen Fehlerbehandler für alle jQuery-Ajax-Anforderungen zu schreiben?

2) Wie kann ich das Verhalten von spring-security anpassen, um 401-Fehler für Ajax-Anfragen zu senden, aber für reguläre Anfragen, um die Standard-Anmeldeseite wie gewohnt anzuzeigen?

3) Haben Sie vielleicht eine elegantere Lösung? Bitte teile es.

Vielen Dank.

22
viator

Hier ist ein Ansatz, den ich für ziemlich einfach halte. Es ist eine Kombination von Ansätzen, die ich auf dieser Website beobachtet habe. Ich habe einen Blogbeitrag darüber geschrieben: http://yoyar.com/blog/2012/06/dealing-with-the-spring-security-ajax-session-timeout-problem/

Die Grundidee besteht darin, ein api-url-Präfix (d. H./Api/protected) wie oben vorgeschlagen zusammen mit einem Authentifizierungseingangspunkt zu verwenden. Es ist einfach und funktioniert.

Hier ist der Einstiegspunkt für die Authentifizierung:

package com.yoyar.yaya.config;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import Java.io.IOException;

public class AjaxAwareAuthenticationEntryPoint 
             extends LoginUrlAuthenticationEntryPoint {

    public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
        super(loginUrl);
    }

    @Override
    public void commence(
        HttpServletRequest request, 
        HttpServletResponse response, 
        AuthenticationException authException) 
            throws IOException, ServletException {

        boolean isAjax 
            = request.getRequestURI().startsWith("/api/secured");

        if (isAjax) {
            response.sendError(403, "Forbidden");
        } else {
            super.commence(request, response, authException);
        }
    }
}

Und so geht es in Ihrer XML-Frühlingszusammenfassung:

<bean id="authenticationEntryPoint"
  class="com.yoyar.yaya.config.AjaxAwareAuthenticationEntryPoint">
    <constructor-arg name="loginUrl" value="/login"/>
</bean>

<security:http auto-config="true"
  use-expressions="true"
  entry-point-ref="authenticationEntryPoint">
    <security:intercept-url pattern="/api/secured/**" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/login" access="permitAll"/>
    <security:intercept-url pattern="/logout" access="permitAll"/>
    <security:intercept-url pattern="/denied" access="hasRole('ROLE_USER')"/>
    <security:intercept-url pattern="/" access="permitAll"/>
    <security:form-login login-page="/login"
                         authentication-failure-url="/loginfailed"
                         default-target-url="/login/success"/>
    <security:access-denied-handler error-page="/denied"/>
    <security:logout invalidate-session="true"
                     logout-success-url="/logout/success"
                     logout-url="/logout"/>
</security:http>
10
Matt Friedman

Ich habe die folgende Lösung verwendet.

Im Frühjahr definierte die Sicherheit eine ungültige Sitzungs-URL 

<security:session-management invalid-session-url="/invalidate.do"/>

Für diese Seite wurde folgender Controller hinzugefügt

@Controller
public class InvalidateSession
{
    /**
     * This url gets invoked when spring security invalidates session (ie timeout).
     * Specific content indicates ui layer that session has been invalidated and page should be redirected to logout. 
     */
    @RequestMapping(value = "invalidate.do", method = RequestMethod.GET)
    @ResponseBody
    public String invalidateSession() {
        return "invalidSession";
    }
}

Und für Ajax verwendet AjaxSetup, um alle Ajax-Anforderungen zu bearbeiten:

// Checks, if data indicates that session has been invalidated.
// If session is invalidated, page is redirected to logout
   $.ajaxSetup({
    complete: function(xhr, status) {
                if (xhr.responseText == 'invalidSession') {
                    if ($("#colorbox").count > 0) {
                        $("#colorbox").destroy();
                    }
                    window.location = "logout";
                }
            }
        });
9
andro83

Werfen Sie einen Blick auf http://forum.springsource.org/showthread.php?t=95881 , ich denke, die vorgeschlagene Lösung ist viel klarer als andere Antworten hier:

  1. Fügen Sie Ihren Jquery-Ajax-Aufrufen einen benutzerdefinierten Header hinzu (mithilfe des Hooks 'beforeSend'). Sie können auch den Header "X-Requested-With" verwenden, den jQuery sendet.
  2. Konfigurieren Sie Spring Security so, dass nach diesem Header auf der Serverseite gesucht wird, um einen HTTP 401-Fehlercode zurückzugeben, anstatt den Benutzer zur Anmeldeseite zu bringen.
4
Guido

Ich habe gerade eine Lösung für dieses Problem gefunden, aber es nicht gründlich getestet. Ich benutze auch Spring, Spring Security und jQuery. Zuerst habe ich vom Controller meines Logins den Statuscode auf 401 gesetzt:

LoginController {

public ModelAndView loginHandler(HttpServletRequest request, HttpServletResponse response) {

...
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
... 
return new ModelAndView("login", model);
}

In ihren onload () -Methoden rufen alle meine Seiten eine Funktion in meiner globalen Javascript-Datei auf:

function initAjaxErrors() {

jQuery(window).ajaxError(function(event, xmlHttpRequest, ajaxOptions, thrownError) {
    if (403 == xmlHttpRequest.status)
        showMessage("Permission Denied");
    else
        showMessage("An error occurred: "+xmlHttpRequest.status+" "+xmlHttpRequest.statusText);
});

}

An dieser Stelle können Sie den 401-Fehler auf beliebige Weise behandeln. In einem Projekt habe ich die jQuery-Authentifizierung durchgeführt, indem ein jQuery-Dialogfeld um einen iframe mit einem Anmeldeformular gelegt wurde.

3
Hank

So mache ich das normalerweise. Überprüfen Sie bei jedem AJAX -Aufruf das Ergebnis, bevor Sie es verwenden.

$.ajax({ type: 'GET',
    url: GetRootUrl() + '/services/dosomething.ashx',
    success: function (data) {
      if (HasErrors(data)) return;

      // process data returned...

    },
    error: function (xmlHttpRequest, textStatus) {
      ShowStatusFailed(xmlHttpRequest);
    }
  });

Und dann sieht die Funktion HasErrors() so aus und kann auf allen Seiten geteilt werden.

function HasErrors(data) {
  // check for redirect to login page
  if (data.search(/login\.aspx/i) != -1) {
    top.location.href = GetRootUrl() + '/login.aspx?lo=TimedOut';
    return true;
  }
  // check for IIS error page
  if (data.search(/Internal Server Error/) != -1) {
    ShowStatusFailed('Server Error.');
    return true;
  }
  // check for our custom error handling page
  if (data.search(/Error.aspx/) != -1) {
    ShowStatusFailed('An error occurred on the server. The Technical Support Team has been provided with the error details.');
    return true;
  }
  return false;
}
2
Glen Little

Also gibt es hier zwei Probleme. 1) Die Frühlingssicherheit funktioniert, aber die Antwort wird in einem Ajax-Aufruf an den Browser zurückgegeben. 2) Die Frühlingssicherheit verfolgt die ursprünglich angeforderte Seite, sodass Sie nach der Anmeldung auf diese Seite weitergeleitet werden können (es sei denn, Sie geben an, dass Sie nach dem Anmelden immer eine bestimmte Seite verwenden möchten). In diesem Fall war die Anfrage eine Ajax-Zeichenfolge, sodass Sie zu dieser Zeichenfolge weitergeleitet werden. Dies wird im Browser angezeigt.

Eine einfache Lösung besteht darin, den Ajax-Fehler zu erkennen. Wenn die zurückgesendete Anforderung spezifisch für Ihre Anmeldeseite ist (Spring sendet die Anmeldeseite html zurück, wird die Eigenschaft 'responseText' der Anforderung erkannt). Dann laden Sie einfach Ihre aktuelle Seite neu, wodurch der Benutzer aus dem Kontext des Ajax-Aufrufs entfernt wird. Spring sendet sie dann automatisch an die Anmeldeseite. (Ich verwende den Standardnamen j_username, einen Zeichenfolgewert, der für meine Anmeldeseite eindeutig ist).

$(document).ajaxError( function(event, request, settings, exception) {
    if(String.prototype.indexOf.call(request.responseText, "j_username") != -1) {
        window.location.reload(document.URL);
    }
});
0
MattC

Wenn ein Timeout auftritt, wird der Benutzer zur Anmeldeseite umgeleitet, nachdem eine Ajax-Aktion ausgelöst wurde, während die Sitzung bereits gelöscht wurde

Sicherheitskontext:

<http use-expressions="true" entry-point-ref="authenticationEntryPoint">
    <logout invalidate-session="true" success-handler-ref="logoutSuccessBean" delete-cookies="JSESSIONID" />
    <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
    <custom-filter position="FORM_LOGIN_FILTER" ref="authFilter" />
    <session-management invalid-session-url="/logout.xhtml" session-authentication-strategy-ref="sas"/>
</http>

<beans:bean id="concurrencyFilter"
  class="org.springframework.security.web.session.ConcurrentSessionFilter">
    <beans:property name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="expiredUrl" value="/logout.xhtml" />
</beans:bean>

<beans:bean id="authenticationEntryPoint"  class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <beans:property name="loginFormUrl" value="/login.xhtml" />
</beans:bean>

<beans:bean id="authFilter"
  class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <beans:property name="sessionAuthenticationStrategy" ref="sas" />
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationSuccessHandler" ref="authenticationSuccessBean" />
    <beans:property name="authenticationFailureHandler" ref="authenticationFailureBean" />
</beans:bean>

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry" />
    <beans:property name="maximumSessions" value="1" />
    <beans:property name="exceptionIfMaximumExceeded" value="1" />
</beans:bean>

Login Listener:

public class LoginListener implements PhaseListener {

@Override
public PhaseId getPhaseId() {
    return PhaseId.RESTORE_VIEW;
}

@Override
public void beforePhase(PhaseEvent event) {
    // do nothing
}

@Override
public void afterPhase(PhaseEvent event) {
    FacesContext context = event.getFacesContext();
    HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();
    String logoutURL = request.getContextPath() + "/logout.xhtml";
    String loginURL = request.getContextPath() + "/login.xhtml";

    if (logoutURL.equals(request.getRequestURI())) {
        try {
            context.getExternalContext().redirect(loginURL);
        } catch (IOException e) {
            throw new FacesException(e);
        }
    }
}

}

0
Thomas Crook