How to have spring security context in child context - spring

I'm trying to have spring security context in child context, so I could have url security on the servlet context file.
I have:
<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>contextConfigLocation</param-name>
<param-value>
classpath:/spring-security.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>myapp-soap</servlet-name>
<servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
<init-param>
<param-name>transformWsdlLocations</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
on web.xml, general security configuration on spring-security.xml and
<!-- Authorization configurations -->
<security:http auto-config="false" use-expressions="true"
create-session="never"
authentication-manager-ref="authenticationManager"
entry-point-ref="authenticationEntryPoint">
<security:custom-filter
position="PRE_AUTH_FILTER" ref="serviceAuthenticationFilter"/>
<security:intercept-url
pattern="/GetForbiddenUrl" access="hasRole('roleThatDoesntExist')" />
<security:intercept-url pattern="/**" access="permitAll" />
</security:http>
<!-- annotation security -->
<security:global-method-security pre-post-annotations="enabled"/>
on the myapp-soap-servlet.xml. It doesn't work but fails with
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/my-app/v1/soap]] (ServerService Thread Pool -- 192) JBWEB000284: Exception starting filter springSecurityFilterChain:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined
However, if I move <security:http> part to spring-security root context configuration, everything works. Shouldn't it work the way I try? How can I get url-based security in my child context?
I also tried combining the context files into one, but the same problem seems to occur.

The DelegatingFilterProxy will by default look in the root ApplicationContext which means by default you need to place your <http> configuration there (it is what creates the springSecurityFilterChain).
However, you can specify use that DelegatingFilterProxy a different ApplicationContext by specifying the contextAttribute for it to use. To do this update your web.xml as shown below
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>contextAttribute</param-name>
<param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.myapp-soap</param-value>
</init-param>
</filter>
A similar example using Spring Security 3.2+'s AbstractSecurityWebApplicationInitializer can be seen below:
public class SecurityApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
#Override
protected String getDispatcherWebApplicationContextSuffix() {
// NOTE: if you are using AbstractDispatcherServletInitializer or
// AbstractAnnotationConfigDispatcherServletInitializer You probably
// want this value to be "dispatcher"
return "myapp-soap";
}
}
This works because it modifies the name of the ServletContext attribute that DelegatingFilterProxy uses to lookup the ApplicationContext. Instead of using the default value which discovers the root ApplicationContext it now uses the attribute that your MessageDispatcherServlet is using (thus pointing to the child context).
Note that MessageDispatcherServlet's (or any subclass of FrameworkServlet such as DispatcherServlet) stores the ApplicationContext in the ServletContext using the attribute name "org.springframework.web.servlet.FrameworkServlet.CONTEXT." + <servlet-name> where <servlet-name> is the name of the servlet. So in this instance, the attribute that must be configured is org.springframework.web.servlet.FrameworkServlet.CONTEXT.myapp-soap. If you changed the servlet-name from myapp-soap to spring-servlet, then you would use org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring-servlet instead.
PS I think the subject should read "How to have spring security context as child context"

<security:http> will have to go to the main application context instead of the child (servlet) context, because this security namespace element creates the springSecurityFilterChain bean, which is looked up by DelegatingFilterProxy in the main context. As its javadoc clearly states:
web.xml will usually contain a DelegatingFilterProxy definition, with the specified filter-name corresponding to a bean name in Spring's root application context.

Related

spring mvc; contextConfigLocation load order

