web-dev-qa-db-fra.com

Délai d'expiration de session et traitement des exceptions ViewExpiredEx sur la demande ajax JSF / PrimeFaces

Je trouve cet article utile pour les requêtes non-ajax Comment gérer l'expiration de session et ViewExpiredException dans JSF 2? mais je ne peux pas l'utiliser lorsque je soumets à l'aide de AJAX appel.

Supposons que dans une boîte de dialogue primefaces, je fasse une demande de publication en utilisant AJAX et la session a déjà expiré. Je vois ma page se bloquer.

Comment résoudre ce type de scénario tel que lorsque je poste avec AJAX, je peux le rediriger vers la page de vue expirée, puis le transférer vers la page de connexion similaire à la solution présentée dans le lien ci-dessus?

JSF2/Primefaces/Glassfish

42
Mark Estrada

Les exceptions levées lors de requêtes ajax n'ont par défaut aucun retour du côté client. Seulement lorsque vous exécutez Mojarra avec stage défini sur Development et utilisez <f:ajax>, vous recevrez alors une alerte JavaScript nue avec le type d’exception et le message. Mais à part cela, et dans PrimeFaces, il n'y a par défaut aucun retour. Vous pouvez toutefois voir l'exception dans le journal du serveur et dans la réponse ajax (dans la section "Réseau" de la boîte à outils pour développeurs du navigateur Web).

Vous devez implémenter une commande personnalisée ExceptionHandler qui effectue essentiellement le travail suivant lorsqu'il y a un ViewExpiredException dans la file d'attente:

String errorPageLocation = "/WEB-INF/errorpages/expired.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();

Vous pouvez également utiliser la bibliothèque d’utilitaires JSF OmniFaces . Il possède un FullAjaxExceptionHandler dans ce but précis (code source ici , démonstration de la vitrine ici ).

Voir également:

46
BalusC

Une fusion entre la réponse de @BalusC et ce post , j'ai résolu mon problème!

Mon ExceptionHandlerWrapper:

public class CustomExceptionHandler extends ExceptionHandlerWrapper {

    private ExceptionHandler wrapped;

    CustomExceptionHandler(ExceptionHandler exception) {
        this.wrapped = exception;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return wrapped;
    }

    @Override
    public void handle() throws FacesException {
        final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
        while (i.hasNext()) {
            ExceptionQueuedEvent event = i.next();
            ExceptionQueuedEventContext context
                    = (ExceptionQueuedEventContext) event.getSource();

            // get the exception from context
            Throwable t = context.getException();

            final FacesContext fc = FacesContext.getCurrentInstance();
            final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
            final NavigationHandler nav = fc.getApplication().getNavigationHandler();

            //here you do what ever you want with exception
            try {

                //log error ?
                //log.log(Level.SEVERE, "Critical Exception!", t);
                if (t instanceof ViewExpiredException) {
                    requestMap.put("javax.servlet.error.message", "Session expired, try again!");
                    String errorPageLocation = "/erro.xhtml";
                    fc.setViewRoot(fc.getApplication().getViewHandler().createView(fc, errorPageLocation));
                    fc.getPartialViewContext().setRenderAll(true);
                    fc.renderResponse();
                } else {
                    //redirect error page
                    requestMap.put("javax.servlet.error.message", t.getMessage());
                    nav.handleNavigation(fc, null, "/erro.xhtml");
                }

                fc.renderResponse();
                // remove the comment below if you want to report the error in a jsf error message
                //JsfUtil.addErrorMessage(t.getMessage());
            } finally {
                //remove it from queue
                i.remove();
            }
        }
        //parent hanle
        getWrapped().handle();
    }
}

Mon ExceptionHandlerFactory:

public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {

    private ExceptionHandlerFactory parent;

    // this injection handles jsf
    public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
        this.parent = parent;
    }

    @Override
    public ExceptionHandler getExceptionHandler() {
        ExceptionHandler handler = new CustomExceptionHandler(parent.getExceptionHandler());
        return handler;
    }

}

