FullAjaxExceptionHandler does not show session expired error page on ajax button - jsf

I have implemented Omnifaces FullAjaxExceptionHandler but the problem is It is not working with ajax requests. After session expires when I click to non-ajax button, It works well. It redirects user to custom error page. But if the button uses ajax, It doesn't do anything. Page just stucks.
Edit: I have changed ActionListener to Action and still same.
Edit2: It gives no error. Neither Apache Tomcat output nor Apache Tomcat Log.
here is my spring security;
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/ajaxErrorPage" access="permitAll"/>
<intercept-url pattern="/pages/*" access="hasRole('admin')" />
<intercept-url pattern="/j_spring_security_check" access="permitAll"/>
<logout logout-success-url="/login.xhtml" />
<form-login login-page="/login.xhtml"
login-processing-url="/j_spring_security_check"
default-target-url="/pages/index.xhtml"
always-use-default-target="true"
authentication-failure-url="/login.xhtml"/>
</http>

You're sending a synchronous redirect as a response to the ajax request (a HTTP 302 response using e.g. response.sendRedirect()). This is not right. The JavaScript ajax engine treats the 302 response as a new destination to re-send the ajax request to. However, that in turn returns a plain vanilla HTML page instead of a XML document with instructions which parts of the page to update. This is confusing and thus the redirected response is altogether ignored. That explains precisely the symptoms you're facing.
The very same problem is also asked and answered in the following closely related questions:
FullAjaxExceptionHandler does not redirect to error page after invalidated session
How to move user to timeout page when session expires, if user click on browser back button
GET request for redirect initiated by browser but not successful
Authorization redirect on session expiration does not work on submitting a JSF form, page stays the same
JSF Filter not redirecting After Initial Redirect
Basically, you need to instruct Spring Security in some way to perform the following conditional check:
if ("partial/ajax".equals(request.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>", loginURL);
} else {
// Normal request. Perform redirect as usual.
response.sendRedirect(loginURL);
}
I'm however no Spring user and I'm not interested to use it, and am therefore not able to give a more detailed answer how to perform this check in Spring Security. I can however tell that Apache Shiro has exactly the same problem which is explained and solved in this blog article: Make Shiro JSF Ajax Aware.

In file spring-security (en el archivo de Spring Security)
<beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
<!-- redirection strategy -->
<beans:bean id="jsfRedirectStrategy" class="com.mycompany.JsfRedirectStrategy">
<beans:property name="invalidSessionUrl" value="/login.xhtml" />
</beans:bean>
<beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
<beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
<beans:property name="invalidSessionStrategy" ref="jsfRedirectStrategy" />
</beans:bean>
<http auto-config='true' use-expressions="true">
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/ajaxErrorPage" access="permitAll"/>
<intercept-url pattern="/pages/*" access="hasRole('admin')" />
<intercept-url pattern="/j_spring_security_check" access="permitAll"/>
<logout logout-success-url="/login.xhtml" />
<form-login login-page="/login.xhtml"
login-processing-url="/j_spring_security_check"
default-target-url="/pages/index.xhtml"
always-use-default-target="true"
authentication-failure-url="/login.xhtml"/>
<!-- custom filter -->
<custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" />
</http>
The custom redirectStrategy (La estrategia de redirección personalizada)
public class JsfRedirectStrategy implements InvalidSessionStrategy
{
private static final String FACES_REQUEST = "Faces-Request";
private String invalidSessionUrl;
public void setInvalidSessionUrl(String invalidSessionUrl) {
this.invalidSessionUrl = invalidSessionUrl;
}
public String getInvalidSessionUrl() {
return invalidSessionUrl;
}
#Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String contextPath = request.getContextPath();
String urlFinal = contextPath+invalidSessionUrl;
if ("partial/ajax".equals(request.getHeader(FACES_REQUEST))) {
// with ajax
response.setContentType("text/xml");
response.getWriter()
.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
.printf("<partial-response><redirect url=\"%s\"></redirect></partial-response>", urlFinal);
} else {
// not ajax
request.getSession(true);
response.sendRedirect(urlFinal);
}
}
work for me.

Related

PrettyFaces 404 page without URL redirect

In our app with PrettyFaces 2.0.12.Final, we have our redirects set up in pretty-config.xml.
<url-mapping id="foo">
<pattern value="/foo/#{alias}" />
<view-id value="/foo.xhtml" />
</url-mapping>
We have a custom 404 page set up in our web.xml.
<error-page>
<error-code>404</error-code>
<location>/404.xhtml</location>
</error-page>
When a user receives a 404 Not Found error, for a foo "alias" that doesn't exist, they're redirected to "/404.xhtml", and the browser URL bar does not retain the offending address.
Is there a way maintain the URL of "/foo/aliasdoesnotexist" in the browser URL bar and still display the 404 page?
One way to handle this scenario is to handle the Exception in your application and perform an internal forward to the error page:
You can set up a PrettyFaces mapping for the 404 page:
<url-mapping id="foo">
<pattern value="/foo/#{alias}" />
<view-id value="/foo.xhtml" />
</url-mapping>
Then in your application code:
catch ( Exception e )
{
String contextName = FacesContext.getCurrentInstance().getExternalContext().getContextName();
FacesContext.getCurrentInstance().getExternalContext().dispatch(contextName + "/404");
}
If you want to catch the exception globally, you'll need to create a servlet filter to do this. See the following topic for how to create a filter that catches all exceptions:
how do I catch errors globally, log them and show user an error page in J2EE app
Hope this helps!
~Lincoln

Verifying additional parameter with j-security-check

I have implemented web application login using j-security-check which accepts parameters j_username and j_password for authentication with background registry. Now, I need to add date of birth as one more parameter and need to authenticate the user with this parameter too.
Is there a way to extend j-security-check to accept additional parameters?
I would like to avoid performing the check in a servlet filter, if possible.
Using WebSphere V8, MyFaces JSF 2.0, Servlet 3.0, Custom database based authentication
The easiest way would be to append the date of birth to the actual j_username (ie. with JavaScript and then manually split it in the login module.
Replace j_security_check by programmatic login via HttpServletRequest#login().
E.g.
<h:form>
<h:inputText value="#{bean.username}" />
<h:inputSecret value="#{bean.password}" />
<h:inputText value="#{bean.birthdate}" />
<h:commandButton value="Login" action="#{bean.login}" />
</h:form>
with
public void login() {
// Do your thing with birthdate here.
// ...
// Then perform programmatic login.
try {
request.login(username, password);
// Login success. Redirect to landing page.
} catch (ServletException e) {
// Login fail. Return to login page.
}
}
This is in detail outlined in 2nd part of this answer: Performing user authentication in Java EE / JSF using j_security_check

JSF / Java EE form vs programmatic authentication

I have been experimenting with the substance of this question (JSF / Java EE login without requiring a protected resource).
If I set up a sample application using BASIC authentication, with one public page (/public.xhtml), and one protected page (/protected/private.xhtml), and I have a link from the first page to the second (as shown below), everything works perfectly.
<h:commandButton value="Go Private" action="/protected/private?faces-redirect=true" />
However, if I remove the login-config and replace the above button with:
<h:commandButton value="Go Private" action="#{mybean.login}" />
...and #{mybean.login} looks something like this...
public String login() {
HttpServletRequest request = ...
try {
request.login("known username", "known password");
} catch (Exception e) {
// handle unknown credentials
}
return "/protected/private?faces-redirect=true";
}
In this case, the login succeeds (no exception from request.login()), but the browser shows a "forbidden resource" page.
Can anyone shed any light on the difference between the two scenarios?
The HttpServletRequest#login() programmatic login works only with FORM based authentication configuration. Removing the <login-config> would make it to default to BASIC and thus the login() will never work. The login() basically sets the user in the session, however the BASIC authentication basically checks the Authenticate HTTP request header, not the session.
Put that <login-config> back and set it to FORM if you want to utilize login().
Where it is:
<h:commandButton value="Go Private" action="#{mybean.login}" />
it should be:
<h:commandButton value="Go Private" action="#{mybean.login()}" />
if you want to use the login methode.
But maybe your mybean also has
String private login;
public String getLogin(){ return this.login ;}
and you've loaded the var with the desired response.

Spring Kerberos Extension, SSO and Machines Outside the Domain

I have been POC'ing with milestone 2 of the Kerberos extension to achieve single sign-on.
Quick summary of my setup:
KDC: Windows Server 2003 (SP2)
Web server: Ubuntu 10.04, Tomcat 5.5, Java 1.6.0_22 (not on the domain)
Spring: Framework 3.0.5, Security 3.0.4, Kerberos Extension 1.0.0 M2
I have setup my configuration to first attempt SPNEGO authentication and if it fails to then redirect to a login page.
This is done by setting SpnegoAuthenticationProcessingFilter's "failureHandler" property. I have successfully tested this
on Windows machines (XP and 7) that are in and out of the domain. The machines that are outside the domain gets redirected to
the login page and then can successfully login.
Here is my config:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">
<http entry-point-ref="spnegoEntryPoint" auto-config="false">
<intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<intercept-url pattern="/j_spring_security_check*" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY" />
<custom-filter ref="spnegoAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
<form-login login-page="/login.html" default-target-url="/" always-use-default-target="true"/>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="kerberosServiceAuthenticationProvider" />
<authentication-provider ref="kerberosAuthenticationProvider"/>
</authentication-manager>
<beans:bean id="spnegoEntryPoint"
class="org.springframework.security.extensions.kerberos.web.SpnegoEntryPoint" />
<beans:bean id="spnegoAuthenticationProcessingFilter"
class="org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter">
<beans:property name="failureHandler">
<beans:bean class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login.html" />
<beans:property name="allowSessionCreation" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="kerberosServiceAuthenticationProvider"
class="org.springframework.security.extensions.kerberos.KerberosServiceAuthenticationProvider">
<beans:property name="ticketValidator">
<beans:bean
class="org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator">
<beans:property name="servicePrincipal" value="HTTP/mywebserver.corpza.corp.co.za"/>
<beans:property name="keyTabLocation" value="classpath:mywebserver.keytab" />
<beans:property name="debug" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean id="kerberosAuthenticationProvider" class="org.springframework.security.extensions.kerberos.KerberosAuthenticationProvider">
<beans:property name="kerberosClient">
<beans:bean class="org.springframework.security.extensions.kerberos.SunJaasKerberosClient">
<beans:property name="debug" value="true" />
</beans:bean>
</beans:property>
<beans:property name="userDetailsService" ref="dummyUserDetailsService" />
</beans:bean>
<beans:bean class="org.springframework.security.extensions.kerberos.GlobalSunJaasKerberosConfig">
<beans:property name="debug" value="true" />
<beans:property name="krbConfLocation" value="/etc/krb5.conf" />
</beans:bean>
<beans:bean id="dummyUserDetailsService" class="main.server.DummyUserDetailsService"/>
</beans:beans>
When the Windows machine is outside the domain my web server responds with the "WWW-Authenticate Negotiate" header (as per usual) to which
the Windows machine responds to with a NTLM header ("Negotiate TlRM..."), where the SpnegoAuthenticationProcessingFilter then says "Negotiate Header was invalid..."
and awesomly redirects the user to the login page. Great.
The Issue:
There are a number of Mac and Linux machines that are permanently outside the domain which would need to use this web app.
When they hit the web app (with Firefox 3.6) my web server responds with the expected "WWW-Authenticate Negotiate" header to inform
the client that the web app is Kerberized, BUT neither the Mac or Linux machines responds at all. Thus the SpnegoAuthenticationProcessingFilter
doesn't get entered again and hence no failure and subsequently no redirection to the login page takes place.
The Question:
Why doesn't the Mac and Linux machines respond in the same way as the Windows machines (can't belive I just asked that...)?
I know that when the Mac and Linux machines obtain a ticket (via kinit) they are able to authenticated but this doesn't seem like a good solution
at all as it requires effort from the user to provide credentials etc. where the tickets expires as well.
So is there any way that we can get these machines to send back a NTLM header as the Windows machines does?
Or if there are any other suggestions/ways please let me know.
B.t.w. I did configure the Firefoxes that I used on to test on the Mac and Linux machines ("network.negotiate-auth.delegation-uris" and "network.negotiate-auth.trusted-uris" was set to ".corpza.corp.co.za").
It doesn't look like you set network.automatic-ntlm-auth.trusted-uris. Did you read through this?
https://developer.mozilla.org/en/Integrated_Authentication
Grant
You are going the wrong way. Don't rely on the failing SPNEGO filter. The Linux and Mac clients behave correctly just as the Windows client does. The general setup should look like the following, if the filter does not implement/support that, you have found a bug.
Client sends a request to the server
Client receives "401 WWW-Authenticate: Negotiate" from the server (or any combination of Negotiate, Basic, Digest).
Client tries to supply auth data and if it fails it shows the 401 page.
Now you rely on faulty/non-processible data sent from the Windows client to present the form. You should rather send the form with the 401 directly to give all clients the ability to choose an appropriate login method (SPNEGO or form, ie. fail-through). Keep in mind that form auth is not an http auth like Negotiate, Digest or Basic. It has to be treated differently.
We are using that filter too and I am not really happy with it. So I have hands-on experience.

Spring Security and JSF redirect after login not working

I am trying to integrate Spring Security with my JSF application.
I am able to get to make the login page show up, but after a successful login, the protected URL does not show up, it stays on the login page.
My applicationContext.xml:
<security:http auto-config="true" access-denied-page="/login/loginerror.jspx">
<security:intercept-url pattern="/restricted/**" access="ROLE_SU"/>
<security:form-login login-page="/login/login.jspx"
default-target-url="/restricted/equipment.jspx"/>
<security:logout logout-success-url="/logoutSuccess.jspx"/>
</security:http>
<security:authentication-provider user-service-ref="userDetailsService"/>
<bean id="userDetailsService" class="com.teach.security.UserDetailsServiceImpl">
<property name="rolesDao" ref="RolesDAO"/>
</bean>
My JSF managed bean, the method that get called when the user hits submit on the login page:
public void login()
{
FacesContext.getCurrentInstance().getExternalContext().redirect("/spring-authentication/j_spring_security_check?j_username=" + userId + "&j_password=" + password);
}
My Java console confirms a successful login, it says "login successful"
Just to be sure, you are not using an AJAX submit but a standard HTML form submit, is that right?
Are you sure the login is sucessful?
The line should be:
FacesContext.getCurrentInstance().getExternalContext().redirect("/j_spring_security_check?j_username=" + userId + "&j_password=" + password);

Resources