Spring Security + MVC : Questions around context definition & bean scope - java

I'm trying to understand the reccommended way for defining Spring Security in Spring-MVC applications, where the bean definitions is split across multiple parent/child contexts.
For example, my current app's web.xml looks as follows, (which I understand to be fairly standard)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:applicationContext.xml
/WEB-INF/securityContext.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>
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
So, I have a standard ContextLoaderListener defined at /, which loads my global configs - applicationContext.xml and securityContext.xml.
I also define the spring mvc DispatcherServlet at /app/, which loads it's own beans from spring-mvc-servlet.xml.
As I understand it, config defined in spring-mvc-servlet.xml is not visible to config defined in either of the top-level context files.
Where then is the best place to define app-level security concepts? For example, I'd like to add the following filter.
<security:http pattern="/oauth/token" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint">
<security:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
</security:http>
This is so that requests to /app/oauth/token pass through this filter, and get basic authentication processed.
Because this pertains directly to a concern of the Spring-MVC app, I initially defined it in spring-mvc-context.xml (which is why the app is excluded from the url).
However, this means it's not visible to the security config defined in securityContext.xml, so it's ignored.
So, I move it up to securityContext.xml, but in doing so, also must move all the dependencies.
I quickly end up moving everything up to applicationContext.xml, which leaves the spring-mvc-context.xml almost empty.
Is this common? What is the reccomended split between what is defined in top-level contexts, and what gets defined in child contexts?
Given that spring-mvc defines a series of controllers, which I want to mark as #Secured, how will these be processed if the controller is not visible to the security context?
Do I need to move my <mvc:annotation-driven /> from the servlet.xml to the global applicationContext.xml?
Do I need additional configuration within the spring-mvc-servlet.xml to tell it to participate in Spring security?
I've read the documentation on Spring-MVC, but there's very few specifics on how to configure this.
Additionally, the Spring OAuth examples seem to define everything within a single config file, which doesn't seem very real-world, and seems to contradict other examples I've read.

First: the beans defined within applicationContext.xml (ContextLoaderListener) can not access the one defined in spring-mvc-servlet.xml (DispatcherServlet) but not the other way around.
You asked:
Given that spring-mvc defines a series of controllers, which I want to mark as #Secured, how will these be processed if the controller is not visible to the security context?
So this works without problems, because the controllers must be defined in the spring-mvc-servlet.xml, so they "see" the Spring Security stuff defined in applicationContext.xml
Do I need to move my from the servlet.xml to the global applicationContext.xml?
No
Do I need additional configuration within the spring-mvc-servlet.xml to tell it to participate in Spring security?
No
... which leaves the spring-mvc-context.xml almost empty. Is this common?
The spring-mvc-context.xml should contain every thing that is related to Web Stuff (except secrutiy). So the common parts of the spring-mvc-context.xml are component scan for #Controller, some Interceptors (mvc:interceptors), mvc:resources, mvc:default-servlet-handler, mvc:view-controller, ReloadableResourceBundleMessageSource, CookieLocaleResolver, .SimpleMappingExceptionResolver...
BTW: If you use component scan, then you need two of them, one at applicationContext.xml to scan for #Service #Repository and #Component (But not #Controller) and a second in spring-mvc-context.xml that only scan for #Controller!
#See also this question: ContextLoaderListener or not? It discuss the theme from an other point of view.

Related

Java Spring: Trying to configure webflow by xml, can't find dispatcher-servlet.xml

I am just starting out trying to see what spring flow is about.This book that i have is describing how i have to configure the dispathcer-servlet.xml and create some flows in there.
Problem is i cannot find this dispatcher-servlet.xml file in my java spring application to start the configuration.Where is it?
I searched on here and on google but i only find people who already have n dispatcher-servlet.xml file and they are asking how to configure it.
Any help would be greatly appreciated!
You don't need dispatcher-servlet.xml to configure DispatcherServlet. It's just a default name Spring will look for if not defined. You can define config in contextConfigLocation init-param when defining DispatcherServlet in web.xml. Or, if you have single DispatcherServlet, you can use global spring xml configuration. If you have a working java spring application, look for
<context-param>
<param-name>contextConfigLocation</param-name>
...
</context-param>
or
<servlet>
<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
...
</servlet>
in your web.xml
If you don't find it in web.xml, you will have to configure it yourself. I also think that the book might be for webflow 1.x. Try using official Spring Webflow reference, it's a good starting point, and is up-to-date:
https://docs.spring.io/spring-webflow/docs/current/reference/html/
Specifically, for Dispatcher servlet configuration look at this chapter:
https://docs.spring.io/spring-webflow/docs/current/reference/html/spring-mvc.html#spring-mvc-config-web.xml

Understanding Spring MVC application contexts

