Seam 2.2.0 and JBoss negotiation SPNEGO without login form
jeroenkoek May 23, 2010 1:11 AMTo help others that are struggeling with SSO,
I have got SPNEGO authentication working using the JAAS module of JBoss (jboss-negotiation). The user is auto logged in using the information stored in the servlet.
Steps to follow;
Ensure that the web.xml is handling the security
<security-constraint> <display-name>Restrict raw XHTML Documents</display-name> <web-resource-collection> <web-resource-name>XHTML</web-resource-name> <url-pattern>*.xhtml</url-pattern> </web-resource-collection> <web-resource-collection> <web-resource-name>SEAM</web-resource-name> <url-pattern>*.seam</url-pattern> </web-resource-collection> <auth-constraint> <role-name>*</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>SPNEGO</auth-method> </login-config>
Enable jaas component;
In the components.xml set the identity configuration. This is the JAAS login-config that is defined by you in the login-config.xml as described in the manual of JBOSS negotiation.
<security:identity jaas-config-name="SPNEGO" />
Disable the Identity httprequestwrapper in the components.xml
<web:identity-filter disabled="true"/>
By doing this the http servlet information such as remote-user and the userprincipal are available to be used in a identity class (next step). If you do not do this the Identity store is retrieving it's own information, resulting in a stack overflow in my case.
Create a identity class that overrules the standard SEAM identity class
The getPrincipal() will now be used in the super class to see that there is a principal. Seam security will conclude that the user is logged in.
The hasRole will still be using the servlet isUserInRole function, but
import java.security.Principal; import java.util.Collection; import javax.faces.context.FacesContext; import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import javax.servlet.http.HttpServletRequest; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Install; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.Startup; import org.jboss.seam.annotations.intercept.BypassInterceptors; import org.jboss.seam.core.Expressions.MethodExpression; import org.jboss.seam.log.Log; import org.jboss.seam.security.Credentials; import org.jboss.seam.security.Identity; @SuppressWarnings("serial") @Name("org.jboss.seam.security.identity") @Scope(ScopeType.SESSION) @Install(precedence = Install.APPLICATION) @BypassInterceptors @Startup public class CustomIdentity extends Identity { @Logger Log log; @Override public Principal getPrincipal() { Principal principal = super.getPrincipal(); if ( principal == null ) { // get the remote user name from the session getCredentials().setUsername( FacesContext.getCurrentInstance().getExternalContext().getRemoteUser()); log.info( "session user:" + FacesContext.getCurrentInstance().getExternalContext().getRemoteUser() ); log.info( "session principal:" + FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal() ); principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal(); acceptExternallyAuthenticatedPrincipal(principal); } return principal; } @Override public boolean hasRole(String arg0) { boolean result = super.hasRole(arg0); if ( !result ) { result = ((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).isUserInRole(arg0); //bit by bit we are extending the role set of this user, this is a kind of caching of the roles. //the servlet interface doesn't provide us with a function to get all roles //so we are retrieving them on a request basis. if ( result ) { super.addRole(arg0); } } log.info("hasRole:" +arg0 + " " + result); return result; } }
Ensure that the login-required flag is on your page.xml
<page no-conversation-view-id="/main.xhtml" view-id="/main.xhtml" login-required="true" .....
Annotation
If I'm right it should work. You can verify it by annotating a seam javabean
@Name("yourbean") @Scope(ScopeType.PAGE) @MeasureCalls @Restrict("#{s:hasRole('your defined rule')}")
Hope this helps,
Jeroen