First version of web.xml to acegi translator
This commit is contained in:
parent
f58cdb7c49
commit
25fa471779
|
@ -0,0 +1,108 @@
|
|||
package net.sf.acegisecurity.util;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.DOMImplementation;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Result;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.dom.DOMResult;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
/**
|
||||
* A utility to translate a web.xml file into a set of
|
||||
* acegi security spring beans.
|
||||
*
|
||||
* <p>
|
||||
* This class wraps the XSL transform which actually does
|
||||
* most of the work. It also tests the result by
|
||||
* loading it into a Spring bean factory.
|
||||
* </p>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class WebXmlSecurityToSpringBeansTranslator {
|
||||
private String webToSpringXsltFile = "web-to-spring.xsl";
|
||||
private String outputFileName = "applicationContext-acegi-security.xml";
|
||||
private Transformer transformer, identityTransformer;
|
||||
private DefaultListableBeanFactory beanFactory;
|
||||
DocumentBuilderFactory dbf;
|
||||
|
||||
public WebXmlSecurityToSpringBeansTranslator() throws Exception {
|
||||
ClassPathResource resource = new ClassPathResource(webToSpringXsltFile);
|
||||
Source xsltSource = new StreamSource(resource.getInputStream());
|
||||
dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setNamespaceAware(true);
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
transformer = tf.newTransformer(xsltSource);
|
||||
identityTransformer = tf.newTransformer();
|
||||
identityTransformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, "-//SPRING//DTD BEAN//EN");
|
||||
identityTransformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "http://www.springframework.org/dtd/spring-beans.dtd");
|
||||
}
|
||||
|
||||
public void translate(InputStream in) throws TransformerException, IOException, ParserConfigurationException, SAXException {
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
Document d = db.parse(in);
|
||||
translate(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the web.xml supplied as a DOM Node
|
||||
*
|
||||
* @param webXml the web application xml
|
||||
*/
|
||||
public void translate(Node webXml) throws IOException, TransformerException, ParserConfigurationException {
|
||||
Source xmlSource = new DOMSource(webXml);
|
||||
DOMResult domResult = new DOMResult();
|
||||
|
||||
transformer.transform(xmlSource, domResult);
|
||||
|
||||
// Obtain DOM for additional manipulation here.
|
||||
Node document = domResult.getNode();
|
||||
|
||||
// Tranform DOM with identity transform to get the output file
|
||||
Result streamResult = new StreamResult(new FileOutputStream(outputFileName));
|
||||
xmlSource = new DOMSource(document);
|
||||
identityTransformer.transform(xmlSource, streamResult);
|
||||
beanFactory = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader beanReader = new XmlBeanDefinitionReader(beanFactory);
|
||||
beanReader.loadBeanDefinitions(new FileSystemResource(outputFileName));
|
||||
}
|
||||
|
||||
public String getOutputFileName() {
|
||||
return outputFileName;
|
||||
}
|
||||
|
||||
public void setOutputFileName(String outputFileName) {
|
||||
this.outputFileName = outputFileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mainly intended for testing
|
||||
* @return the bean factory built from the created acegi security application context file
|
||||
*
|
||||
*/
|
||||
public BeanFactory getBeanFactory() {
|
||||
return beanFactory;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
| XSL Sheet used by the web.xml to acegi-security beans converter
|
||||
| $Id$
|
||||
-->
|
||||
|
||||
|
||||
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
|
||||
|
||||
<xsl:output doctype-public="-//SPRING//DTD BEAN//EN"
|
||||
doctype-system="http://www.springframework.org/dtd/spring-beans.dtd"
|
||||
indent="yes"/>
|
||||
|
||||
<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'"/>
|
||||
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
|
||||
|
||||
<xsl:variable name="welcome-files" select="web-app/welcome-file-list/welcome-file"/>
|
||||
<!-- convert the auth-method content to upper case -->
|
||||
<xsl:variable name="auth-method" select="translate(string(web-app/login-config/auth-method), $lowercase, $uppercase)"/>
|
||||
|
||||
<xsl:variable name="all-roles">
|
||||
<xsl:for-each select="web-app/security-role/role-name">
|
||||
<xsl:text>ROLE_</xsl:text>
|
||||
<xsl:value-of select="translate(string(), $lowercase, $uppercase)"/>
|
||||
<xsl:if test="position() != last()">,</xsl:if>
|
||||
</xsl:for-each>
|
||||
</xsl:variable>
|
||||
|
||||
<!-- The list of filters for use in filterToBeanProxy -->
|
||||
<xsl:variable name="filter-list">
|
||||
<xsl:text>/**=httpSessionContextIntegrationFilter</xsl:text>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$auth-method = 'FORM'">
|
||||
<xsl:text>,authenticationProcessingFilter</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:when test="$auth-method = 'BASIC'">
|
||||
<xsl:text>,basicProcessingFilter</xsl:text>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:message terminate="yes">Unsupported auth-method in web.xml, must be FORM or BASIC</xsl:message>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:text>,rememberMeProcessingFilter,anonymousProcessingFilter,securityEnforcementFilter</xsl:text>
|
||||
</xsl:variable>
|
||||
|
||||
|
||||
|
||||
<xsl:template match = "web-app">
|
||||
|
||||
<beans>
|
||||
<xsl:call-template name="filter-to-bean-proxy"/>
|
||||
<xsl:call-template name="authentication-beans"/>
|
||||
|
||||
<xsl:apply-templates select="./login-config"/>
|
||||
<xsl:call-template name="filter-invocation-interceptor"/>
|
||||
</beans>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="authentication-beans">
|
||||
<xsl:comment>======================== AUTHENTICATION =======================</xsl:comment>
|
||||
|
||||
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
|
||||
<property name="providers">
|
||||
<list>
|
||||
<ref local="daoAuthenticationProvider"/>
|
||||
<ref local="anonymousAuthenticationProvider"/>
|
||||
<ref local="rememberMeAuthenticationProvider"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
|
||||
<property name="authenticationDao"><ref local="inMemoryDaoImpl"/></property>
|
||||
<!-- property name="userCache"><ref local="userCache"/></property-->
|
||||
</bean>
|
||||
|
||||
<bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl">
|
||||
<property name="userMap">
|
||||
<value>
|
||||
superuser=password,<xsl:value-of select="$all-roles"/>
|
||||
<xsl:text>
</xsl:text>
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="anonymousProcessingFilter" class="net.sf.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
|
||||
<property name="key"><value>foobar</value></property>
|
||||
<property name="userAttribute"><value>anonymousUser,ROLE_ANONYMOUS</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="anonymousAuthenticationProvider" class="net.sf.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
|
||||
<property name="key"><value>foobar</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="httpSessionContextIntegrationFilter" class="net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter">
|
||||
</bean>
|
||||
|
||||
<bean id="rememberMeProcessingFilter" class="net.sf.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
|
||||
<property name="rememberMeServices"><ref local="rememberMeServices"/></property>
|
||||
</bean>
|
||||
|
||||
<bean id="rememberMeServices" class="net.sf.acegisecurity.ui.rememberme.TokenBasedRememberMeServices">
|
||||
<property name="authenticationDao"><ref local="inMemoryDaoImpl"/></property>
|
||||
<property name="key"><value>springRocks</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="rememberMeAuthenticationProvider" class="net.sf.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
|
||||
<property name="key"><value>springRocks</value></property>
|
||||
</bean>
|
||||
</xsl:template>
|
||||
|
||||
<!-- login configuration -->
|
||||
<xsl:template match="login-config">
|
||||
<xsl:call-template name="security-enforcement-filter"/>
|
||||
<xsl:choose>
|
||||
<xsl:when test="$auth-method = 'FORM'">
|
||||
<xsl:call-template name="form-login"/>
|
||||
</xsl:when>
|
||||
<xsl:when test="$auth-method = 'BASIC'">
|
||||
<bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter">
|
||||
<property name="authenticationManager"><ref local="authenticationManager"/></property>
|
||||
<property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
|
||||
</bean>
|
||||
|
||||
<bean id="basicProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
|
||||
<property name="realmName"><value>Your Realm</value></property>
|
||||
</bean>
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!--
|
||||
| Inserts the security enforcement filter bean with the appropriate entry point
|
||||
| (depending on whether FORM or BASIC authentication is selected in web.xml).
|
||||
-->
|
||||
<xsl:template name="security-enforcement-filter">
|
||||
<bean id="securityEnforcementFilter" class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
|
||||
<property name="filterSecurityInterceptor"><ref local="filterInvocationInterceptor"/></property>
|
||||
<property name="authenticationEntryPoint">
|
||||
<xsl:choose>
|
||||
<xsl:when test="$auth-method = 'FORM'">
|
||||
<ref local="authenticationProcessingFilterEntryPoint"/>
|
||||
</xsl:when>
|
||||
<xsl:when test="$auth-method = 'BASIC'">
|
||||
<ref local="basicProcessingFilterEntryPoint"/>
|
||||
</xsl:when>
|
||||
</xsl:choose>
|
||||
</property>
|
||||
</bean>
|
||||
</xsl:template>
|
||||
|
||||
<!--
|
||||
| Outputs a standard filterToBeanProxy bean.
|
||||
-->
|
||||
<xsl:template name="filter-to-bean-proxy">
|
||||
<xsl:comment>======================== FILTER CHAIN =======================</xsl:comment>
|
||||
|
||||
<xsl:comment>if you wish to use channel security, add "channelProcessingFilter," in front
|
||||
of "httpSessionContextIntegrationFilter" in the list below</xsl:comment>
|
||||
<bean id="filterChainProxy" class="net.sf.acegisecurity.util.FilterChainProxy">
|
||||
<property name="filterInvocationDefinitionSource">
|
||||
<value>
|
||||
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
|
||||
PATTERN_TYPE_APACHE_ANT
|
||||
<xsl:value-of select="$filter-list"/>
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<!--
|
||||
Converts a form login configuration to an Acegi AuthenticationProcessingFilter and its entry point.
|
||||
The content of the form-login-page element is used for the loginFormUrl property of the entry point
|
||||
and the form-error-page is used for the authenticationFailureUrl property of the filter.
|
||||
|
||||
The user must manually change the form Url to "j_acegi_security_check"
|
||||
-->
|
||||
<xsl:template name="form-login">
|
||||
<xsl:message>Processing form login configuration</xsl:message>
|
||||
<xsl:message>Remember to switch your login form action from "j_security_check" to "j_acegi_security_check"</xsl:message>
|
||||
|
||||
<bean id="authenticationProcessingFilter" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
|
||||
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||
<property name="authenticationFailureUrl"><value><xsl:value-of select="form-login-config/form-error-page"/></value></property>
|
||||
<property name="defaultTargetUrl"><value></value></property>
|
||||
<property name="filterProcessesUrl"><value>/j_acegi_security_check</value></property>
|
||||
<property name="rememberMeServices"><ref local="rememberMeServices"/></property>
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
|
||||
<property name="loginFormUrl"><value><xsl:value-of select="form-login-config/form-login-page"/></value></property>
|
||||
<property name="forceHttps"><value>false</value></property>
|
||||
</bean>
|
||||
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="filter-invocation-interceptor">
|
||||
<bean id="httpRequestAccessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
|
||||
<property name="allowIfAllAbstainDecisions"><value>false</value></property>
|
||||
<property name="decisionVoters">
|
||||
<list>
|
||||
<ref bean="roleVoter"/>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- An access decision voter that reads ROLE_* configuration settings -->
|
||||
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
|
||||
|
||||
<xsl:text>
</xsl:text>
|
||||
<xsl:comment>
|
||||
Note the order that entries are placed against the objectDefinitionSource is critical.
|
||||
The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.
|
||||
Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last
|
||||
</xsl:comment>
|
||||
<bean id="filterInvocationInterceptor" class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
|
||||
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
|
||||
<property name="accessDecisionManager"><ref local="httpRequestAccessDecisionManager"/></property>
|
||||
<property name="objectDefinitionSource">
|
||||
<value>
|
||||
<xsl:text>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
</xsl:text>
|
||||
<xsl:text>PATTERN_TYPE_APACHE_ANT
</xsl:text>
|
||||
<xsl:apply-templates select="security-constraint"/>
|
||||
</value>
|
||||
</property>
|
||||
</bean>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="security-constraint">
|
||||
<xsl:value-of select="web-resource-collection/url-pattern"/>
|
||||
<xsl:text>=</xsl:text>
|
||||
<xsl:for-each select="./auth-constraint/role-name">
|
||||
<xsl:choose>
|
||||
<xsl:when test="string() = '*'">
|
||||
<xsl:value-of select="$all-roles"/>
|
||||
</xsl:when>
|
||||
<xsl:otherwise>
|
||||
<xsl:text>ROLE_</xsl:text>
|
||||
<xsl:value-of select="translate(string(), $lowercase, $uppercase)"/>
|
||||
</xsl:otherwise>
|
||||
</xsl:choose>
|
||||
<xsl:if test="position() != last()">,</xsl:if>
|
||||
</xsl:for-each>
|
||||
<xsl:text>
</xsl:text>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template name="list-roles">
|
||||
<xsl:for-each select="security-role/role-name">
|
||||
<xsl:text>ROLE_</xsl:text>
|
||||
<xsl:value-of select="translate(string(), $lowercase, $uppercase)"/>
|
||||
<xsl:if test="position() != last()">,</xsl:if>
|
||||
</xsl:for-each>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,59 @@
|
|||
package net.sf.acegisecurity.util;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
import net.sf.acegisecurity.providers.ProviderManager;
|
||||
import net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider;
|
||||
import net.sf.acegisecurity.providers.dao.memory.InMemoryDaoImpl;
|
||||
import net.sf.acegisecurity.UserDetails;
|
||||
import net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter;
|
||||
|
||||
import net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor;
|
||||
|
||||
/**
|
||||
* Tests the WebXmlSecurityToSpringBeansTranslator by applying it
|
||||
* to a test sample web.xml file.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
*/
|
||||
public class WebXmlSecurityToSpringBeansTranslatorTests extends TestCase {
|
||||
|
||||
public void testFileTranslation() throws Exception {
|
||||
WebXmlSecurityToSpringBeansTranslator t = new WebXmlSecurityToSpringBeansTranslator();
|
||||
|
||||
Resource r = new ClassPathResource("test-web.xml");
|
||||
t.translate(r.getInputStream());
|
||||
|
||||
BeanFactory bf = t.getBeanFactory();
|
||||
assertNotNull(bf.getBean("filterChainProxy"));
|
||||
|
||||
ProviderManager pm = (ProviderManager) bf.getBean("authenticationManager");
|
||||
assertNotNull(pm);
|
||||
assertEquals(3, pm.getProviders().size());
|
||||
|
||||
DaoAuthenticationProvider dap =
|
||||
(DaoAuthenticationProvider) bf.getBean("daoAuthenticationProvider");
|
||||
assertNotNull(dap);
|
||||
|
||||
InMemoryDaoImpl dao = (InMemoryDaoImpl) dap.getAuthenticationDao();
|
||||
UserDetails user = dao.loadUserByUsername("superuser");
|
||||
assertEquals("password",user.getPassword());
|
||||
assertEquals(2, user.getAuthorities().length);
|
||||
assertNotNull(bf.getBean("anonymousProcessingFilter"));
|
||||
assertNotNull(bf.getBean("anonymousAuthenticationProvider"));
|
||||
assertNotNull(bf.getBean("httpSessionContextIntegrationFilter"));
|
||||
assertNotNull(bf.getBean("rememberMeProcessingFilter"));
|
||||
assertNotNull(bf.getBean("rememberMeAuthenticationProvider"));
|
||||
|
||||
SecurityEnforcementFilter sef =
|
||||
(SecurityEnforcementFilter) bf.getBean("securityEnforcementFilter");
|
||||
assertNotNull(sef);
|
||||
assertNotNull(sef.getAuthenticationEntryPoint());
|
||||
FilterSecurityInterceptor fsi = sef.getFilterSecurityInterceptor();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<web-app>
|
||||
<display-name>login-xml</display-name>
|
||||
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
</welcome-file-list>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<url-pattern>/home.jsp</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>*</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<url-pattern>/admin/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>admin</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<url-pattern>/user/*</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>user</role-name>
|
||||
<role-name>admin</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
|
||||
<login-config>
|
||||
<auth-method>form</auth-method>
|
||||
<form-login-config>
|
||||
<form-login-page>/login.jsp</form-login-page>
|
||||
<form-error-page>/login.jsp?login_error=1</form-error-page>
|
||||
</form-login-config>
|
||||
|
||||
</login-config>
|
||||
|
||||
<security-role>
|
||||
<role-name>user</role-name>
|
||||
</security-role>
|
||||
|
||||
<security-role>
|
||||
<role-name>admin</role-name>
|
||||
</security-role>
|
||||
|
||||
</web-app>
|
Loading…
Reference in New Issue