Marco Osorio
Marco Osorio

Reputation: 203

Add security to spring batch admin

First of all, thanks in advance!

I'm working with Spring-Batch-Admin 1.3.1 and my processes work correctly, but now we have the goal of applying security so that only authorized people can access the processes. The security settings are working perfectly in other applications and go against a CAS.

By doing the tests with spring-batch-admin-sample, I have placed the security configuration file in the path /META-INF/spring/cas-security-config.xml and it is imported from another configuration xml file to overwrite the Properties or load new ones for security. The batch-admin starts up correctly, but when I try to access the application it generates the following error:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined

I have read all the related posts but I can not find the solution. I have tried with overwriting the resourceService bean with /batch but when it is accessed, the others url does not find them and generates 404 error.

We want to protect everything, which redirects to CAS and then returns to the batch-manager menu.

These are my configuration files:

Property load

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

<context:annotation-config />
<context:component-scan base-package="com.aneta.services" />
<!--
    SOBREESCRITURA DE LAS PROPERTIES DEL MANAGER DE SPRING BATCH 
 -->
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <list>
            <value>classpath:*.properties</value>
            <value>classpath:properties/*.properties</value>

        </list>
    </property>
    <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
    <property name="ignoreResourceNotFound" value="true" />
    <property name="ignoreUnresolvablePlaceholders" value="false" />
    <property name="order" value="1" />
</bean>

<import resource="classpath:META-INF/spring/cas-security-config.xml"/>
</beans>

Web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
        http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:/org/springframework/batch/admin/web/resources/webapp-config.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<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>

<filter>
    <filter-name>shallowEtagHeaderFilter</filter-name>
    <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>

<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

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

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

<servlet>
    <servlet-name>Batch Servlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/org/springframework/batch/admin/web/resources/servlet-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>Batch Servlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

</web-app>

And

cas-security-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.2.xsd">

<context:annotation-config />
<context:component-scan base-package="com.aneta.services" />

<bean id="roleVoter" class="org.springframework.security.access.vote.RoleVoter">
    <property name="rolePrefix" value=""/>
</bean>

<bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
    <constructor-arg >
        <list>
            <ref bean="roleVoter"/>
            <bean class="org.springframework.security.web.access.expression.WebExpressionVoter"/>
            <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
        </list>
    </constructor-arg>
</bean>


<security:http entry-point-ref="casEntryPoint" use-expressions="true" auto-config="true"
    access-decision-manager-ref="accessDecisionManager">
    <security:csrf disabled="false"/>
    <security:custom-filter position="FIRST" ref="ajaxSessionFilter"/>
    <security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" />
    <security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER" />

    <security:custom-filter ref="casAuthenticationFilter" after="CAS_FILTER"/>
    <security:custom-filter position="LAST" ref="loginFilter"/>

    <!-- ACCESO CON RESTRICCIONES -->
    <security:intercept-url pattern="/**" access="hasAnyAuthority('ADMINISTRATOR')" />

</security:http>

<bean id="casServiceProperties" class="org.springframework.security.cas.ServiceProperties"
    p:service="${service.base.url}"
    p:sendRenew="false" p:authenticateAllArtifacts="true" />

<bean id="casEntryPoint"
    class="org.springframework.security.cas.web.CasAuthenticationEntryPoint"
    p:serviceProperties-ref="casServiceProperties" p:loginUrl="${cas.server.base.url}/login" />

<bean id="ajaxSessionFilter" class="com.psoplaneta.services.security.filters.AjaxSessionFilter">    
    <property name="homePage" value="${cas.server.base.url}/login"/>
</bean>

<bean id="loginFilter" class="com.aneta.services.security.filters.LoginFilter"> 
    <property name="errorPage" value="${service.base.url}/403"/>
    <property name="loginPage" value="${cas.server.base.url}/login"/>
</bean>


<bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter"
    p:proxyGrantingTicketStorage-ref="proxyGrantingTicketStorage"
    p:proxyReceptorUrl="/login/cas/proxyreceptor"
    p:serviceProperties-ref="casServiceProperties"
    p:authenticationManager-ref="authenticationManager">
    <property name="authenticationFailureHandler">
        <bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
            <property name="defaultFailureUrl" value="/casfailed"/>
        </bean>
    </property>
    <property name="authenticationSuccessHandler">
        <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"
            p:defaultTargetUrl="/">
        </bean>
    </property>
    <property name="proxyGrantingTicketStorage" ref="proxyGrantingTicketStorage" />
</bean>

<bean id="proxyGrantingTicketStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl" />

<bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/>

<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter"
    class="org.springframework.security.web.authentication.logout.LogoutFilter"
    p:filterProcessesUrl="/j_spring_cas_security_logout">
    <constructor-arg value="${cas.server.base.url}/logout" />
    <constructor-arg >
        <bean
            class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
    </constructor-arg>
</bean>

<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider ref="casAuthenticationProvider"/>
</security:authentication-manager>

<bean id="casAuthenticationProvider"
    class="org.springframework.security.cas.authentication.CasAuthenticationProvider"
    p:key="casAuthProviderKey"
    p:serviceProperties-ref="casServiceProperties">
    <property name="authenticationUserDetailsService">
        <bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
            <constructor-arg ref="userDetailService" />
        </bean>
    </property>
    <property name="ticketValidator">
        <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"
            p:proxyGrantingTicketStorage-ref="proxyGrantingTicketStorage">
            <constructor-arg index="0" value="${cas.server.base.url}" />
        </bean>
    </property>
</bean>

<bean id="userDetailService" class="com.aneta.services.security.userdetails.UserDetailsService"/>


</beans>

Upvotes: 0

Views: 1060

Answers (1)

Marco Osorio
Marco Osorio

Reputation: 203

In case you are interested, I put here the solution to activate authentication with CAS SSO and I suppose it will be useful for another type of authentication. After giving so many laps, I have seen that in the web.xml there are two dispatchers and only one was being used, ie only one configuration was loaded and the Batch servlet. The solution is to create a single dispatcher and load all configurations into a single dispatcher. That if, the configuration files I put them out of the folders that override, ie META-INF / spring / servlet-config.xml and within this to make the security imports and if necessary add more things, as this is The right place to make imports of new configurations.

<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <!-- set to blank to ensure context is only loaded once -->
        <param-name>contextConfigLocation</param-name>
        <param-value></param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath*:/org/springframework/batch/admin/web/resources/webapp-config.xml,
        classpath*:/org/springframework/batch/admin/web/resources/servlet-config.xml,
        classpath*:/META-INF/spring/aneta-servlet-config.xml
    </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>shallowEtagHeaderFilter</filter-name>
    <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>
<filter>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>shallowEtagHeaderFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>hiddenHttpMethodFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- SPRING SECURITY -->
<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>
<!-- -->

</web-app>

I hope it works for you!

Upvotes: 2

Related Questions