views:

1456

answers:

4

Hi all,

I've been struggling with this issue for a little while now. Found several posts about it but none solved my problem. It will probably have something to do with the fact that a SecurityContext is boud to a specific Thread but even then I do not know how to solve it:

Consider following code to retrieve the user that was logged in:

SecurityContextHolder.getContext().getAuthentication().getPrincipal()

Running this code in a controller would return (correctly) the user logged in. Running this code from a taglib or jsp throws NPE (authentication = null). Also the spring tag does not function (presumably for the same reason).

Extract from web.xml:

    <filter>
 <filter-name>AcegiFilter</filter-name>
 <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
 <init-param>
  <param-name>targetClass</param-name>
  <param-value>org.acegisecurity.util.FilterChainProxy</param-value>
 </init-param>
</filter>

<filter-mapping>
 <filter-name>AcegiFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

Extract from spring security config file:

    <bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
 <property name="filterInvocationDefinitionSource">
  <value>
   CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 
   PATTERN_TYPE_APACHE_ANT
   /**=httpSessionIntegrationFilter,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
  </value>
 </property>
</bean>
    <bean id="filterSecurityInterceptor"
 class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
 <property name="authenticationManager" ref="authenticationManager" />
 <property name="accessDecisionManager" ref="accessDecisionManager" />
 <property name="alwaysReauthenticate" value="true" />
 <property name="objectDefinitionSource">
  <value>
   CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON 
   PATTERN_TYPE_APACHE_ANT
   /myaccount.htm=ROLE_CUSTOMER
  </value>
 </property>
</bean>

Thanks for your help!

Stijn

A: 

This may seem like an obvious question, but have you forced the user to log in (ie authenticate) at any point prior to them accessing the page /myaccount.htm ? I didn't see any mappings to a login page requiring anonymous access in your object definition source which is why I ask. If the user can access /myaccount.htm without authenticating, then no principal would have been created in the security context by the time they've accessed the page, hence your NullPointerException. Also, are you using form based authentication ? HTTP BASIC authentication ? Some other type supported by Acegi ?

Alex Marshall
A: 

Alex, thanks.

If the user navigates to myaccount.htm then he is redirected to login.htm (form based authentication) where he must enter the credentials. If successfull he will be redirected to myaccount.htm. Again, the principal is correct (and stays correct during the session) when checking the controller(s); in the jsp/tag-class (I assume they go together) it stays null.

regards, Stijn

TheStijn
Stijn, are you using Acegi's tags or writing your own tag which accesses the currently authenticated principal ?
Alex Marshall
A: 

In the mean time I've refactored the config using the security namespace:

<context:annotation-config/>

<s:authentication-manager alias="authenticationManager" />

<s:authentication-provider user-service-ref="userService">
 <s:password-encoder hash="sha">
  <s:salt-source system-wide="ksm4wtlcc"/>
 </s:password-encoder>
</s:authentication-provider>

<bean id="roleBasedAccessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
 <property name="decisionVoters">
  <list>
   <bean class="org.springframework.security.vote.RoleVoter">
    <property name="rolePrefix" value="ROLE_" />
   </bean>
   <bean class="org.springframework.security.vote.AuthenticatedVoter" />
  </list>
 </property>
</bean>

<s:http access-decision-manager-ref="roleBasedAccessDecisionManager" auto-config="true" access-denied-page="/error.htm" lowercase-comparisons="true" path-type="ant">
 <s:intercept-url pattern="/style/**" filters="none" />
 <s:intercept-url pattern="/images/**" filters="none" />
 <s:intercept-url pattern="/js/**" filters="none" />
 <s:intercept-url pattern="/login.htm" filters="none" />
 <s:intercept-url pattern="/**/myaccount.htm" access="ROLE_CUSTOMER"/>
 <s:form-login login-page="/login.htm" always-use-default-target="false" default-target-url="/myaccount.htm" authentication-failure-url="/login.htm?login_error=1" login-processing-url="/j_acegi_security_check"/>
</s:http>

and web.xml:

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Unfortunatly, the problem remains.

I did discover one thing that is very likely a big hint to the cause (but not for me); by default a spring filter will wrap each request into a SecurityContextHolderAwareRequestWrapper. So, a request should allways be an instance of the wrapper. request instanceof SecurityContextHolderAwareRequestWrapper is: - true: when debugging any controller class (or even dwr handler class) - false: when debugging a tag class or jsp

hopefuly this helps you to help me :-)

TheStijn
+2  A: 

RESOLVED

the problem arose from the filter sequence. The PageFilter (sitemesh) was invoked before the spring security filter and because of this the security context was not yet available in the jsp. Changing the order of the filters in web.xml (security filter first) fixed the issue.

regards, Stijn

TheStijn