SEC-2251: Create Hello World Java Configuration guides

This commit is contained in:
Rob Winch 2013-08-06 11:35:48 -05:00
parent e8278f3b9b
commit e0cb931f69
13 changed files with 452 additions and 3 deletions

View File

@ -3,12 +3,15 @@ import groovy.text.SimpleTemplateEngine
buildscript {
repositories {
maven { url "http://repo.springsource.org/plugins-release" }
maven { url "http://dl.bintray.com/content/aalmiray/asciidoctor" }
maven { url "http://jcenter.bintray.com"}
}
dependencies {
classpath("org.springframework.build.gradle:propdeps-plugin:0.0.3")
classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.7")
classpath("org.springframework.build.gradle:bundlor-plugin:0.1.2")
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:0.9.8")
classpath("org.asciidoctor:asciidoctor-gradle-plugin:0.4.1")
}
}
@ -44,7 +47,7 @@ sonarRunner {
}
// Set up different subproject lists for individual configuration
ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && project.name != 'faq' && project.name != 'manual' }
ext.javaProjects = subprojects.findAll { project -> project.name != 'docs' && project.name != 'faq' && project.name != 'manual' && project.name != 'guides' }
ext.sampleProjects = subprojects.findAll { project -> project.name.startsWith('spring-security-samples') }
ext.itestProjects = subprojects.findAll { project -> project.name.startsWith('itest') }
ext.coreModuleProjects = javaProjects - sampleProjects - itestProjects
@ -122,6 +125,7 @@ task dist(type: Zip) {
classifier = 'dist'
evaluationDependsOn(':docs')
evaluationDependsOn(':docs:manual')
def zipRootDir = "${project.name}-$version"
into(zipRootDir) {
@ -131,6 +135,7 @@ task dist(type: Zip) {
into('docs') {
with(project(':docs').apiSpec)
with(project(':docs:manual').spec)
with(project(':docs:guides').spec)
}
into('dist') {
from coreModuleProjects.collect {project -> project.libsDir }

View File

@ -2,7 +2,7 @@
apply plugin: 'base'
task docs {
dependsOn 'manual:reference', 'faq:referenceHtmlSingle', 'apidocs'
dependsOn 'manual:reference', 'faq:referenceHtmlSingle', 'apidocs', 'guides:asciidoctor'
}
subprojects {
@ -92,6 +92,7 @@ assemble.dependsOn = [apidocs, 'manual:reference']
task docsZip(type: Zip) {
dependsOn docs
evaluationDependsOn('guides')
group = 'Distribution'
baseName = rootProject.name
classifier = 'docs'
@ -101,6 +102,7 @@ task docsZip(type: Zip) {
with(project(':docs').apiSpec)
with(project(':docs:manual').spec)
with(project(':docs:faq').spec)
with(project(':docs:guides').spec)
}
task schemaZip(type: Zip) {

34
docs/guides/build.gradle Normal file
View File

@ -0,0 +1,34 @@
import org.asciidoctor.gradle.*
file("src/asciidoc").eachFileMatch(~/.*\.asc/) { file->
task "asciidoctor-${file.name}"(type: AsciidoctorTask) {
outputDir = project.file("$buildDir/docs")
sourceDocumentName = file
options = [
eruby: 'erubis',
attributes: [
copycss : '',
icons : 'font',
'source-highlighter': 'prettify',
sectanchors : '',
'toc-placement' : 'preamble',
toc: '',
idprefix: '',
idseparator: '-',
'spring-security-version' : project.version
]
]
}
}
task asciidoctor {
group = 'Documentation'
description = "Generates the asciidoc for $project.name"
dependsOn tasks.matching { t -> t.name.startsWith('asciidoctor-') }
}
ext.spec = copySpec {
into ('guides') {
from("$buildDir/docs/")
}
}

View File

@ -0,0 +1,12 @@
require 'asciidoctor'
require 'erb'
guard 'shell' do
watch(/^.*\.asc$/) {|m|
Asciidoctor.render_file(m[0], :to_dir => "build/", :safe => Asciidoctor::SafeMode::UNSAFE, :attributes=> {'toc' => '', 'idprefix' => '', 'idseparator' => '-', 'copycss' => '', 'icons' => 'font', 'source-highlighter' => 'prettify', 'sectanchors' => '', 'toc-placement' => 'preamble', 'spring-security-version' => '3.2.0.CI-SNAPSHOT'})
}
end
guard 'livereload' do
watch(%r{build/.+\.(css|js|html)$})
end

View File

@ -0,0 +1,86 @@
=== Exploring the secured application
Start the server as we did in <<running-the-{starter-appname}-application>> Now when you visit http://localhost:8080/sample/ you will be prompted with a login page that is automatically generated by Spring Security.
==== Authenticating to the secured application
Try entering an invalid username and password:
* *Username* _invalid_
* *Password* _invalid_
You should see an error message stating that authentication failed. Now try entering a valid username and password:
* *Username* _user_
* *Password* _password_
You should now see the page that we wanted to secure.
NOTE: The reason we can successfully authenticate with *Username* _user_ and *Password* _password_ is because that is what we configured in our <<security-config-java,`SecurityConfig`>>.
==== Displaying the user name
Now that we have authenticated, let's update the application to display the username. Update the body of index.jsp to be the following:
.src/main/webapp/index.jsp
[source,html]
----
<body>
<div class="container">
<h1>This is secured!</h1>
<p>
Hello <b><c:out value="${pageContext.request.remoteUser}"/></b>
</p>
</div>
</body>
----
WARNING: The `<c:out />` tag ensures the username is escaped to avoid http://en.wikipedia.org/wiki/Cross-site_scripting[XSS vulnerabilities] Regardless of how an application renders user inputed values, it should ensure that the values are properly escaped.
Refresh the page at http://localhost:8080/sample/ and you will see the user name displayed. This works because Spring Security integrates with the <<servlet-api-integration,Servlet API methods>>
==== Logging out
Now that we can view the user name, let's update the application to allow logging out. Update the body of index.jsp to contain a log out link as shown below:
.src/main/webapp/index.jsp
[source,html]
----
<body>
<div class="container">
<h1>This is secured!</h1>
<c:url var="logoutUrl" value="/logout"/>
<p>
Hello <b><c:out value="${pageContext.request.remoteUser}"/></b>
</p>
<p>
<a href="${logoutUrl}">Click here</a> to log out.
</p>
</div>
</body>
----
Refresh the page at http://localhost:8080/sample/ and you will see the log out link. Click the link and see that the application logs you out successfully.
==== Basic authentication
We stated that Spring Security supported both form and HTTP Basic authentication, but how does Spring Security know when to use one and not the other? When using HTTP Basic, the user should receive a HTTP 401 response, but when we visit our application in our web browser we are redirected to a login page. The reason for this is because Spring Security uses content negotiation to determine which type of authentication to use. For example, if we specified our *Accept* header to be _application/json_ the result would be an HTTP 401.
You can use any tool you prefer (i.e. curl), but the instructions in this section we will use https://www.google.com/intl/en/chrome/browser/[Google Chrome] and the https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en[Postman - REST Client] to make an _application/json_ request to our application.
* Open Google Chrome and launch the Postman - REST Client extension
* Enter _http://localhost:8080/sample/_ into the request URL field
* Select the *Headers* button
* Enter _Accept_ into the *Header* input
* Enter _application/json_ into the *Value* field
* Presss the *Send* button
Observe that we get an HTTP Status of 401 instead of our redirect. Now lets try entering our user name and password.
* Select the *Basic Auth* tab
* Enter _user_ for the *Username*
* Enter _password_ for the *Password*
* Click the *Refresh headers* button
* Click the *Send* button
This time you should see the HTML of our secured page.

View File

@ -0,0 +1,98 @@
== Securing the application
Before securing your application, it is important to ensure that the existing application works as we did in <<running-the-{starter-appname}-application>>. Now that the application runs without security, we are ready to add security to our application. This section demonstrates the minimal steps to add Spring Security to a Spring MVC application.
=== Updating your dependencies
You will need to ensure you have added the dependencies. Spring Security milestones and release canidates are available in the https://github.com/SpringSource/spring-framework/wiki/SpringSource-repository-FAQ[Spring Milestone Repository]. In short, if you are using Maven and using a milestone or release canidate ensure you have the following repository in your pom.xml:
.pom.xml
[source,xml]
----
<repositories>
<!-- ... possibly other repository elements ... -->
<repository>
<id>spring-libs-milestone</id>
<name>Spring Milestone Repository</name>
<url>http://repo.springsource.org/milestone</url>
</repository>
</repositories>
----
You will then need to include the Spring Security dependencies
.pom.xml
[source,xml]
[subs="verbatim,attributes"]
----
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>{spring-security-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>{spring-security-version}</version>
</dependency>
</dependencies>
----
After you have completed this, you need to ensure that STS knows about the updated dependencies by:
* Right click on the _spring-security-samples-{starter-appname}_ application
* Select *Maven->Update project...*
* Ensure the project is selected, and click *OK*
=== Creating your Spring Security configuration
The next step is to create a Spring Security configuration.
* Right click the _spring-security-samples-{starter-appname}_ project the Package Explorer view
* Select *New->Class*
* Enter _org.springframework.security.samples.config_ for the *Package*
* Enter _SecurityConfig_ for the *Name*
* Click *Finish*
* Replace the file with the following contents:
[[security-config-java]]
.src/main/java/org/springframework/security/samples/config/SecurityConfig.java
[source,java]
----
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.configuration.*;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
----
[[servlet-api-integration]]
The <<security-config-java,`SecurityConfig`>> will:
* Require authentication to every URL in your application
* Generate a login form for you
* Allow the user with the *Username* _user_ and the *Password* _password_ to authenticate with form based authentication
* Allow the user with the *Username* _user_ and the *Password* _password_ to authenticate with HTTP basic authentication
* Allow the user to logout
* Integrate with the following Servlet API methods
** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()[HttpServletRequest#getRemoteUser()]
** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()[HttpServletRequest.html#getUserPrincipal()]
** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)[HttpServletRequest.html#isUserInRole(java.lang.String)]
** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login(java.lang.String,%20java.lang.String)[HttpServletRequest.html#login(java.lang.String, java.lang.String)]
** http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout()[HttpServletRequest.html#logout()]

View File

@ -0,0 +1,61 @@
== Setting up the sample
This section outlines how to setup a workspace within STS so that you can follow along with this guide. The <<securing-the-application>> 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
There are multiple ways in which you can obtain the source. We have highlighted a few ways below:
* <<downloading-from-github>>
* <<cloning-from-github>>
==== Downloading from github
You can download the source from github.
* https://github.com/SpringSource/spring-security/archive/master.zip[Development Version]
* https://github.com/SpringSource/spring-security/releases[Released Versions]
Extract the zip to a known location and remember it as _SPRING_SECURITY_HOME_. You are now ready to <<import-the-{starter-appname}-sample-application>>
==== Cloning from github
If you wish you can also obtain the source by cloning from github. For example, if you have a git client installed you can type the following:
----
cd ~/git/
git clone https://github.com/SpringSource/spring-security.git
----
In this example, the code will be placed at "~/git/spring-security". Remember this location as _SPRING_SECURITY_HOME_.
=== Import the {starter-appname} sample application
In oder to follow along, we encourage you to import the {starter-appname} sample application into your IDE. You may use any IDE you prefer, but the instructions in the guide will assume you are using Spring Tool Suite (STS).
TIP: The completed sample application can be found at _SPRING_SECURITY_HOME_/{completed-appname}
* If you do not have STS installed, download STS from http://www.springsource.org/spring-tool-suite-download For performance reasons, we prefer the release based on Eclipse Juno.
* Start STS and import the sample applications into STS using the following steps:
** *File->Import*
** *Existing Maven Projects*
** Click *Next >*
** Click *Browse...*
** Navigate to the samples (i.e. _SPRING_SECURITY_HOME_/samples/{starter-appname}) and click *OK*
** Click *Finish*
=== Running the {starter-appname} application
In the following exerecise we will be modifying the _spring-security-samples-{starter-appname}_ application. Before we make any changes, it is best to verify that the sample works properly. Perform the following steps to ensure that _spring-security-samples-{starter-appname}_ works.
* Right click on the _spring-security-samples-{starter-appname}_ application
* Select *Run As->Run on Server*
* Select the latest tc Server (i.e. v2.9)
* Click *Finish*
include::{verify-starter-app-include}[]
Once you have verified the application runs, stop the application server using the following steps:
* In the Servers view select the latest tc Server
* Click the stop button (a red square) to stop the application server

View File

@ -0,0 +1 @@
A page stating *TODO Secure this* should be displayed at http://localhost:8080/sample/

View File

@ -0,0 +1,5 @@
Verify the application is working:
* A page displaying a user's inbox can be seen at http://localhost:8080/sample/
* 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.

View File

@ -0,0 +1,81 @@
= Hello Spring MVC Security Java Config
Rob Winch
{spring-security-version}
:starter-appname: insecuremvc
:completed-appname: hellomvc-jc
:verify-starter-app-include: hello-includes/verify-insecuremvc-app.asc
This guide provides instructions on how to add Spring Security to an existing Spring MVC application without the use of XML.
include::hello-includes/setting-up-the-sample.asc[]
include::hello-includes/secure-the-application.asc[]
=== Registering Spring Security with the war
We have created the Spring Security configuration, but we still need to register it with the war. This can be done using the following steps:
* Right click the _spring-security-samples-{starter-appname}_ project the Package Explorer view
* Select *New->Class*
* Enter _org.springframework.security.samples.config_ for the *Package*
* Enter _SecurityWebApplicationInitializer_ for the *Name*
* Click *Finish*
* Replace the file with the following contents:
.src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java
[source,java]
----
package org.springframework.security.samples.config;
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
----
The `SecurityWebApplicationInitializer` will automatically register the springSecurityFilterChain Filter for every URL in your application.
=== Verify SecurityConfig is loaded
Just because <<security-config-java,`SecurityConfig`>> exists, does not mean that our Spring application knows about it. In this instance, our Spring root application context is initialized using MessageWebApplicationInitializer which is included with our spring-security-samples-messages-jc project. You can find a snippet of it below:
.MessageWebApplicationInitializer.java
[source,java]
----
@Order(1)
public class MessageWebApplicationInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { RootConfiguration.class };
}
// ... other overrides ...
}
----
You will notice it is loading the `RootConfiguration` class which is also included in our spring-security-samples-messages-jc project.
[[root-configuration-java]]
.RootConfiguration.java
[source,java]
----
@Configuration
@ComponentScan(value = "org.springframework.security.samples.config",
excludeFilters = @Filter(type = FilterType.ASSIGNABLE_TYPE, value = RootConfiguration.class))
public class RootConfiguration {
}
----
The `@ComponentScan` is loading all configuration in the org.springframework.security.samples.config package. Since <<security-config-java,`SecurityConfig`>> is in this package, it will be loaded with our existing setup and there is nothing more to do.
NOTE: Had <<security-config-java,`SecurityConfig`>> not been loaded, we could have used an `@Import(SecurityConfig)` above the class definition of <<root-configuration-java,`RootConfiguration`>> or added <<security-config-java,`SecurityConfig`>> as one of the results for `getRootConfigClasses()`.
include::hello-includes/exploring-the-secured-application.asc[]
== Conclusion
You should now now how to secure your application using Spring Security without using any XML.

View File

@ -0,0 +1,52 @@
= Hello Spring Security Java Config
Rob Winch
{spring-security-version}
:starter-appname: insecure
:completed-appname: helloworld-jc
:verify-starter-app-include: hello-includes/verify-insecure-app.asc
This guide provides instructions on how to add Spring Security to an existing application without the use of XML.
include::hello-includes/setting-up-the-sample.asc[]
include::hello-includes/secure-the-application.asc[]
=== Registering Spring Security with the war
We have created the Spring Security configuraiton, but we still need to register it with the war. This can be done using the following steps:
* Navigate to the *Package Explorer* view
* Right click the *org.springframework.security.samples.config* package within the *spring-security-samples-{starter-appname}* project
* Select *New->Class*
* Enter _SecurityWebApplicationInitializer_ for the *Name*
* Click *Finish*
* Replace the file with the following contents:
.src/main/java/org/springframework/security/samples/config/SecurityWebApplicationInitializer.java
[source,java]
----
package org.springframework.security.samples.config;
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
----
The `SecurityWebApplicationInitializer` will do the following things:
* Automatically register the springSecurityFilterChain Filter for every URL in your application
* Add a ContextLoaderListener that loads the <<security-config-java,`SecurityConfig`>>.
NOTE: Since we were not already using Spring, this is a simple way to add our <<security-config-java,`SecurityConfig`>>. If we were already using Spring, then we should add our <<security-config-java,`SecurityConfig`>> with the reset of our Spring configuration (i.e. a subclass of AbstractContextLoaderInitializer or AbstractDispatcherServletInitializer) and use the default constructor instead.
include::hello-includes/exploring-the-secured-application.asc[]
== Conclusion
You should now now how to secure your application using Spring Security without using any XML.

View File

@ -0,0 +1,12 @@
= Spring Security Guides
Rob Winch
{spring-security-version}
These are set by step guides on how to use Spring Security.
== Hello World
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

View File

@ -75,7 +75,7 @@ itest.each { name ->
p.projectDir = new File(settingsDir, "itest/${name}");
}
include 'docs', 'docs:faq', 'docs:manual'
include 'docs', 'docs:faq', 'docs:manual', 'docs:guides'
def docs = findProject(':docs')
docs.buildFileName = 'docs.gradle'