Spring MVC
&
Spring Security
Craig Walls
About you...
• By show of hands...
• Java 6? Java 5? Java 1.4? Java 1.3? Java 1.2-?
• C#? Ruby? Groovy? Scala? Erlang? Python?
• Spring 1.x? Spring 2.0.x? Spring 2.5.x?
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
About me...
• Professionally developing software for almost 14
years
• Java developer for most of that time
• Telecom, finance, retail, education, software
• Now at Semantra, Inc. developing natural language
business intelligence solution
• Author of Spring in Action and XDoclet in Action
• Spring fanatic
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Agenda
• Spring MVC
• Review of Spring MVC pre-2.5
• What’s new in Spring 2.5’s MVC framework
• Spring Security
• Review of Acegi Security 1.0.x
• Introducing Spring Security 2.0
• Summary
• Q&A
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring MVC
The “Other” Web Framework
Why Spring MVC?
• Spring was first released in June 2003
• 1.0 final in March 2004
• MVC choices were slim
• At the time, Struts led the pack
• WebWork was the next best choice
• Spring could do it better
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring MVC vs. Struts
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring MVC vs. Struts
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
DispatcherServlet
In WEB-INF/[Link]:
<servlet>
<servlet-name>roadrantz</servlet-name>
<servlet-class>
[Link]
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>roadrantz</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring’s Controllers
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
A Very Basic Controller
public class HomePageController extends AbstractController {
protected ModelAndView handleRequestInternal(
HttpServletRequest request, HttpServletResponse response)
throws Exception {
List recentRants = [Link]();
return new ModelAndView("home", "rants", recentRants);
}
private RantService rantService;
public void setRantService(RantService rantService) {
[Link] = rantService;
}
}
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Command Controller
public class RantsForVehicleController extends AbstractCommandController {
public RantsForVehicleController() {
setCommandClass([Link]);
setCommandName("vehicle");
}
protected ModelAndView handle(HttpServletRequest request,
HttpServletResponse response, Object command,
BindException errors) throws Exception {
Vehicle vehicle = (Vehicle) command;
Map model = [Link]();
[Link]("rants", [Link](vehicle));
[Link]("vehicle", vehicle);
return new ModelAndView("rantsForVehicle", model);
}
private RantService rantService;
public void setRantService(RantService rantService) {
[Link] = rantService;
}
}
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Form Controller
public class AddRantFormController extends SimpleFormController {
private static final String[] ALL_STATES = { "AL", "AK", ... };
public AddRantFormController() {
setCommandClass([Link]);
setCommandName("rant");
}
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
Rant rantForm = (Rant) [Link](request);
[Link](new Vehicle());
return rantForm;
}
protected Map referenceData(HttpServletRequest request) throws Exception {
Map referenceData = new HashMap();
[Link]("states", ALL_STATES);
return referenceData;
}
protected ModelAndView onSubmit(Object command, BindException bindException)
throws Exception {
Rant rant = (Rant) command;
[Link](rant);
return new ModelAndView(getSuccessView());
}
private RantService rantService;
public void setRantService(RantService rantService) {
[Link] = rantService;
}
}
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Configuring Controllers
In WEB-INF/[Link]:
<bean id="homePageController"
class="[Link]">
<property name="rantService" ref="rantService" />
</bean>
<bean id="rantsForVehicleController"
class="[Link]">
<property name="rantService" ref="rantService" />
</bean>
<bean id="addRantController"
class="[Link]">
<property name="formView" value="addRant" />
<property name="successView" value="rantAdded" />
<property name="rantService" ref="rantService" />
</bean>
<bean id="loginController"
class="[Link]" />
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
Response
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Follow That Request
Request
Response
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Handler Mappings
• Map requested URL patterns to controllers
• Comes in 5 flavors:
• BeanNameUrlHandlerMapping
• CommonsPathMapHandlerMapping
• ControllerClassNameHandlerMapping
• DefaultAnnotationHandlerMapping
• SimpleUrlHandlerMapping
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
SimpleUrlHandlerMapping
In WEB-INF/[Link]:
<bean id="urlMapping"
class="[Link]">
<property name="mappings">
<value>
/[Link]=loginController
/[Link]=homePageController
/[Link]=rantsForVehicleController
/[Link]=addRantController
</value>
</property>
</bean>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
ControllerClassNameHandlerMapping
In WEB-INF/[Link]:
<bean id="urlMapping"
class="[Link].
ControllerClassNameHandlerMapping" />
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
View Resolvers
• Map logical view names in ModelAndView to actual
view implementation (ex. a JSP file)
• Come in 9 flavors:
• BeanNameViewResolver
• FreeMarkerViewResolver
• InternalResourceViewResolver
• JasperReportsViewResolver
• ResourceBundleViewResolver
• VelocityLayoutViewResolver
• VelocityViewResolver
• XmlViewResolver
• XsltViewResolver
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
InternalResourceViewResolver
In WEB-INF/[Link]:
<bean class="[Link].
InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring JSP Tag Libraries
• Two tag libraries:
• General purpose
• Form-bindin
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
JSP Tag Libraries
In WEB-INF/[Link]:
<%@ page contentType="text/html" %>
<%@ taglib prefix="form" uri="[Link] %>
<%@ taglib prefix="spring" uri="[Link] %>
<%@ taglib prefix="rr" tagdir="/WEB-INF/tags" %>
<html>
<body>
<h2>Enter a rant...</h2>
<form:form method="POST" action="[Link]" commandName="rant">
<b><spring:message code="[Link]" /></b>
<rr:stateSelection path="[Link]" />
<form:errors path="[Link]" cssClass="error"/><br>
<b><spring:message code="[Link]" /></b>
<form:input path="[Link]" />
<form:errors path="[Link]" cssClass="error"/><br>
<b><spring:message code="[Link]" /></b>
<form:errors path="rantText" cssClass="error"/><br>
<form:textarea path="rantText" rows="5" cols="50" />
<input type="submit"/>
</form:form>
</body>
</html>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring MVC Summary
• DispatcherServlet is front controller
• Handler mappings maps URLs to controllers
• Controllers process requests
• Returning results and logical view name in
ModelAndView
• View resolvers map logical view names to actual
views
• Two tag libraries
• Form-binding and general-purpose
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
And then there’s 2.5
• Annotation-driven
• Much less XML
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring MVC 2.5 XML
In WEB-INF/[Link]:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="[Link]
xmlns:xsi="[Link]
xmlns:context="[Link]
xmlns:p="[Link]
xsi:schemaLocation="[Link]
[Link]
[Link]
[Link]
<context:component-scan base-package="[Link]" />
<bean class="[Link].
DefaultAnnotationHandlerMapping" />
<bean class="[Link]"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
</beans>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
<context:component-scan>
• Scans all beans in a package and sub-packages
• Automatically registers beans from classes that are
annotated with certain stereotype annotations
• @Aspect
• @Component
• @Controller
• @Repository
• @Service
• Automatically autowires any properties, method
parameters, or constructor args that are annotated with
@Autowired
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
DefaultAnnotationHandlerMapping
• Maps request URLs to controllers based on
@RequestMapping annotation
• @RequestMapping can be placed at class-level or
at method-level
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Revisiting the Controller
@Controller
@RequestMapping("/[Link]")
public class HomePageController {
@RequestMapping(method = [Link])
public String showHomePage(ModelMap model) {
[Link]([Link]());
return "home";
}
@Autowired
RantService rantService;
}
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
A Form Controller ?
@Controller
@RequestMapping("/[Link]")
public class AddRantFormController {
@RequestMapping(method = [Link])
public String setupForm(ModelMap model) {
return "addRant";
}
@ModelAttribute("rant")
public Rant setupRant() {
Rant rant = new Rant();
[Link](new Vehicle());
return rant;
}
@ModelAttribute("states")
public String[] getAllStates() { return WebConstants.ALL_STATES;}
@RequestMapping(method = [Link])
protected String addRant(@ModelAttribute("rant")
Rant rant) {
[Link](rant);
return "rantAdded";
}
@Autowired
RantService rantService;
}
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring MVC 2.5 Summary
• <context:component-scan> lets you skip writing
XML to configure controllers (and other stuff)
• DefaultAnnotationHandlerMapping lets you move
the URL mappings into the controllers themselves
• No more controller hierarchy...controllers are now
just annotated POJOs
• @Controller, @RequestMapping, @ModelAttribute
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring Security
Save the Fairies
Acegi Security for Spring
• Created by Ben Alex in 2003
• 1.0 in March 2004
• Applies security rules using Servlet Filters and
Spring AOP
• Extremely powerful and flexible
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
What Acegi Offers
• Declarative Security
• Keeps security details out of your code
• Authentication and Authorization
• Against virtually any user store
• Support for anonymous sessions, concurrent
sessions, remember-me, channel-enforcement, and
much more
• Spring-based, but can be used for non-Spring web
frameworks
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
The Downside of Acegi
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Acegi Guilty of Fairycide?
“Every time you use Acegi...A fairy dies.”
- Daniel Deiphouse
[Link]
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Acegi’s Filter Proxy
In WEB-INF/[Link]:
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>[Link]</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>[Link]</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Filter Proxy
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Example Acegi Config
<?xml version="1.0" encoding="UTF-8"?> </property>
<beans xmlns="[Link] </bean>
xmlns:xsi="[Link] <bean id="httpSessionIntegrationFilter"
xsi:schemaLocation="[Link] class="[Link]">
[Link] <property name="forceEagerSessionCreation" value="true" />
<bean id="filterChainProxy" </bean>
class="[Link]"> <bean id="filterSecurityInterceptor"
<property name="filterInvocationDefinitionSource"> class="[Link]">
<value> <property name="authenticationManager" ref="authenticationManager" />
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON <property name="accessDecisionManager" ref="accessDecisionManager" />
PATTERN_TYPE_APACHE_ANT <property name="objectDefinitionSource">
/**=channelProcessingFilter,httpSessionIntegrationFilter, <value>
logoutFilter,authenticationProcessingFilter,rememberMeProcessingFilter, CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
anonymousProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor PATTERN_TYPE_APACHE_ANT
</value> /[Link]=ROLE_BOOGER
</property> </value>
</bean> </property>
<bean id="authenticationProcessingFilter" </bean>
class="[Link]"> <bean id="anonymousProcessingFilter"
<property name="authenticationManager" ref="authenticationManager"/> class="[Link]">
<property name="authenticationFailureUrl" value="/[Link]?login_error=1" /> <property name="key" value="foobar" />
<property name="defaultTargetUrl" value="/" /> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" />
<property name="filterProcessesUrl" value="/j_acegi_security_check" /> </bean>
<property name="rememberMeServices" ref="rememberMeServices" /> <bean id="anonymousAuthenticationProvider"
</bean> class="[Link]">
<bean id="authenticationManager" <property name="key" value="foobar" />
class="[Link]"> </bean>
<property name="providers"> ! <bean id="rememberMeProcessingFilter"
<list> ! class="[Link]">
<ref bean="daoAuthenticationProvider" /> ! <property name="rememberMeServices" ref="rememberMeServices" />
<ref bean="anonymousAuthenticationProvider" /> ! <property name="authenticationManager" ref="authenticationManager" />
<ref bean="rememberMeAuthenticationProvider" /> ! </bean>
</list> ! <bean id="rememberMeServices"
</property> ! class="[Link]">
</bean> ! <property name="userDetailsService" ref="userDetailsService" />
<bean id="daoAuthenticationProvider" ! <property name="key" value="roadRantz" />
class="[Link]"> ! </bean>
<property name="userDetailsService" ! <bean id="rememberMeAuthenticationProvider"
ref="userDetailsService" /> ! class="[Link]">
</bean> ! <property name="key" value="roadRantz" />
<bean id="userDetailsService" ! </bean>
class="[Link]"> <bean id="logoutFilter"
<property name="dataSource" ref="dataSource" /> class="[Link]">
<property name="usersByUsernameQuery" <constructor-arg value="/[Link]" />
value="SELECT email as username, password, 'true' FROM Motorist WHERE email=?" /> <constructor-arg>
<property name="authoritiesByUsernameQuery" <list>
value="SELECT email as username, privilege FROM Motorist_Privileges mp, Motorist m WHERE <ref bean="rememberMeServices"/>
mp.motorist_id = [Link] AND [Link]=?" /> <bean class="[Link]"/>
</bean> </list>
<bean id="authenticationEntryPoint" </constructor-arg>
class="[Link]"> </bean>
<property name="loginFormUrl" value="/[Link]" /> <bean id="channelProcessingFilter"
<property name="forceHttps" value="true" /> class="[Link]">
</bean> <property name="channelDecisionManager" ref="channelDecisionManager" />
<bean id="accessDecisionManager" <property name="filterInvocationDefinitionSource">
class="[Link]"> <value>
<property name="allowIfAllAbstainDecisions" value="false" /> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
<property name="decisionVoters"> PATTERN_TYPE_APACHE_ANT
<list> /[Link]=REQUIRES_SECURE_CHANNEL
<bean class="[Link]" /> /j_acegi_security_check*=REQUIRES_SECURE_CHANNEL
</list> /**=REQUIRES_INSECURE_CHANNEL
</property> </value>
</bean> </property>
<bean id="exceptionTranslationFilter" </bean>
class="[Link]"> </beans>
<property name="authenticationEntryPoint"
ref="authenticationEntryPoint" />
<property name="accessDeniedHandler">
<bean class="[Link]">
<property name="errorPage" value="/[Link]" />
</bean>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
What was in that XML?
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Acegi’s Moving Parts
• Security Interceptor - Determines what resources are to
be secured--and how they are to be secured
• Filters - Intercept requests and apply various aspects of
security
• Authentication, Remember-me, Concurrent sessions, Anonymous, Logout, Channel-
processing, Integration, etc
• Authentication Manager - Determines who the user is and
what authorities they are granted
• Authentication Providers - Used by the authentication manager to retrieve user
information. Includes DAO-based, anonymous, and remember-me providers
• Authorization Manager - Allows/Disallows access to a
resource, givena user’s granted authorities and a
resource’s required authorization
• Usually defers to one or more access decision voters to help decide
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring Security 2.0
• All the same goodness of Acegi...and more
• Exploits custom Spring configuration namespaces
(ala Spring 2.0)
• Acegi’s complex configuration details now hidden
behind simpler XML elements
• Offers auto-configuration
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Spring Security 2.0 Filter
In WEB-INF/[Link]:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>[Link]</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Simpler Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="[Link]
xmlns:beans="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]
[Link]
[Link]
<http auto-config="true">
<intercept-url pattern="/[Link]" access="ROLE_MOTORIST" />
<intercept-url pattern="/[Link]" requires-channel="http" />
<intercept-url pattern="/[Link]" requires-channel="https" />
<form-login login-page="/[Link]" />
</http>
<authentication-provider user-service-ref="userService" />
<jdbc-user-service id="userService" data-source-ref="dataSource" />
</beans:beans>
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
<http>:The Magic Element
• The central configuration element for web security
• <intercept-url> declares pages to be secured (and
how)
• <form-login> refers to a login page
• Other sub-elements provide support for HTTP Basic
authentication, Logout, Remember-Me, and
Anonymous sessions...or...
• “auto-config” does all of that for you!
• In fact, it can also automatically create a login form for you
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
<authentication-provider>
• Declares an authentication provider
• Refers to a user details service
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
<jdbc-user-service>
• Declares a JDBC-based user service
• Retrieves user information and authorization details from
a database table.
• Assumes “SELECT username,password,enabled FROM users WHERE
username=?” for user details.
• Assumes “SELECT username,authority FROM authorities WHERE username=?” for
authorization info.
• As of 2.0-m4, can’t override SQL. See and (please) vote for SEC-703.
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Summary
Is it over yet?
Spring MVC & Security
• Spring continues to evolve and improve
• This especially applies to Spring MVC and Spring
Security
• Spring MVC 2.5 has embraced annotations for
configuration, reducing the amount of XML required
• Spring Security 2.0 has dramatically decreased the
amount of XML required by employing security-specific
configuration elements
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Example Code & Slides
• RoadRantz example (from SiA) updated to use the
latest Spring goodness:
• svn://[Link]/SiA/trunk/RoadRantz
• Updated to use Spring MVC 2.5, Spring Security
2.0 nightly build (with SEC-703 patch), and Spring
WebFlow 2.0-m4
• Work in progress--No guarantees
• These slides: [Link]
E-mail: craig@[Link] Blog: [Link] Source Code: svn://[Link]/SiA svn://[Link]/habuma
Q&A
Time to Play Stump the Speaker