application - Tiempo de espera de la sesión y manejo de ViewExpiredException en la solicitud JSF/PrimeFaces ajax
javax faces application viewexpiredexception (5)
Encuentro que este artículo es útil para solicitudes sin ajax. ¿Cómo puedo manejar la caducidad de la sesión y ViewExpiredException en JSF 2? pero no puedo hacer uso de esto cuando estoy enviando una llamada AJAX.
Supongamos que en un cuadro de diálogo de Primefaces, estoy haciendo una solicitud posterior usando AJAX y la sesión ya ha expirado. Veo que mi página se atasca.
¿Cómo arreglar este tipo de escenario de modo que cuando publico usando AJAX, podría redirigirlo a mi página expirada y luego reenviarlo a la página de inicio de sesión similar a la solución en el enlace de arriba?
JSF2 / Primefaces / Glassfish
Estoy usando Mojarra 2.1.7 en modo de producción con JBoss 7. Después de que la sesión caduque, las llamadas AJAX devuelven un documento XML de error. Puede detectar fácilmente este error utilizando el controlador usual onerror 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>
He incluido esto en mi clase ViewExpiredExceptionHandler y funcionó bien para mí en 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();
}
Espero que esto ayude
Me enfrenté a este problema, el requisito debe mostrar una ventana emergente de confirmación cuando el usuario realiza cualquier acción después de que la sesión se agote, mi solución propuesta fue:
<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 debe extender AuthenticationProcessingFilterEntryPoint y anular el método de inicio
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);
}
}
Ahora puede vincular simplemente una función javascript de devolución de llamada para detectar el error 403 arrojado y hacer lo que quiera:
$(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}/'';
}
});
Una fusión entre la respuesta de @BalusC y esta publicación , ¡resolví mi problema!
My 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();
}
}
My 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;
}
}
Mi faces-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>
Las excepciones que se lanzan durante las solicitudes ajax no tienen, por defecto, ninguna retroalimentación en el lado del cliente. Solo cuando ejecute Mojarra con la etapa del proyecto establecida en Development
y use <f:ajax>
, obtendrá una alerta de JavaScript con el tipo y mensaje de excepción. Pero aparte de eso, y en PrimeFaces, por defecto no hay retroalimentación. Sin embargo, puede ver la excepción en el registro del servidor y en la respuesta ajax (en la sección "Red" del conjunto de herramientas del desarrollador del webbrowser).
ViewExpiredException
implementar un ExceptionHandler
personalizado que básicamente hace el siguiente trabajo cuando hay una ViewExpiredException
en la cola:
String errorPageLocation = "/WEB-INF/errorpages/expired.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
Alternativamente, puede usar la biblioteca de utilidades JSF OmniFaces . Tiene un FullAjaxExceptionHandler
para exactamente este propósito (código fuente here , demostración de demostración here ).