I am integrating ZK with Spring Security, so far I've configured the latter so all URLs are secured except for the login page. Which of course works fine when page switching takes place by navigating to different URLs.
However, when I change some content dynamically (i.e. by changing the src attribute of an Include
element in ZK), and then within the controller of that new content I call SecurityContextHolder.getContext().getAuthentication(), I obtain no sort of credentials, but a null as the Authentication
object. My guess is that the culprit is pointed by this line in debug:
SecurityContextPersistenceFilter:97 - SecurityContextHolder now cleared, as request processing completed
The problem is I obtain all my data from a REST webservice that uses Basic Authentication, so I need to obtain the user's credentials on every request, and that includes those of dynamically loaded content.
I've seen I could set my SecurityContext level to be global, but I've also read it is bad practice and it could cause users to obtain someone else's credentials.
PS: Funny thing is, if instead of:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
I use:
Authentication authentication = ((org.springframework.security.core.context.SecurityContextImpl)session().getAttribute("SPRING_SECURITY_CONTEXT")).getAuthentication();
My Authentication object is in fact there. So there must be something I'm missing here.
EDIT: here's my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">
<display-name>XESAC</display-name>
<!-- Spring Security -->
<!-- A servlet filter capturing every user requests and sending them to
the configured security filters to make sure access is authorized. -->
<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>
<context-param>
<param-name>webAppRootKey</param-name>
<param-value>sigur-ui.root</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring/appcontext-spring.xml,classpath:/spring/appcontext-security.xml</param-value>
</context-param>
<listener>
<description>Spring Loader</description>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<description>Used to cleanup when a session is destroyed</description>
<display-name>ZK Session Cleaner</display-name>
<listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<description>
The ZK loader for ZUML pages</description>
<servlet-name>zkLoader</servlet-name>
<servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class>
<init-param>
<param-name>update-uri</param-name>
<param-value>/zkau</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<description>The asynchronous update engine for ZK</description>
<servlet-name>auEngine</servlet-name>
<servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zul</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>zkLoader</servlet-name>
<url-pattern>*.zhtml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>auEngine</servlet-name>
<url-pattern>/zkau/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>dspLoader</servlet-name>
<servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class>
<init-param>
<param-name>class-resource</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dspLoader</servlet-name>
<url-pattern>*.dsp</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.zul</welcome-file>
</welcome-file-list>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.zul</location>
</error-page>
</web-app>
appcontext-security.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:zksp="http://www.zkoss.org/2008/zkspring/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<global-method-security secured-annotations="enabled" pre-post-annotations="disabled"/>
<http pattern="/resources/**" security="none" />
<http pattern="/zkau/**" security="none" />
<http auto-config="true" use-expressions="true">
<!-- En funcion del rol de cada usuario -->
<intercept-url pattern="/login.zul" access="permitAll" />
<intercept-url pattern="/timeout.zul" access="permitAll" />
<intercept-url pattern="/css/**" access="permitAll" />
<intercept-url pattern="/img/**" access="permitAll" />
<intercept-url pattern="/**" access="isAuthenticated()" />
<!-- Areas Patterns -->
<form-login login-page="/login.zul" authentication-failure-url="/login.zul?login_error=1" default-target-url="/index.zul" />
<logout logout-success-url="/login.zul" logout-url="/logout" invalidate-session="true" />
</http>
<!-- Autenticación mock para las pruebas -->
<authentication-manager erase-credentials="false">
<authentication-provider ref="myProvider" />
</authentication-manager>
<beans:bean id="myProvider" class="com.myapp.provider.MyProvider" />
</beans:beans>
MyProvider.java (this is a class of my own that authenticates against a REST endpoint):
public class MyProvider implements AuthenticationProvider {
@Autowired
private IAuthenticationService authenticationService;
@Autowired
private HttpSession session;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetailsDTO userDetails = authenticationService.doAuthenticate(authentication);
if (null != userDetails && !userDetails.isAnonymous()) {
session.setAttribute("user", userDetails);
List<GrantedAuthority> grantedAuths = new ArrayList<>();
//should set the roles here
return new UsernamePasswordAuthenticationToken(userDetails, password, grantedAuths);
}
else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
In your security config, you have that line :
<http pattern="/zkau/**" security="none" />
All URLs beginning with /zkau
completely by-pass the spring security filter. If ZK uses those URLs which are not processed by spring security, the Authentication
will be null.
You should at least remove that line from appcontext-security.xml