Reputation: 264
Does anybody have the Spring Security sample PreAuthentication Filter for WebSphere working (WebSpherePreAuthenticatedProcessingFilter)? There is very little documentation on it and I just can't seem to nail it. I'm looking for someone who has it working and might be willing to provide examples of your configuration. Ideally for Spring 3.1 and WAS 7 or 8.
I have a configuration in place that seems like it's "kind of" working. I can authenticate with WebSphere and then hit a URL in my app, but the browser returns this message:
Error 500: java.lang.RuntimeException: Exception occured while looking up groups for user
I get an exception stack trace like this:
java.lang.RuntimeException: Error while invoking method java.lang.reflect.Method.getGroupsForUser([UNAUTHENTICATED])
at org.springframework.security.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor.invokeMethod(DefaultWASUsernameAndGroupsExtractor.java:147) [spring-security-web-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.security.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor.getWebSphereGroups(DefaultWASUsernameAndGroupsExtractor.java:115) [spring-security-web-3.1.3.RELEASE.jar:3.1.3.RELEASE]
at org.springframework.security.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor.getWebSphereGroups(
...
Caused by: java.lang.reflect.InvocationTargetException: null
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60) ~[na:1.6.0]
...
Caused by: com.ibm.websphere.security.EntryNotFoundException: null
at com.ibm.ws.wim.registry.util.MembershipBridge.getGroupsForUser(MembershipBridge.java:293) ~[com.ibm.ws.runtime.wim.core.jar:201207200704]
...
[12/28/12 14:05:15:879 CST] 00000055 LocalTranCoor E WLTC0017E: Resources rolled back due to setRollbackOnly() being called.
[12/28/12 14:05:15:879 CST] 00000055 webapp E com.ibm.ws.webcontainer.webapp.WebApp logServletError SRVE0293E: [Servlet Error]-[ServletNameNotFound]: java.lang.RuntimeException: Exception occured while looking up groups for user
at org.springframework.security.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor.getWebSphereGroups(DefaultWASUsernameAndGroupsExtractor.java:123)
at org.springframework.security.web.authentication.preauth.websphere.DefaultWASUsernameAndGroupsExtractor.getWebSphereGroups(DefaultWASUsernameAndGroupsExtractor.java:94)
...
My web.xml file is as follows:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:/applicationContext-jcr.xml,
classpath*:/applicationContext-security.xml
</param-value>
</context-param>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter>
<filter-name>filterChainProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!--
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
-->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-->
<filter-mapping>
<filter-name>filterChainProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- ====================================================================== -->
<!-- S E R V L E T S -->
<!-- ====================================================================== -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>ViewStatusMessages</servlet-name>
<servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet</servlet-class>
</servlet>
<!-- ====================================================================== -->
<!-- S E R V L E T M A P P I N G S -->
<!-- ====================================================================== -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ViewStatusMessages</servlet-name>
<url-pattern>/lbClassicStatus</url-pattern>
</servlet-mapping>
<!-- ====================================================================== -->
<!-- W E L C O M E F I L E S -->
<!-- ====================================================================== -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Use if configuring for JNDI Datasource on J2EE Server -->
<resource-ref id="ResourceRef_LDP_Datasource">
<description>Resource reference to the LDP datasource.</description>
<!-- DB2 -->
<res-ref-name>jdbc/ldpdbDS</res-ref-name>
<!-- MS SQL -->
<!--
<res-ref-name>jdbc/ldpdbMSSQLDS</res-ref-name>
-->
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<!--
security-constraint reference:
http://s170.codeinspot.com/q/3419721
OK, I figured this out. The problem is that even though I had J2EE security setup in Websphere and was authenticated,
my web.xml contained no security constraints. Because of this, Websphere was not supplying the principal for my requests.
This is apparently an intentional feature. If you are not accessing a protected URL, you should not need the pre-authentication information.
To overcome this, I added a security constraint to my web.xml, which allowed ALL users to access the resources.
Effectively, the resources were not secured, but still - there was a constraint now.
This tricks the Websphere into filling in the user principal information in the request.
-->
<security-constraint>
<web-resource-collection>
<web-resource-name>All areas</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
My Spring security context XML file is as follows:
<!--
<sec:http use-expressions="true">
<sec:intercept-url pattern="/**" access="denyAll" />
<sec:form-login />
</sec:http>
-->
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<sec:filter-chain-map path-type="ant">
<!--
<sec:filter-chain pattern="/**" filters="sif,webspherePreAuthFilter,logoutFilter,etf,fsi"/>
-->
<sec:filter-chain pattern="/**" filters="webspherePreAuthFilter,logoutFilter,etf,fsi"/>
</sec:filter-chain-map>
</bean>
<!--
<bean id="sif" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
<bean id="sif" class="org.springframework.security.web.context.SecurityContextIntegrationFilter"/>
-->
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider ref="preAuthenticatedAuthenticationProvider"/>
</sec:authentication-manager>
<bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
</bean>
<bean id="preAuthenticatedUserDetailsService" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>
<!--
This AbstractPreAuthenticatedProcessingFilter implementation is based on WebSphere authentication.
It will use the WebSphere RunAs user principal name as the pre-authenticated principal.
-->
<bean id="webspherePreAuthFilter" class="org.springframework.security.web.authentication.preauth.websphere.WebSpherePreAuthenticatedProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
</bean>
<bean id="preAuthenticatedProcessingFilterEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="/"/>
<constructor-arg>
<list>
<bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</list>
</constructor-arg>
</bean>
<!--
This AuthenticationDetailsSource implementation will set the pre-authenticated granted authorities based on the WebSphere
groups for the current WebSphere user, mapped using the configured Attributes2GrantedAuthoritiesMapper.
This AuthenticationDetailsSource implementation, when configured with a MutableGrantedAuthoritiesContainer,
will set the pre-authenticated granted authorities based on the WebSphere groups for the current WebSphere user,
mapped using the configured Attributes2GrantedAuthoritiesMapper.
By default, this class is configured to build instances of the PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails class.
-->
<bean id="authenticationDetailsSource" class="org.springframework.security.web.authentication.preauth.websphere.WebSpherePreAuthenticatedWebAuthenticationDetailsSource">
<property name="webSphereGroups2GrantedAuthoritiesMapper" ref="websphereUserGroups2GrantedAuthoritiesMapper"/>
</bean>
<bean id="websphereUserGroups2GrantedAuthoritiesMapper" class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
<property name="convertAttributeToUpperCase" value="true"/>
</bean>
<bean id="webXmlResource" class="org.springframework.web.context.support.ServletContextResource">
<constructor-arg ref="servletContext"/>
<constructor-arg value="/WEB-INF/web.xml"/>
</bean>
<bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>
<bean id="etf" class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="preAuthenticatedProcessingFilterEntryPoint"/>
</bean>
<bean id="httpRequestAccessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<ref bean="roleVoter"/>
</list>
</property>
</bean>
<!-- See: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/core-web-filters.html -->
<bean id="fsi" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadataSource">
<sec:filter-security-metadata-source>
<sec:intercept-url pattern="/**" access="ROLE_LDP_ADMINS"/>
</sec:filter-security-metadata-source>
</property>
</bean>
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter"/>
<!--
Simply put, the filter wraps the current httprequest with one that delegates request.isUserInRole() and request.getRemoteUser() to acegi.
<bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter">
<property name="wrapperClass" value="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper"/>
</bean>
-->
Upvotes: 5
Views: 7159
Reputation: 95
For some reason WebSphere doesn't require authentication for resources with the following constraint definition and the pre-authentication doesn't happen:
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
It fails in the later stage when it tries to determine groups for an unknown principal.
I protected the resource with a default group Users and mapped all users to it:
<security-constraint>
<web-resource-collection>
<web-resource-name>AuthTest</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>User</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>User</role-name>
</security-role>
It is sort of a workaround because I don't want this role and manage its members but it is all I have for now.
Upvotes: 2
Reputation: 23565
I don't have a working config but a piece of advice
MembershipBridge.getGroupsForUser
, I have a feeling the username passed to it is nullBased on my gut feeling I suspect the name of the pre-authenticated user is not being passed on. I remember vaguely that I had this a few years ago - need to dig up that code.
Upvotes: 2