diff --git a/docs/guides/src/asciidoc/form-includes/verify-app.asc b/docs/guides/src/asciidoc/form-includes/verify-app.asc
new file mode 100644
index 0000000000..c8127c2c7a
--- /dev/null
+++ b/docs/guides/src/asciidoc/form-includes/verify-app.asc
@@ -0,0 +1,6 @@
+Verify the application is working:
+
+* Navigate to http://localhost:8080/sample/ You should see a login form
+* Enter the *username* _user_, the *password* _password_, and click the *Login* button. You should now see the main application.
+* Try clicking on the Compose link and creating a message. The message details should be displayed.
+* Now click on the Inbox link and see the message listed. You can click on the summary link to see the details displayed again.
diff --git a/docs/guides/src/asciidoc/form.asc b/docs/guides/src/asciidoc/form.asc
new file mode 100644
index 0000000000..bcc9c19750
--- /dev/null
+++ b/docs/guides/src/asciidoc/form.asc
@@ -0,0 +1,255 @@
+= Creating a Custom Login Form
+:author: Rob Winch
+:starter-appname: hellomvc-jc
+:completed-appname: form-jc
+:verify-starter-app-include: form-includes/verify-app.asc
+
+This guide builds off of link:hellomvc.html[Hello Spring MVC Security Java Config] to explain how to configure and use a custom login form with Spring Security Java Configuration.
+
+include::hello-includes/setting-up-the-sample.asc[]
+
+= Overriding the default configure(HttpSecurity) method
+
+As we saw in link:hellomvc.html[Hello Spring MVC Security Java Config], Spring Security's `WebSecurityConfigurerAdapter` provides some convenient defaults to get our application
+up and running quickly. However, our login form does not match the rest of our application. Let's see how we can update our configuration to use a custom form.
+
+== Default configure(HttpSecurity)
+
+The default configuration for the configure(HttpSecurity) method can be seen below:
+
+[source,java]
+----
+protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .anyRequest().authenticated() <1>
+ .and()
+ .formLogin() <2>
+ .and()
+ .httpBasic(); <3>
+}
+----
+
+The configuration ensures that:
+
+<1> every request requires the user to be authenticated
+<2> form based authentication is supported
+<3> HTTP Basic Authentication is supported
+
+== Configuring a custom login page
+
+We will want to ensure we compensate for overriding these defaults in our updates. Open up the `SecurityConfig` and insert the configure method as shown below:
+
+.src/main/java/org/springframework/security/samples/config/SecurityConfig.java
+[source,java]
+----
+// ...
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login");
+ }
+
+ // ...
+}
+----
+
+The line `loginPage("/login")` instructs Spring Security
+
+* when authentication is required, redirect the browser to */login*
+* we are in charge of rendering the login page when */login* is requested
+* when authentication attempt fails, redirect the browser to */login?error* (since we have not specified otherwise)
+* we are in charge of rendering a failure page when */login?error* is requested
+* when we successfully logout, redirect the browser to */login?logout* (since we have not specified otherwise)
+* we are in charge of rendering a logout confirmation page when */login?logout* is requested
+
+Go ahead and start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. In many browsers you will see an error similar to *This webpage has a redirect loop*. What is happening?
+
+== Granting access to unauthenticated users
+
+The issue is that Spring Security is protecting access to our custom login page. In particular the following is happening:
+
+* We make a request to our web application
+* Spring Security sees that we are not authenticated
+* We are redirected to */login*
+* The browser requests */login*
+* Spring Security sees that we are not authenticated
+* We are redirected to */login* ...
+
+To fix this we need to instruct Spring Security to allow anyone to access the */login* URL. We can easily do this with the following updates:
+
+.src/main/java/org/springframework/security/samples/config/SecurityConfig.java
+[source,java]
+----
+// ...
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login")
+ .permitAll();
+ }
+
+ // ...
+}
+----
+
+The `permitAll()` statement instructs Spring Security to allow any access to any URL (i.e. */login* and */login?error*) associated to `formLogin()`.
+
+NOTE: Granting access to the `formLogin()` URLs is not done by default since Spring Security needs to make certain assumptions about what is allowed and what is not. To be secure, it is best to ensure granting access to resources is explicit.
+
+Start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. You should now get a 404 error stating that */login* cannot be found.
+
+= Creating a login page
+
+Within Spring Web MVC there are two steps to creating our login page:
+
+* Creating a controller
+* Creating a view
+
+== Configuring a login view controller
+
+Within Spring Web MVC, the first step is to ensure that we have a controller that can point to our view. Since our project adds the *messages-jc* project as a dependency and it contains a view controller for */login* we do not need to create a controller within our application. For reference, you can see the configuration below:
+
+[source,java]
+----
+// ...
+
+@EnableWebMvc
+@ComponentScan("org.springframework.security.samples.mvc")
+public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
+
+ // ...
+
+ @Override
+ public void addViewControllers(ViewControllerRegistry registry) {
+ registry.addViewController("/login").setViewName("login");
+ registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
+ }
+
+ @Bean
+ public InternalResourceViewResolver jspxViewResolver() {
+ InternalResourceViewResolver result = new InternalResourceViewResolver();
+ result.setPrefix("/WEB-INF/views/");
+ result.setSuffix(".jspx");
+ return result;
+ }
+}
+----
+
+== Creating a login view
+
+Our existing configuration means that all we need to do is create a *login.jspx* file with the following contents:
+
+.src/main/webapp/WEB-INF/views/login.jspx
+[source,xml]
+----
+
+
+
+
+Please Login
+
+
+
+ <1>
+
+
+
+
+
+----
+
+<1> The URL we submit our username and password to is the same URL as our login form (i.e. */login*), but a *POST* instead of a *GET*.
+<2> When authentication fails, the browser is redirected to */login?error* so we can display an error message by detecting if the parameter *error* is non-null.
+
+IMPORTANT: Do not display details about why authentication failed. For example, we do not want to display that the user does not exist as this will tell an attacker that they should try a different username.
+
+<3> When we are successfully loged out, the browser is redirected to */login?logout* so we can display an logout success message by detecting if the parameter *logout* is non-null.
+<4> The username should be present on the HTTP parameter username
+<5> The password should be present on the HTTP parameter password
+
+TIP: We use Spring Web MVC's tag to automatically add the CSRF token to our form. We could also manually add the CSRF token using ``.
+
+Start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. We now see our login page, but it does not look very pretty. The issue is that we have not granted access to the css files.
+
+== Grant access to remaining resources
+
+We need to update our configuration to allow anyone to access our resources and our logout pages. Update the configuration as shown below:
+
+.src/main/java/org/springframework/security/samples/config/SecurityConfig.java
+[source,java]
+----
+// ...
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .antMatchers("/resources/**").permitAll() <1>
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login")
+ .permitAll()
+ .and()
+ .logout() <2>
+ .permitAll();
+ }
+
+ // ...
+}
+----
+
+<1> This allows anyone to access a URL that begins with */resources/*. Since this is where our css, javascript, and images are stored all our static resources are viewable by anyone.
+<2> As you might expect, `logout().permitAll()` allows any user to request logout and view logout success URL.
+
+
+Start up the server and try visiting http://localhost:8080/sample/ to see the updates to our configuration. We now see a custom login page that looks like the rest of our application.
+
+* Try entering an invalid username and password. You will see our error message is displayed.
+* Try entering a valid username and password. You will be authenticated successfully.
+* Try clicking the Log Out button. You will see our logout success message
diff --git a/docs/guides/src/asciidoc/hello-includes/setting-up-the-sample.asc b/docs/guides/src/asciidoc/hello-includes/setting-up-the-sample.asc
index 757ab046e1..dcdf5803d6 100644
--- a/docs/guides/src/asciidoc/hello-includes/setting-up-the-sample.asc
+++ b/docs/guides/src/asciidoc/hello-includes/setting-up-the-sample.asc
@@ -1,6 +1,6 @@
== Setting up the sample
-This section outlines how to setup a workspace within STS so that you can follow along with this guide. The <> section outlines generic steps for how to apply Spring Security to your existing application. While you could simply apply the steps to your existing application, we encourage you to follow along with this guide as is to reduce the complexity.
+This section outlines how to setup a workspace within STS so that you can follow along with this guide. The next section outlines generic steps for how to apply Spring Security to your existing application. While you could simply apply the steps to your existing application, we encourage you to follow along with this guide as is to reduce the complexity.
=== Obtaining the sample projects
diff --git a/docs/guides/src/asciidoc/hellomvc.asc b/docs/guides/src/asciidoc/hellomvc.asc
index 0ef4d7f56e..7053b7bd2b 100644
--- a/docs/guides/src/asciidoc/hellomvc.asc
+++ b/docs/guides/src/asciidoc/hellomvc.asc
@@ -142,4 +142,4 @@ include::hello-includes/basic-authentication.asc[]
== Conclusion
-You should now now how to secure your application using Spring Security without using any XML.
+You should now now how to secure your application using Spring Security without using any XML. Next, we will see how to link:form.html[customize our login form].
diff --git a/docs/guides/src/asciidoc/index.asc b/docs/guides/src/asciidoc/index.asc
index aa5c20ffe7..9d9721e9a4 100644
--- a/docs/guides/src/asciidoc/index.asc
+++ b/docs/guides/src/asciidoc/index.asc
@@ -10,3 +10,7 @@ These are the most basic starting points for using a web based application.
* link:helloworld.html[Hello Spring Security Java Config] - demonstrates how to integrate Spring Security with an existing application that does not already use Spring
* link:hellomvc.html[Hello Spring MVC Security Java Config] - demonstrates how to integrate Spring Security with an existing Spring MVC application
+
+== Simple Customization
+
+* link:hellomvc.html[Creating a custom login form] - demonstrates how to create a custom login form
\ No newline at end of file
diff --git a/samples/form-jc/build.gradle b/samples/form-jc/build.gradle
new file mode 100644
index 0000000000..f2696c79ca
--- /dev/null
+++ b/samples/form-jc/build.gradle
@@ -0,0 +1,25 @@
+apply from: WAR_SAMPLE_GRADLE
+
+dependencies {
+
+ providedCompile "javax.servlet:javax.servlet-api:3.0.1",
+ 'javax.servlet.jsp:jsp-api:2.1'
+
+ compile project(":spring-security-config"),
+ project(":spring-security-samples-messages-jc"),
+ project(":spring-security-core"),
+ project(":spring-security-web"),
+ "org.springframework:spring-webmvc:$springVersion",
+ "org.springframework:spring-jdbc:$springVersion",
+ "org.slf4j:slf4j-api:$slf4jVersion",
+ "org.slf4j:log4j-over-slf4j:$slf4jVersion",
+ "org.slf4j:jul-to-slf4j:$slf4jVersion",
+ "org.slf4j:jcl-over-slf4j:$slf4jVersion",
+ "javax.servlet:jstl:1.2",
+ "javax.validation:validation-api:1.0.0.GA",
+ "org.hibernate:hibernate-validator:4.2.0.Final"
+
+ runtime "opensymphony:sitemesh:2.4.2",
+ 'cglib:cglib-nodep:2.2.2',
+ 'ch.qos.logback:logback-classic:0.9.30'
+}
\ No newline at end of file
diff --git a/samples/form-jc/pom.xml b/samples/form-jc/pom.xml
new file mode 100644
index 0000000000..ed9390edc4
--- /dev/null
+++ b/samples/form-jc/pom.xml
@@ -0,0 +1,214 @@
+
+
+ 4.0.0
+ org.springframework.security
+ spring-security-samples-form-jc
+ 3.2.0.CI-SNAPSHOT
+ war
+ spring-security-samples-form-jc
+ spring-security-samples-form-jc
+ http://springsource.org/spring-security
+
+ SpringSource
+ http://springsource.org/
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ rwinch
+ Rob Winch
+ rwinch@vmware.com
+
+
+
+ scm:git:git://github.com/SpringSource/spring-security
+ scm:git:git://github.com/SpringSource/spring-security
+ https://github.com/SpringSource/spring-security
+
+
+
+
+ maven-compiler-plugin
+
+ 1.7
+ 1.7
+
+
+
+
+
+
+ spring-snasphot
+ http://repo.springsource.org/libs-snapshot
+
+
+
+
+ javax.servlet
+ jstl
+ 1.2
+ compile
+
+
+ javax.validation
+ validation-api
+ 1.0.0.GA
+ compile
+
+
+ org.hibernate
+ hibernate-validator
+ 4.2.0.Final
+ compile
+
+
+ org.slf4j
+ jcl-over-slf4j
+ 1.7.5
+ compile
+
+
+ org.slf4j
+ jul-to-slf4j
+ 1.7.5
+ compile
+
+
+ org.slf4j
+ log4j-over-slf4j
+ 1.7.5
+ compile
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.5
+ compile
+
+
+ org.springframework.security
+ spring-security-config
+ 3.2.0.CI-SNAPSHOT
+ compile
+
+
+ org.springframework.security
+ spring-security-core
+ 3.2.0.CI-SNAPSHOT
+ compile
+
+
+ org.springframework.security
+ spring-security-samples-messages-jc
+ 3.2.0.CI-SNAPSHOT
+ compile
+
+
+ org.springframework.security
+ spring-security-web
+ 3.2.0.CI-SNAPSHOT
+ compile
+
+
+ org.springframework
+ spring-core
+ 3.2.3.RELEASE
+ compile
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.springframework
+ spring-jdbc
+ 3.2.3.RELEASE
+ compile
+
+
+ org.springframework
+ spring-webmvc
+ 3.2.3.RELEASE
+ compile
+
+
+ commons-logging
+ commons-logging
+ 1.1.1
+ compile
+ true
+
+
+ javax.servlet.jsp
+ jsp-api
+ 2.1
+ provided
+
+
+ javax.servlet
+ javax.servlet-api
+ 3.0.1
+ provided
+
+
+ cglib
+ cglib-nodep
+ 2.2.2
+ runtime
+
+
+ ch.qos.logback
+ logback-classic
+ 0.9.30
+ runtime
+
+
+ opensymphony
+ sitemesh
+ 2.4.2
+ runtime
+
+
+ ch.qos.logback
+ logback-classic
+ 0.9.29
+ test
+
+
+ junit
+ junit
+ 4.7
+ test
+
+
+ org.easytesting
+ fest-assert
+ 1.4
+ test
+
+
+ org.mockito
+ mockito-core
+ 1.8.5
+ test
+
+
+ org.springframework
+ spring-test
+ 3.2.3.RELEASE
+ test
+
+
+
+ /sample
+
+
diff --git a/samples/form-jc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java b/samples/form-jc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java
new file mode 100644
index 0000000000..2b6f0c5172
--- /dev/null
+++ b/samples/form-jc/src/main/java/org/springframework/security/samples/config/MessageSecurityWebApplicationInitializer.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2002-2013 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.samples.config;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
+
+/**
+ * No customizations of {@link AbstractSecurityWebApplicationInitializer} are necessary.
+ *
+ * @author Rob Winch
+ */
+public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
+}
diff --git a/samples/form-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java b/samples/form-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java
new file mode 100644
index 0000000000..323f04cc4c
--- /dev/null
+++ b/samples/form-jc/src/main/java/org/springframework/security/samples/config/SecurityConfig.java
@@ -0,0 +1,34 @@
+package org.springframework.security.samples.config;
+
+import org.springframework.context.annotation.*;
+import org.springframework.security.config.annotation.authentication.builders.*;
+import org.springframework.security.config.annotation.web.builders.*;
+import org.springframework.security.config.annotation.web.configuration.*;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .antMatchers("/resources/**").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login")
+ .permitAll()
+ .and()
+ .logout()
+ .permitAll();
+ }
+
+ @Override
+ protected void registerAuthentication(
+ AuthenticationManagerBuilder auth) throws Exception {
+ auth
+ .inMemoryAuthentication()
+ .withUser("user").password("password").roles("USER");
+ }
+}
diff --git a/samples/form-jc/src/main/webapp/WEB-INF/decorators.xml b/samples/form-jc/src/main/webapp/WEB-INF/decorators.xml
new file mode 100644
index 0000000000..a4542da250
--- /dev/null
+++ b/samples/form-jc/src/main/webapp/WEB-INF/decorators.xml
@@ -0,0 +1,5 @@
+
+
+ /*
+
+
diff --git a/samples/form-jc/src/main/webapp/WEB-INF/decorators/main.jsp b/samples/form-jc/src/main/webapp/WEB-INF/decorators/main.jsp
new file mode 100644
index 0000000000..29e25eb9d0
--- /dev/null
+++ b/samples/form-jc/src/main/webapp/WEB-INF/decorators/main.jsp
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+ SecureMail:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+