I am trying to understand basic Spring MVC stuff.
Consider the web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!-- 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</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>
</web-app>
Also consider below diagram that is given in the official docs:
Doubts:
What is precise types of instances created by DispatcherServlet and ContextLoaderListener? The confusion arise, since some articles online say ContextLoaderListener creates ApplicationContext instance and DispatcherServlet creates WebApplicationContext instance. But, after looking at their source, I feel both create instance of type WebApplicationContext, and since its subtype of ApplicationContext, some articles say ContextLoaderListener creates ApplicationContext instance. Is it also the case that only ApplicationContext-provided functionalities of instance created by ContextLoaderListener are used, and hence the article say ContextLoaderListener creates ApplicationContext, but not WebApplicationContext
`
Is it like the WebApplicaionContext created by ContextLoaderListener is what usually online articles refer to as "root" WebApplicationContext, just to differentiate it from WebApplicationContexts created by DispatcherServlet?
Reading this article, I understood that DispatcherServlet loads from [servlet-name]-servlet.xml file which should load "web tier components". On the other hand ContextLoaderListener loads "middle tier components". Given that there can be only one ContextLoaderListener in web.xml (right?), there should be only single single set of "middle tier components", whereas, since there can be multiple DispatcherServlets, there can be multiple sets of "web tier components". Is this right? If so, then why the above diagram shows multiple WebApplicationContexts of middle tier components and single WebApplicationContext of web tier component?
Both WebApplicationContext and ApplicationContext are interfaces, so when documentation says "creates ApplicationContext instance" it means that it creates an object instance of a class that implements ApplicationContext. Whether that class also implements WebApplicationContext is besides the point. It may. It may not. Doesn't invalidate the statement that it "creates ApplicationContext instance".
what usually online articles refer to as "root"
No, it's what the javadoc of ContextLoaderListener itself refers to a root:
Performs the actual initialization work for the root application context.
why the above diagram shows multiple WebApplicationContexts of middle tier components and single WebApplicationContext of web tier component?
It only shows a single WebApplicationContext of web tier component because the diagram is for a single DispatcherServlet.
Just because ContextLoaderListener only creates a single root context, doesn't mean that multiple contexts couldn't be created by other means. The diagram accurately shows what is possible.

Spring - how to load properties prior to context loader getting called

I would like to read the properties from my properties files in my custom contextLoadListener. However my listener class gets executed prior to spring loading the properties file. How do I get my 'rdbaccess.properties' loaded prior to the execution of my CustomContextLoaderListener class? Below is my relevant parts of the configuration.
In web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>
com.my.package.setup.CustomContextLoaderListener
</listener-class>
</listener>
In spring-servlet.xml
<context:component-scan base-package="com.my.package" />
<context:property-placeholder location="classpath:rdbaccess.properties" />
The ContextLoader(Listener) is the instance that initializes your Spring context. This has to be the very first thing happening in the lifecycle of your (Spring) application, at all. It's not possible that your properties get loaded before the context has initialized. You can't decorate your living room before you've built the house :)
However, regarding the name of your properties, you probably need to inject the database credentials into your beans. This issue can be resolved with another approach, which is a lot cleaner in my opinion, especially for applications that run in multiple environments.
Instead of keeping/putting the credentials in your classpath, you should configure properties that change for each environment (local, development, integration, production ...) right there. For example, if you run a Tomcat, then put a properties file into the conf directory containing your database credentials. You can access that file in your custom ContextLoaderListener and provide the properties to your beans through a class with a static properties map, for instance.
Try adding a custom context initializer to load your properties file, like so:
https://gist.github.com/rponte/3989915

WEB-INF/myproject-servlet.xml versus WEB-INF/web.xml

I spent few days fixing bugs in my Spring project. For really long time, my main problems in error log were:
Bean already exists
I have got two files:
WEB-INF/myproject-servlet.xml
WEB-INF/web.xml
in first one, I can put following input (let's assume I have got an application to manage animals in zoo):
<context:component-scan base-package="com.my.package.animals" />
with that (as well as I understand) we are enabling Spring beans auto-discovery. So now, when we run our application, Spring will take all classes from this package, later it will go through all config files in resources directory and will inititliaze all beans (placed in config files, which are associated with a given package).
The second one, web.xml includes lines like this
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myproject-servlet.xml</param-value>
</context-param>
I can also put path to my config files, for instance:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:animals-config.xml</param-value>
</context-param>
So now, I have 'auto-detecting' in myproject-servlet.xml and I have got context-param in web.xml for the same objects.
My question is, is it possible, that errors "Bean already exists" are coming from this? I am almost sure that yes, I checked all beans ids and there is no duplicates.
So I have another question. What is a good approach of doing that? When I create new config file, where I should inform my application about that? In myproject-servlet.xml or web.xml. I really need to clean up my application and I will start with that.
I checked some examples and people rather do not put more than one <context-param> in web.xml file Simple example
Thank you in advance
Ok, I am really close to solve my problem.
Let's assume I have got two packages:
com.my.pckg.a
com.my.pckg.b
with classes
com.my.pckg.a.ClassA
com.my.pckg.b.ClassB
I added in myproject-servlet.xml following line:
<context:component-scan base-package="com.my.pckg.a" />
I have got a config file myconfig.xml and inside I have got beans based on classes ClassA and ClassB.
Let's say we have got beans with ids as follows:
ClassA: ida1, ida2
ClassB: idb1, idb2
So, I am running my jetty server and the question is:
Which beans will be initialized? I declared only package com.my.pckg.a, so from myconfig.xml, spring should load only ida1 and ida2 but this file includes also beans for another class.
So finally... ?
Finally, I suppose I find a problem. In web.xml file I have got a line:
<context:component-scan base-package="com.dirty.pckg" />
in this package I have got a class DirtyClass with #Controller annotation. One of the fields of this class is:
private static ApplicationContext context = new ClassPathXmlApplicationContext("dirty-config.xml");
So, when my application is getting up, Spring takes DirtyClass (because it has #) and maps it. Because os static modifier of context it triggers reading dirty-config.xml. That's way I could not understand why my code behaves in strange way.
The web.xml file is the configuration of your web application. It is not related to Spring.
The contextConfigLocation context-param is the one spring listener use to search for Spring configuration files. It is spring related.
You can have multiple spring configuration files that you register in your web.xml but these files must not define the same bean (bean id must be different). You can also have only one spring configuration file that will it self include other configuration files as described here : http://www.mkyong.com/spring/load-multiple-spring-bean-configuration-file/
Answer to the other question :
When you added the component scan, you ask spring to scan the package com.my.pckg.a for annotation like #Service, #Component, ... The component scan is not a filter for the rest of the configuration it is a configuration itself. So the fact that you added component scan, will not change the behaviour of myconfig.xml. Both ida1, ida2 and idb1, idb2 will be instanciated.
I don't really get what you are trying to accomplished with your configuration files. Maybe if you explain your needs, we could help you set up the right config for you.
web.xml is configuration file. It has classes(like listeners, filters,filter-mapping, servlet,servlet-mapping), resources and configuration(like context param, diplayname, error-page, session-config) of the application and how the web server uses them to handle web requests. When the web server receives a request for the application, it uses web.xml to map the URL of the request to the code that is supposed to handle the request.
Coming to what should be in web.xml and what should be in myproject-servlet.xml:
1)In Spring, ApplicationContext can be hierarchical. One ApplicationContext can have multiple child ApplicationContexts and can only have one parent. Beans in child ApplicationContext can access the beans in parent.
2)DispatcherServlet dispatches requests to handlers(controllers), with configurable handler mappings, view resolution configuration(what view template you use for the application for example jsp).
Keeping the two points in mind, our web.xml should be like:
<!--Root web application context(Parent context) - beans for service or persistence layer should be in this -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:service-layer-beans.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<!--DispatcherServlet loads its configuration into its own context(chile context) and refers Root web application context as a parent, so it have access to beans in parent context and can override it but not vice versa.-->
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myproject-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>

ContextLoaderListener or not?

A standard spring web application (created by Roo or "Spring MVC Project" Template) create a web.xml with ContextLoaderListener and DispatcherServlet. Why do they not only use the DispatcherServlet and make it to load the complete configuration?
I understand that the ContextLoaderListener should be used to load the stuff that is not web relevant and the DispatcherServlet is used to load the web relevant stuff (Controllers,...). And this result in two contexts: a parent and a child context.
Background:
I was doing it this standard way for several years.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:META-INF/spring/applicationContext*.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>
<!-- Handles Spring requests -->
<servlet>
<servlet-name>roo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/spring/webmvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
This often caused problems with the two contexts and the dependencies between them. In the past I was always able to find a solution, and I have the strong feeling that this makes the software structure/architecture always better. But now I am facing a problem with the events of the both contexts.
-- However this makes my rethink this two context pattern, and I am asking myself: why should I bring myself into this trouble, why not loading all spring configuration files with one DispatcherServlet and removing the ContextLoaderListener completely. (I still will to have different configuration files, but only one context.)
Is there any reason not to remove the ContextLoaderListener?
In your case, no, there's no reason to keep the ContextLoaderListener and applicationContext.xml. If your app works fine with just the servlet's context, that stick with that, it's simpler.
Yes, the generally-encouraged pattern is to keep non-web stuff in the webapp-level context, but it's nothing more than a weak convention.
The only compelling reasons to use the webapp-level context are:
If you have multiple DispatcherServlet that need to share services
If you have legacy/non-Spring servlets that need access to Spring-wired services
If you have servlet filters that hook into the webapp-level context (e.g. Spring Security's DelegatingFilterProxy, OpenEntityManagerInViewFilter, etc)
None of these apply to you, so the extra complexity is unwarranted.
Just be careful when adding background tasks to the servlet's context, like scheduled tasks, JMS connections, etc. If you forget to add <load-on-startup> to your web.xml, then these tasks won't be started until the first access of the servlet.
You can configure the application context the other way around as well. E.g. in order to make the OpenEntityManagerInViewFilter work. Setup the ContextLoaderListener and then configure your DispatcherServlet with:
<servlet>
<servlet-name>spring-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
</servlet>
Just make sure that the contextConfigLocation parameter value is empty.
I want to share what I've done on my Spring-MVC application:
On the we-mvc-config.xml I added just the classes annotated with #Controller:
<context:component-scan base-package="com.shunra.vcat">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
On the applicationContext.xml files I added all the rest:
<context:component-scan base-package="com.shunra.vcat">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>

Categories