I'm reconfiguring a webapp. I want to move everything out of dispatcher servlet into ContextLoaderListener. (This is due to changes in security configuration beyond the scope of this question)
Question 1, if I have multiple application context xml files, does it matter what order they are loaded? For example does the xml file containing context:component-scan need to be loaded before the xml file specifying DAO and service beans?
Question 2, (or is this moot?) how would I specify the order in which *_applicationContext.xml are loaded assuming that A_applicationContext.xml should be loaded before B_applicationContext.xml which should be loaded before C_applicationContext.xml
My web.xml is as follows:
<web-app id="WebApp_ID" version="2.4"
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">
<servlet>
<servlet-name>AssessmentDelivery</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AssessmentDelivery</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/*_applicationContext.xml</param-value>
</context-param>
<!-- security filter -->
<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>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
</web-app>
Some suggestions:
For these days consider do the configuration for Spring through Javaconfig.
To answer questions 1 and 2 is very important you understand the following:
When you run the app Spring creates an Application Context where exists all the beans created and managed by Spring. Now consider that for that Application Context it should be created from two 'sub' applications contexts, normally they are 'mentioned' in the documentation how ServletApplicationContext and RootApplicationContext
The former should scan all about the Web, such as your #Controllers and #Bean's about infrastructure such as for ViewResolver etc..
The later should scan all about the Server, such as #Service and #Repositories and #Bean's about infrastructure such as for a DataSource etc.
Is very important understand the following:
ServletApplicationContext --> RootApplicationContext
It means the former can get access the latter (it about use dependencies i.e: a #Controller needs a #Service). Therefore it reflects that the Web side can access the server side.
Once said this the following is not possible
RootApplicationContext --> ServletApplicationContext
has no sense that a Bean from the server side want access the web side (a bad practice)
Long time ago I don't use web.xml but
DispatcherServlet + contextConfigLocation (through <init-param>) represents the ServletApplicationContext
ContextLoaderListener + contextConfigLocation (through <context-param>) represents the RootApplicationContext
It does not matter if the beans are declared through:
XML
JavaConfig
annotations #Controller etc.
Spring manages the cycle about in what order the beans are created. So do not matter how the .xml files (in your case) are declared (about the order).

Spring Security 3.2: #Secured annotations not taken into account

I am trying to secure my RESTful API using Spring 3.2.4 and Spring Security 3.2 using the #Secured annotations. I have the following setup:
web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:spring/*.xml
/WEB-INF/classes/security/security-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 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>
<!-- Servlet configuration -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/servlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
servlet-context.xml:
<context:component-scan base-package="com.mycompany.rest.controller" />
<security:global-method-security secured-annotations="enabled" />
security-context.xml
<beans:bean id="merchantUserDetailsService" class="com.mycompany.rest.security.CustomUserDetailsService" />
<http auto-config="false" create-session="never">
<http-basic />
</http>
<authentication-manager>
<authentication-provider user-service-ref="customUserDetailsService" />
</authentication-manager>
I am programmatically assigning custom roles (ROLE_GROUP, ROLE_DIVISION, ROLE_READ, ROLE_WRITE) in the customUserDetailsService to the user and this works fine.
One of my controllers:
#Secured("ROLE_DIVISION")
#RequestMapping(method = RequestMethod.GET)
ResponseEntity<List<CustomerResource>> getCustomer() throws ResourceDoestNotExistException {
List<Customer> providers = // retrieve providers from DAO
List<CustomerResource> resources = customerResourceAssembler.toResources(customers);
return new ResponseEntity<>(resources, HttpStatus.OK);
}
Now to my problem, the #Secured annotations are being ignored. I want to use the #Secured annotation to avoid having to define multiple 's in the configuration. Spring Security works fine when I add at least one , but how can avoid defining them and instead rely on the #Secured annotations?
I can now access the method above with an user with the role "ROLE_GROUP".
Looks like you have everything right except that you've enabled the wrong type of annotations. If you check the documentation for global-method-security you'll see there's a separate attribute called secured-annotations which enables #Secured.

Authentication failed in Spring due to no mapping found for HTTP request with URI?

I have a web project using Spring Security. The user begin from login.jsp, pass through the authentication, and direct to hello.jsp. Take note that all JSP pages are locate in WEB-INF. During the process, I notice the URL begin with localhost:8081/Project1/pages/login.jsp, right after submit the page, the URL have change to http://localhost:8081/Project1/pages/j_spring_security_check;jsessionid=792884C463EAC3C6CBC155EA75F4C6E4.
And then the page showing me HTTP status 404 after the authentication instead of showing hello.jsp. In the console output, I see this message:
WARNING: No mapping found for HTTP request with URI [/SpringSecurity3/pages/j_spring_security_check] in DispatcherServlet with name 'SpringSecurity3'
I am not sure what is miss configure in Spring configuration? The configuration code is as follow:
<http auto-config="true">
<intercept-url pattern="/welcome*" access="ROLE_ADMIN"/>
<intercept-url pattern="/pages/hello.jsp" access="ROLE_ADMIN"/>
<form-login login-page="/login" default-target-url="/welcome" authentication-failure-url="/loginfailed" />
<logout logout-success-url="/pages/login.jsp"/>
</http>
and my web.xml is as follow:
<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-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/WEB-INF/pages/*</url-pattern>
</filter-mapping>
Remove
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/WEB-INF/pages/*</url-pattern>
</filter-mapping>
Also you can refer to this tutorials:
http://www.mkyong.com/spring-security/spring-security-hello-world-example http://www.mkyong.com/spring-security/spring-security-form-login-example/
Double-check if the security configuration is included in the main application context (as opposed to the servlet application context). The xml configuration file declaring the security filter chain (<http ...>) should be listed in the contextConfigLocation context parameter in web.xml (or be imported by one of the xml config files listed there).
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
...
[spring-security config file should be included here]
...
</param-value>
</context-param>

Spring MVC, Spring Security and Hibernate cannot autowire properties between contexts

I am using Spring MVC 3.0.6 and Spring security 3.0.7. I cannot #Autowire the RoleDao class to my user class when in the security context.
my web.xml file:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/security-app-context.xml
</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<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>
The security-app-context.xml:
<beans:bean id="chineseCheckersEntryPoint" class="com.nike.golf.security.ChineseCheckersAuthenticationEntryPoint" />
<beans:bean id="chineseCheckersFilter" class="com.nike.golf.security.ChineseCheckersAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager"/>
</beans:bean>
<http use-expressions="true" auto-config="false" entry-point-ref="chineseCheckersEntryPoint">
<intercept-url pattern="/secure/extreme/**" access="hasRole('supervisor')" />
<intercept-url pattern="/user/**" access="permitAll" />
<intercept-url pattern="/profile/**" access="isAuthenticated()" />
<intercept-url pattern="/secure/**" access="isAuthenticated()" />
<custom-filter position="PRE_AUTH_FILTER" ref="chineseCheckersFilter" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="chineseCheckersAuthenticationProvider" />
</authentication-manager>
<beans:bean id="chineseCheckersAuthenticationProvider" class="com.nike.golf.security.ChineseCheckersAuthenticationProvider" />
In my user object where it uses the roleDao, it's null. It has not been autowired. From all the research I have done online it seems to be related to the different contexts, and not being able to autowire between them.
Can someone help me understand these contexts and how I can get them into the same context?
Thank you everyone for your help. I managed to figure this out.
This question was similar to mine and got me moving in the right direction:
Declaring Spring Bean in Parent Context vs Child Context
This forum post really simplified the idea for me.
In your web.xml file you define the servlet context and the application context.
The application context is configured through these pieces of XML:
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/root-context.xml
/WEB-INF/security-app-context.xml
</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
any number of *context.xml files you pass into the context-param > contextConfigLocation are in the application context. This is the parent context.
The servlet context gets created in the web.xml file by this bit of xml:
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
The servlet context as the child context has access to the application context (parent).
This was the key thing for me to understand. So I moved all configuration I had in the servlet context, up to the application context.
Like the answer in the other question I linked to above says #Autowired still does not work. Anyone know the reason for that?
So to get around this I defined the beans and the properties in the xml all the way from the property I was concerned with down to the sessionFactory.
The thing is now I could wire up the beans I needed in xml all the way up the hierarchy to sessionFactory because it was in the same context, since I moved it up to the application context from the servlet context where it was before.
In my question I didn't even post the servlet-context.xml file because I didn't think it needed to be touched, but in fact I needed to move the configuration up to the app context if I wanted to wire things up to my security beans.
I hope that makes sense.
You can imagine a context as a set of Spring beans.
Contexts can be nested, so that the outer context can access the beans from the inner one, but not the other way around. An example for this are typical web application, the have two contexts: the inner one specified with the contextConfigLocation and loaded by ContextLoaderListener, and the outer one configured with the DispatcherServlet.
One way to merge two xml files to one context, is introducing a third apllication configuration xml file, that only include then other xml files via bean:include. And then you have only to specify this third xml files for the loader. But I am not sure if you really have 2 application contexts configured for ContextLoaderListener. -- Anyway you can try the trick with the 3. xml file.

#Secured does not work in controller, but intercept-url seems to be working fine

It doesn't look like #Secured on methods in my #Controller are being read. When security filtering based on sec:intercept-url is being used, this seems to be working just fine. The following code results in Spring Security giving me this log entry:
DEBUG: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Public object - authentication not attempted
web.xml
contextConfigLocation
/WEB-INF/spring/root-context.xml
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/appServlet/servlet-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Filter 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>
servlet-context.xml holds the configuration of the viewResolvers and all the marshalling. This configuration is annotation-driven.
root-context.xml
<sec:global-method-security secured-annotations="enabled" />
<sec:http auto-config="true">
<sec:http-basic/>
</sec:http>
<!-- Declare an authentication-manager to use a custom userDetailsService -->
<sec:authentication-manager>
<sec:authentication-provider
user-service-ref="userDetailsService">
<sec:password-encoder ref="passwordEncoder" />
</sec:authentication-provider>
</sec:authentication-manager>
<bean
class="org.springframework.security.authentication.encoding.PlaintextPasswordEncoder"
id="passwordEncoder" />
<sec:user-service id="userDetailsService">
<sec:user name="john" password="john" authorities="ROLE_USER, ROLE_ADMIN" />
<sec:user name="jane" password="jane" authorities="ROLE_USER" />
</sec:user-service>
PingController.java
#Controller
public class PingController {
#Secured("ROLE_ADMIN")
#RequestMapping(value = "/ping", method = RequestMethod.GET)
public void ping() {
}
}
This doesn't seem to have any relation to which authentication method I'm using, so the basic-http-tag can be overlooked.
I have this idea that the #Secured doesn't work because of it being used in another context than the root-context.xml in which the security is being configured. I've tried to move this configuration to the servlet-context.xml, but it doesn't seem to reach the springSecurityFilterChain. Any thoughts on the problem and and my theory?
You are right, <global-method-security> is applied at per-context basis. However, you don't need to move the whole security configuration to servlet-context.xml, just add a <global-method-security> element to it.
See Spring Security FAQ (emphasis mine). If you apply pointcuts to service layer you only need to set <global-method-security> in your app's security context.
In a Spring web application, the application context which holds the
Spring MVC beans for the dispatcher servlet is often separate from the
main application context. It is often defined in a file called
myapp-servlet.xml, where “myapp” is the name assigned to the Spring
DispatcherServlet in web.xml. An application can have multiple
DispatcherServlets, each with its own isolated application context.
The beans in these “child” contexts are not visible to the rest of the
application. The “parent” application context is loaded by the
ContextLoaderListener you define in your web.xml and is visible to all
the child contexts. This parent context is usually where you define
your security configuration, including the
element). As a result any security constraints applied to methods in
these web beans will not be enforced, since the beans cannot be seen
from the DispatcherServlet context. You need to either move the
declaration to the web context or moved the
beans you want secured into the main application context.
Generally we would recommend applying method security at the service
layer rather than on individual web controllers.

Resources