1

My Issue: I'm not certain, but I believe there is an SAP provided authentication Servlet Filter, redirecting to the SAP default logon page (which of course is causing a problem since it isn't returning an xml response), which is making the FullAjaxExceptionHandler to not be invoked. I'm looking for help in determining what the root cause really is, or if there are any alternatives to fix it.

My environment:

  • SAP Netweaver 7.4 (servlet 2.5 with JSF 2.1.x support)
  • SAP JVM 6.1 (SAP's 1.6 jvm)
  • Mojarra 2.1.29
  • Omnifaces 1.11

Background:

I have a sample application which has session-config session-timeout set to 1 minute, and have the Omnifaces FullAjaxExceptionHandlerFactory configured in faces-config.xml.

web.xml

...

<error-page>
    <error-code>500</error-code>
    <location>/WEB-INF/errorPage.xhtml</location>
</error-page>
<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/WEB-INF/viewExpired.xhtml</location>
</error-page>
<session-config>
    <session-timeout>1</session-timeout>
</session-config>
... 

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1">

<factory>
    <exception-handler-factory>org.omnifaces.exceptionhandler.FullAjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
...

The way I've determined Netweaver works, is it will mark the session as "expired" after one minute, but the session itself is not cleaned up immediately (I understand this is common in web containers). If I submit an ajax request after the session expired but before the session is completely removed by the server, the FullAjaxExceptionHandler kicks in, and properly displays my View Expired page. If however the server has already cleaned up the expired session, nothing happens, and the page appears "dead" until a physical refresh is performed.

From reading many answers by @balusc, I suspect that an SAP provided http filter is kicking in, which is redirecting the ajax request to the SAP login page, which is why the application isn't working.

Is there any way of determining for sure what is causing it? If it is a server provided filter, is there anyway to override it?

Chris
  • 85
  • 7
  • How does the ajax response look like in case when it fails? Check browser's HTTP traffic monitor to see it (press F12 and open Network tab). – BalusC Sep 15 '15 at 19:22
  • Response is 200, and is as i suspected, is html from `/logon` which is the shared logon page. So this is for sure the root cause, is there any fix? – Chris Sep 15 '15 at 19:33
  • Basically, you need to make the authentication framework "JSF ajax aware". OmniFaces has a builtin fix, but it's only for container managed login page as defined in ``, not for 3rd party authentication frameworks. You can find in this related/duplicate Q&A some clues: http://stackoverflow.com/questions/17296420/fullajaxexceptionhandler-does-not-show-session-expired-error-page-on-ajax-button/ I'm not familiar with SAP, so I can unfortunately not post a detailed answer for that. – BalusC Sep 15 '15 at 19:38
  • In Netweaver, if you define `` `FORM` `` without the `` element, it forwards to the shared logon page. So it is container managed login, however customized by SAP. My assumption was (and now you confirmed) that I need to make it "JSF ajax aware" as you say, but wondering, can this only be done by overriding a SAP provided filter, as you did with the Shiro example? – Chris Sep 15 '15 at 19:45
  • Theoretically, yes. SAP/Netweaver guys can best answer this. You basically need to have somewhere a hook where you could manipulate the response which should bring the enduser to the login page. – BalusC Sep 15 '15 at 20:00

1 Answers1

1

Yet again, thanks BalusC! You pushed me in the right direction.

SAP Netweaver has a global-web.xml file which defines some default functionality. One of them is as follows:

<filter>
    <filter-name>AuthenticationFilter</filter-name>
    <filter-class>com.sap.engine.services.servlets_jsp.server.servlet.AuthenticationFilter</filter-class>
</filter>

So what I did was used the same <filter-name> in my own web.xml, providing a class which extends SAP's Authentication Filter.

<filter>
    <filter-name>AuthenticationFilter</filter-name>
    <filter-class>com.example.AjaxAwareAuthenticationFilter</filter-class>
</filter>

Since the <filter-name> is the same, the one defined in my local web.xml wins.

Here is my class:

package com.example;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.sap.engine.services.servlets_jsp.server.servlet.AuthenticationFilter;

public class AjaxAwareAuthenticationFilter extends AuthenticationFilter {
    @Override
    public void destroy() {
        super.destroy();
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;

        HttpSession session = httpRequest.getSession(false);
        if (session == null && "partial/ajax".equals(httpRequest.getHeader("Faces-Request"))) {
                // JSF ajax request. Return special XML response which instructs
                // JavaScript that it should in turn perform a redirect.
                response.setContentType("text/xml");
                response.getWriter()
                    .append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
                    .printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>",
                            httpRequest.getContextPath());
        } else {
            super.doFilter(request, response, chain);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        super.init(config);
    }
}
Chris
  • 85
  • 7