Mes visages-config.xml

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
              xmlns="http://xmlns.jcp.org/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">

    <factory>
        <exception-handler-factory>
            your.package.here.CustomExceptionHandlerFactory
        </exception-handler-factory>
    </factory>
</faces-config>
18

J'utilise Mojarra 2.1.7 en mode Production avec JBoss 7. Une fois la session expirée, AJAX appelle un document XML d'erreur. Vous pouvez facilement intercepter cette erreur en utilisant le gestionnaire onerror habituel de f: ajax.

<script type="text/javascript">
    function showError(data) {
        alert("An error happened");
        console.log(data);
    }
</script>

<h:commandLink action="...">
    <f:ajax execute="..." render="..." onerror="showError"/>
</h:commandLink>
3
RajV

J'ai inclus cela dans ma classe ViewExpiredExceptionHandler et cela a bien fonctionné pour moi dans WAS

    public void handle() throws FacesException {
    FacesContext facesContext = FacesContext.getCurrentInstance();
                 for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents()
            .iterator(); iter.hasNext();) {
        Throwable exception = iter.next().getContext().getException();

        if (exception instanceof ViewExpiredException) {


            final ExternalContext externalContext = facesContext
                    .getExternalContext();

            try {


                facesContext.setViewRoot(facesContext.getApplication()
                        .getViewHandler()
                        .createView(facesContext, "/Login.xhtml"));     //Login.xhtml is the page to to be viewed. Better not to give /WEB-INF/Login.xhtml
                externalContext.redirect("ibm_security_logout?logoutExitPage=/Login.xhtml");    //  when browser back button is pressed after session timeout, I used this.         
                facesContext.getPartialViewContext().setRenderAll(true);
                facesContext.renderResponse();

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                iter.remove();
            }
        }

    }

    getWrapped().handle();
}

J'espère que cela t'aides

1
Ramya

J'ai fait face à ce problème. Il est nécessaire d'afficher une fenêtre de confirmation lorsque l'utilisateur effectue une action après l'expiration de la session. La solution proposée était la suivante:

<security:http use-expressions="true" auto-config="true" entry-point-ref="authenticationEntryPoint">
            <security:intercept-url pattern="/common/auth/**" access="permitAll" />
            <security:intercept-url pattern="/javax.faces.resource/**" access="permitAll" />
            <security:intercept-url pattern="/**/   *.*" access="hasRole('ROLE_ADMIN')" />
            <security:form-login login-page="/common/auth/login.jsf" />
            <!-- <security:remember-me key="secret" services-ref="rememberMeServices" /> -->
            <security:logout invalidate-session="true" logout-success-url="/common/auth/login.jsf" />
        </security:http>
        <bean id="authenticationEntryPoint" class="com.x.y.MyRedirectEntryPoint" >
           <property name="loginFormUrl" value="/common/auth/login.jsf"/>
        </bean>

MyRedirectEntryPoint devrait étendre la méthode AuthenticationProcessingFilterEntryPoint et override

public void commence(HttpServletRequest request, HttpServletResponse response,   AuthenticationException authException)
        throws IOException, ServletException {
    boolean ajaxRedirect = request.getHeader("faces-request") != null
            && request.getHeader("faces-request").toLowerCase().indexOf("ajax") > -1;
    if (ajaxRedirect) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            response.sendError(403);

        }
    } else {

        super.commence(request, response, authException);
    }
}

Maintenant, vous pouvez simplement lier une fonction javascript de rappel pour intercepter l'erreur 403 et faire ce que vous voulez:

$(document).bind('ajaxError',
                    function(event, request, settings, exception){
                          if (request.status==403){
                             //do whatever you wanted may be show a popup or just redirect
                             window.location = '#{request.contextPath}/';
                             }
                             });
1
Rami