SEC-2349: Convert Reference to Asciidoctor

This commit is contained in:
Rob Winch 2013-10-03 14:15:09 -05:00
parent df5e034fc3
commit 4b43cf3f50
68 changed files with 8255 additions and 12481 deletions

View File

@ -3,7 +3,6 @@ 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 {
@ -11,7 +10,6 @@ buildscript {
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")
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
}
}

View File

@ -12,6 +12,10 @@ repositories {
}
}
dependencies {
compile gradleApi()
}
// GAE
dependencies {
compile 'com.google.appengine:appengine-tools-sdk:1.4.2'
@ -27,6 +31,21 @@ dependencies {
'net.sourceforge.saxon:saxon:9.1.0.8'
}
dependencies {
compile('org.asciidoctor:asciidoctor-java-integration:0.1.3') {
exclude group: 'rubygems', module :'haml'
exclude group: 'rubygems', module :'asciidoctor'
exclude group: 'rubygems', module :'coderay'
exclude group: 'rubygems', module :'tilt'
exclude group: 'rubygems', module :'erubis'
exclude group: 'rubygems', module :'slim'
}
compile 'org.apache.avalon.framework:avalon-framework-api:4.3.1'
compile 'org.apache.avalon.framework:avalon-framework-impl:4.3.1'
compile 'org.apache.xmlgraphics:fop:1.1'
runtime 'net.sf.xslthl:xslthl:2.1.0'
}
task ide(type: Copy) {
from configurations.runtime
into 'ide'

View File

@ -0,0 +1,45 @@
/*
* Copyright 2012-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.asciidoctor.gradle
/**
* Supported backends.
*
* @author Benjamin Muschko
*/
enum AsciidoctorBackend {
HTML5('html5'), DOCBOOK('docbook'), PDF('pdf')
private final static Map<String, AsciidoctorBackend> ALL_BACKENDS
private final String id
static {
ALL_BACKENDS = values().collectEntries{ [it.id, it] }.asImmutable()
}
private AsciidoctorBackend(String id) {
this.id = id
}
String getId() {
id
}
static boolean isSupported(String name) {
ALL_BACKENDS.containsKey(name)
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2012-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.asciidoctor.gradle
import groovy.lang.Closure;
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
* @author Noam Tenne
* @author Andres Almiray
*/
class AsciidoctorPlugin implements Plugin<Project> {
void apply(Project project) {
project.task('asciidoctor', type: AsciidoctorTask, group: 'Documentation')
}
}

View File

@ -0,0 +1,201 @@
/*
* Copyright 2012-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.asciidoctor.gradle
import org.asciidoctor.gradle.*
import org.apache.commons.io.IOUtils
import org.asciidoctor.Asciidoctor
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.InvalidUserDataException
import org.gradle.api.tasks.*
import javax.xml.transform.*
import javax.xml.transform.stream.*
import javax.xml.transform.sax.*
import org.apache.fop.apps.FopFactory
import org.apache.fop.apps.Fop
import org.apache.fop.apps.MimeConstants
class AsciidoctorTask extends DefaultTask {
@InputFile File sourceDocument
@Input Map options = [:]
@Optional @OutputDirectory File outputDir
@Optional @Input List<String> backends
AsciidoctorTask() {
sourceDocument = project.file("src/asciidoctor/index.adoc")
outputDir = project.file("${project.buildDir}/asciidoctor")
backends = [AsciidoctorBackend.HTML5.id]
}
@TaskAction
void render() {
Asciidoctor asciidoctor = Asciidoctor.Factory.create()
for(backend in backends) {
boolean isPdf = backend == AsciidoctorBackend.PDF.id
String asciidoctorBackend = isPdf ? AsciidoctorBackend.DOCBOOK.id : backend
File distDir = new File("${outputDir}/dist/$backend")
File workingDir = new File("${outputDir}/work/$backend")
[workingDir,distDir]*.mkdirs()
try {
asciidoctor.renderFile(sourceDocument, mergedOptions(options, isPdf ? workingDir : distDir, asciidoctorBackend))
if(isPdf) {
generatePdf(workingDir,distDir)
} else {
project.copy {
from "${sourceDocument.parent}/images"
into "${distDir}/images/"
}
}
} catch (Exception e) {
throw new GradleException('Error running Asciidoctor on single source '+asciidoctorBackend, e)
}
}
}
private void generatePdf(File workingDir, File distDir) {
String docbookXmlUrl = 'http://maven-us.nuxeo.org/nexus/content/repositories/public/docbook/docbook-xml/4.5/docbook-xml-4.5.jar'
String docbookXslUrl = 'http://downloads.sourceforge.net/project/docbook/docbook-xsl-ns/1.78.1/docbook-xsl-ns-1.78.1.zip'
File docbookXmlFile = downloadFile(docbookXmlUrl)
File docbookXslFile = downloadFile(docbookXslUrl)
project.copy {
from "src/asciidoctor/images"
into "${workingDir}/images/"
}
project.copy {
from project.zipTree(docbookXmlFile)
into "$workingDir/docbook"
}
project.copy {
from(project.zipTree(docbookXslFile)) {
eachFile { details ->
details.path = details.path.substring(details.relativePath.segments[0].length())
}
}
into "$workingDir/docbook/"
}
unzipDockbookXsl(workingDir)
def outputUri = workingDir.toURI().toASCIIString()
Vector params = new Vector()
params.add("highlight.xslthl.config")
params.add(outputUri + "docbook-xsl/xslthl-config.xml")
params.add("admon.graphics.path")
params.add(outputUri + "docbook/images/")
params.add("callout.graphics.path")
params.add(outputUri + "docbook/images/callouts/")
params.add("img.src.path")
params.add(outputUri)
params.add("fop-output-format")
params.add("application/pdf")
params.add("fop-version")
params.add("1.1")
File outputFile = new File("${distDir}/", sourceDocument.name.replaceAll("\\..*", ".pdf"))
File docbookFile = new File("$workingDir/",sourceDocument.name.replaceAll("\\..*", ".xml"))
File xsltFile = new File("${workingDir}/docbook-xsl/fo-pdf.xsl")
InputHandler handler = new InputHandler(docbookFile, xsltFile, params)
FopFactory fopFactory = FopFactory.newInstance(); // Reuse the FopFactory if possible!
fopFactory.setUserConfig(new File("${workingDir}/docbook-xsl/fop-config.xml"))
// do the following for each new rendering run
def foUserAgent = fopFactory.newFOUserAgent();
handler.createCatalogResolver(foUserAgent)
def out = new java.io.BufferedOutputStream(
new java.io.FileOutputStream(outputFile));
foUserAgent.setOutputFile(outputFile);
try {
handler.renderTo(foUserAgent, MimeConstants.MIME_PDF, out)
} finally {
IOUtils.closeQuietly(out)
}
}
private void unzipDockbookXsl(def installDir) {
def docbookXslResourceName = 'docbook-xsl.zip'
def docbookXslInputStream = this.class.classLoader.getResourceAsStream(docbookXslResourceName)
if (docbookXslInputStream == null) {
throw new GradleException("could not find ${docbookXslResourceName} on the classpath");
}
// the file is a jar:file - write it to disk first
File docbookXslOutputFile = new File("${installDir}/downloads/${docbookXslResourceName}")
docbookXslOutputFile.parentFile.mkdirs()
IOUtils.copy(docbookXslInputStream, new FileOutputStream(docbookXslOutputFile))
project.copy {
from project.zipTree(docbookXslOutputFile)
into "${installDir}/"
}
}
private File downloadFile(String url) {
def home = System.getProperty("user.home")
File destinationFile = new File("${home}/.fopdf/downloads", url.split("/")[-1])
destinationFile.parentFile.mkdirs()
if(!destinationFile.exists()) {
logger.info("Downloading " + url + " to "+ destinationFile + "...")
destinationFile.bytes = new URL(url).bytes
}
destinationFile
}
private static Map<String, Object> mergedOptions(Map options, File outputDir, String backend) {
Map<String, Object> mergedOptions = [:]
mergedOptions.putAll(options)
mergedOptions.in_place = false
mergedOptions.safe = 0i
mergedOptions.to_dir = outputDir.absolutePath
Map attributes = mergedOptions.get('attributes', [:])
attributes.backend = backend
// Issue #14 force GString -> String as jruby will fail
// to find an exact match when invoking Asciidoctor
for (entry in mergedOptions) {
if (entry.value instanceof CharSequence) {
mergedOptions[entry.key] = entry.value.toString()
}
}
for (entry in attributes) {
if (entry.value instanceof CharSequence) {
attributes[entry.key] = entry.value.toString()
}
}
mergedOptions
}
}

View File

@ -0,0 +1,336 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/* $Id$ */
package org.asciidoctor.gradle;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Vector;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.ResourceEventProducer;
import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.render.awt.viewer.Renderable;
/**
* Class for handling files input from command line
* either with XML and XSLT files (and optionally xsl
* parameters) or FO File input alone.
*/
public class InputHandler implements ErrorListener, Renderable {
/** original source file */
protected File sourcefile;
private File stylesheet; // for XML/XSLT usage
private Vector xsltParams; // for XML/XSLT usage
private EntityResolver entityResolver = null;
private URIResolver uriResolver = null;
/** the logger */
protected Log log = LogFactory.getLog(InputHandler.class);
/**
* Constructor for XML->XSLT->FO input
*
* @param xmlfile XML file
* @param xsltfile XSLT file
* @param params Vector of command-line parameters (name, value,
* name, value, ...) for XSL stylesheet, null if none
*/
public InputHandler(File xmlfile, File xsltfile, Vector params) {
if(!xsltfile.exists()) {
throw new RuntimeException("Couldn't find "+ xsltfile);
}
sourcefile = xmlfile;
stylesheet = xsltfile;
xsltParams = params;
}
/**
* Constructor for FO input
* @param fofile the file to read the FO document.
*/
public InputHandler(File fofile) {
sourcefile = fofile;
}
/**
* Generate a document, given an initialized Fop object
* @param userAgent the user agent
* @param outputFormat the output format to generate (MIME type, see MimeConstants)
* @param out the output stream to write the generated output to (may be null if not applicable)
* @throws FOPException in case of an error during processing
*/
public void renderTo(FOUserAgent userAgent, String outputFormat, OutputStream out)
throws FOPException {
FopFactory factory = userAgent.getFactory();
Fop fop;
if (out != null) {
fop = factory.newFop(outputFormat, userAgent, out);
} else {
fop = factory.newFop(outputFormat, userAgent);
}
// if base URL was not explicitly set in FOUserAgent, obtain here
if (fop.getUserAgent().getBaseURL() == null && sourcefile != null) {
String baseURL = null;
try {
baseURL = new File(sourcefile.getAbsolutePath())
.getParentFile().toURI().toURL().toExternalForm();
} catch (Exception e) {
baseURL = "";
}
fop.getUserAgent().setBaseURL(baseURL);
}
// Resulting SAX events (the generated FO) must be piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
transformTo(res);
}
/** {@inheritDoc} */
public void renderTo(FOUserAgent userAgent, String outputFormat) throws FOPException {
renderTo(userAgent, outputFormat, null);
}
/**
* In contrast to render(Fop) this method only performs the XSLT stage and saves the
* intermediate XSL-FO file to the output file.
* @param out OutputStream to write the transformation result to.
* @throws FOPException in case of an error during processing
*/
public void transformTo(OutputStream out) throws FOPException {
Result res = new StreamResult(out);
transformTo(res);
}
/**
* Creates a Source for the main input file. Processes XInclude if
* available in the XML parser.
*
* @return the Source for the main input file
*/
protected Source createMainSource() {
Source source;
InputStream in;
String uri;
if (this.sourcefile != null) {
try {
in = new java.io.FileInputStream(this.sourcefile);
uri = this.sourcefile.toURI().toASCIIString();
} catch (FileNotFoundException e) {
//handled elsewhere
return new StreamSource(this.sourcefile);
}
} else {
in = System.in;
uri = null;
}
try {
InputSource is = new InputSource(in);
is.setSystemId(uri);
XMLReader xr = getXMLReader();
if (entityResolver != null) {
xr.setEntityResolver(entityResolver);
}
source = new SAXSource(xr, is);
} catch (SAXException e) {
if (this.sourcefile != null) {
source = new StreamSource(this.sourcefile);
} else {
source = new StreamSource(in, uri);
}
} catch (ParserConfigurationException e) {
if (this.sourcefile != null) {
source = new StreamSource(this.sourcefile);
} else {
source = new StreamSource(in, uri);
}
}
return source;
}
/**
* Creates a catalog resolver and uses it for XML parsing and XSLT URI resolution.
* Tries the Apache Commons Resolver, and if unsuccessful,
* tries the same built into Java 6.
* @param userAgent the user agent instance
*/
public void createCatalogResolver(FOUserAgent userAgent) {
String[] classNames = new String[] {
"org.apache.xml.resolver.tools.CatalogResolver",
"com.sun.org.apache.xml.internal.resolver.tools.CatalogResolver"};
ResourceEventProducer eventProducer
= ResourceEventProducer.Provider.get(userAgent.getEventBroadcaster());
Class resolverClass = null;
for (int i = 0; i < classNames.length && resolverClass == null; ++i) {
try {
resolverClass = Class.forName(classNames[i]);
} catch (ClassNotFoundException e) {
// No worries
}
}
if (resolverClass == null) {
eventProducer.catalogResolverNotFound(this);
return;
}
try {
entityResolver = (EntityResolver) resolverClass.newInstance();
uriResolver = (URIResolver) resolverClass.newInstance();
} catch (InstantiationException e) {
log.error("Error creating the catalog resolver: " + e.getMessage());
eventProducer.catalogResolverNotCreated(this, e.getMessage());
} catch (IllegalAccessException e) {
log.error("Error creating the catalog resolver: " + e.getMessage());
eventProducer.catalogResolverNotCreated(this, e.getMessage());
}
}
/**
* Creates a Source for the selected stylesheet.
*
* @return the Source for the selected stylesheet or null if there's no stylesheet
*/
protected Source createXSLTSource() {
Source xslt = null;
if (this.stylesheet != null) {
if (entityResolver != null) {
try {
InputSource is = new InputSource(this.stylesheet.getPath());
XMLReader xr = getXMLReader();
xr.setEntityResolver(entityResolver);
xslt = new SAXSource(xr, is);
} catch (SAXException e) {
// return StreamSource
} catch (ParserConfigurationException e) {
// return StreamSource
}
}
if (xslt == null) {
xslt = new StreamSource(this.stylesheet);
}
}
return xslt;
}
private XMLReader getXMLReader() throws ParserConfigurationException, SAXException {
SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setFeature("http://xml.org/sax/features/namespaces", true);
spf.setFeature("http://apache.org/xml/features/xinclude", true);
XMLReader xr = spf.newSAXParser().getXMLReader();
return xr;
}
/**
* Transforms the input document to the input format expected by FOP using XSLT.
* @param result the Result object where the result of the XSL transformation is sent to
* @throws FOPException in case of an error during processing
*/
protected void transformTo(Result result) throws FOPException {
try {
// Setup XSLT
System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
TransformerFactory factory = TransformerFactory.newInstance();
if (uriResolver != null) {
factory.setURIResolver(uriResolver);
}
factory.setErrorListener(this);
Transformer transformer;
Source xsltSource = createXSLTSource();
if (xsltSource == null) { // FO Input
transformer = factory.newTransformer();
} else { // XML/XSLT input
transformer = factory.newTransformer(xsltSource);
// Set the value of parameters, if any, defined for stylesheet
if (xsltParams != null) {
for (int i = 0; i < xsltParams.size(); i += 2) {
transformer.setParameter((String) xsltParams.elementAt(i),
(String) xsltParams.elementAt(i + 1));
}
}
}
transformer.setErrorListener(this);
// Create a SAXSource from the input Source file
Source src = createMainSource();
// Start XSLT transformation and FOP processing
transformer.transform(src, result);
} catch (Exception e) {
throw new FOPException(e);
}
}
// --- Implementation of the ErrorListener interface ---
/**
* {@inheritDoc}
*/
public void warning(TransformerException exc) {
log.warn(exc.getLocalizedMessage());
}
/**
* {@inheritDoc}
*/
public void error(TransformerException exc) {
log.error(exc.toString());
}
/**
* {@inheritDoc}
*/
public void fatalError(TransformerException exc)
throws TransformerException {
throw exc;
}
}

View File

@ -0,0 +1 @@
implementation-class=org.asciidoctor.gradle.AsciidoctorPlugin

Binary file not shown.

View File

@ -2,7 +2,7 @@
apply plugin: 'base'
task docs {
dependsOn 'manual:reference', 'faq:referenceHtmlSingle', 'apidocs', 'guides:asciidoctor'
dependsOn 'manual:asciidoctor', 'faq:referenceHtmlSingle', 'apidocs', 'guides:asciidoctor'
}
project('faq') {
@ -25,26 +25,37 @@ project('faq') {
project('manual') {
apply plugin: 'base'
apply plugin: 'docbook-reference'
apply plugin: 'asciidoctor'
[referenceHtmlMulti, referencePdf, referenceHtmlSingle]*.sourceDir = file('src/docbook')
ext.expandPlaceholders = ""
asciidoctor {
backends = ["html5", "pdf"]
options = [
eruby: 'erubis',
attributes: [
copycss : '',
icons : 'font',
'source-highlighter': 'prettify',
sectanchors : '',
toc2: '',
idprefix: '',
idseparator: '-',
doctype: 'book',
'spring-security-version' : project.version,
revnumber : project.version
]
]
}
defaultTasks 'referenceHtmlMulti', 'referenceHtmlSingle', 'referencePdf'
def imagesDir = new File(projectDir, 'src/docbook/images');
ext.spec = copySpec {
into ('reference') {
from("$buildDir/reference")
from("${asciidoctor.outputDir}/dist/")
}
}
}
task reference (type: Copy) {
dependsOn 'manual:reference'
destinationDir = buildDir
with(project('manual').spec)
}
task apidocs(type: Javadoc) {
destinationDir = new File(buildDir, 'apidocs')
title = "Spring Security $version API"
@ -91,7 +102,7 @@ ext.apiSpec = copySpec {
}
}
assemble.dependsOn = [apidocs, 'manual:reference']
assemble.dependsOn = [apidocs, 'manual:asciidoctor']
task docsZip(type: Zip) {
dependsOn docs

View File

@ -1,20 +1,21 @@
import org.asciidoctor.gradle.*
file("src/asciidoc").eachFileMatch(~/.*\.asc/) { file->
task "asciidoctor-${file.name}"(type: AsciidoctorTask) {
outputDir = project.file("$buildDir/docs")
sourceDocumentName = file
sourceDocument = file
options = [
eruby: 'erubis',
eruby: 'erubis',
attributes: [
copycss : '',
icons : 'font',
'source-highlighter': 'prettify',
sectanchors : '',
'toc-placement' : 'preamble',
toc: '',
toc2: '',
idprefix: '',
idseparator: '-',
doctype: 'book',
'spring-security-version' : project.version,
revnumber : project.version
]
@ -30,6 +31,6 @@ task asciidoctor {
ext.spec = copySpec {
into ('guides') {
from("$buildDir/docs/")
from("$buildDir/asciidoctor/dist/html5/")
}
}

View File

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

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,138 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="anonymous"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Anonymous Authentication</title>
</info>
<section xml:id="anonymous-overview">
<info>
<title>Overview</title>
</info>
<para>It's generally considered good security practice to adopt a
<quote>deny-by-default</quote> where you explicitly specify what is allowed and disallow
everything else. Defining what is accessible to unauthenticated users is a similar
situation, particularly for web applications. Many sites require that users must be
authenticated for anything other than a few URLs (for example the home and login pages).
In this case it is easiest to define access configuration attributes for these specific
URLs rather than have for every secured resource. Put differently, sometimes it is nice
to say <literal>ROLE_SOMETHING</literal> is required by default and only allow certain
exceptions to this rule, such as for login, logout and home pages of an application. You
could also omit these pages from the filter chain entirely, thus bypassing the access
control checks, but this may be undesirable for other reasons, particularly if the pages
behave differently for authenticated users.</para>
<para>This is what we mean by anonymous authentication. Note that there is no real
conceptual difference between a user who is <quote>anonymously authenticated</quote> and
an unauthenticated user. Spring Security's anonymous authentication just gives you a
more convenient way to configure your access-control attributes. Calls to servlet API
calls such as <methodname>getCallerPrincipal</methodname>, for example, will still
return null even though there is actually an anonymous authentication object in the
<classname>SecurityContextHolder</classname>.</para>
<para>There are other situations where anonymous authentication is useful, such as when an
auditing interceptor queries the <classname>SecurityContextHolder</classname> to
identify which principal was responsible for a given operation. Classes can be authored
more robustly if they know the <classname>SecurityContextHolder</classname> always
contains an <interfacename>Authentication</interfacename> object, and never
<literal>null</literal>.</para>
</section>
<section xml:id="anonymous-config">
<info>
<title>Configuration</title>
</info>
<para>Anonymous authentication support is provided automatically when using the HTTP
configuration Spring Security 3.0 and can be customized (or disabled) using the
<literal>&lt;anonymous></literal> element. You don't need to configure the beans
described here unless you are using traditional bean configuration.</para>
<para>Three classes that together provide the anonymous authentication feature.
<literal>AnonymousAuthenticationToken</literal> is an implementation of
<interfacename>Authentication</interfacename>, and stores the
<interfacename>GrantedAuthority</interfacename>s which apply to the anonymous principal.
There is a corresponding <literal>AnonymousAuthenticationProvider</literal>, which is
chained into the <literal>ProviderManager</literal> so that
<literal>AnonymousAuthenticationToken</literal>s are accepted. Finally, there is an
<classname>AnonymousAuthenticationFilter</classname>, which is chained after the normal
authentication mechanisms and automatically adds an
<literal>AnonymousAuthenticationToken</literal> to the
<classname>SecurityContextHolder</classname> if there is no existing
<interfacename>Authentication</interfacename> held there. The definition of the filter
and authentication provider appears as follows:</para>
<para> <programlisting language="xml">
<![CDATA[
<bean id="anonymousAuthFilter"
class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
<property name="key" value="foobar"/>
<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
</bean>
<bean id="anonymousAuthenticationProvider"
class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
<property name="key" value="foobar"/>
</bean>]]>
</programlisting> </para>
<para>The <literal>key</literal> is shared between the filter and authentication provider,
so that tokens created by the former are accepted by the latter<footnote>
<para>The use of the <literal>key</literal> property should not be regarded as providing
any real security here. It is merely a book-keeping exercise. If you are sharing a
<classname>ProviderManager</classname> which contains an
<classname>AnonymousAuthenticationProvider</classname> in a scenario where it is
possible for an authenticating client to construct the
<interfacename>Authentication</interfacename> object (such as with RMI invocations),
then a malicious client could submit an
<classname>AnonymousAuthenticationToken</classname> which it had created itself
(with chosen username and authority list). If the <literal>key</literal> is
guessable or can be found out, then the token would be accepted by the anonymous
provider. This isn't a problem with normal usage but if you are using RMI you would
be best to use a customized <classname>ProviderManager</classname> which omits the
anonymous provider rather than sharing the one you use for your HTTP authentication
mechanisms.</para>
</footnote>. The <literal>userAttribute</literal> is expressed in the form of
<literal>usernameInTheAuthenticationToken,grantedAuthority[,grantedAuthority]</literal>.
This is the same syntax as used after the equals sign for
<literal>InMemoryDaoImpl</literal>'s <literal>userMap</literal> property.</para>
<para>As explained earlier, the benefit of anonymous authentication is that all URI patterns
can have security applied to them. For example:</para>
<para> <programlisting language="xml">
<![CDATA[
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager"/>
<property name="securityMetadata">
<security:filter-security-metadata-source>
<security:intercept-url pattern='/index.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/hello.htm' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/logoff.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/login.jsp' access='ROLE_ANONYMOUS,ROLE_USER'/>
<security:intercept-url pattern='/**' access='ROLE_USER'/>
</security:filter-security-metadata-source>" +
</property>
</bean>]]>
</programlisting> </para>
</section>
<section xml:id="anonymous-auth-trust-resolver">
<title><interfacename>AuthenticationTrustResolver</interfacename></title>
<para> Rounding out the anonymous authentication discussion is the
<interfacename>AuthenticationTrustResolver</interfacename> interface, with its
corresponding <literal>AuthenticationTrustResolverImpl</literal> implementation. This
interface provides an <literal>isAnonymous(Authentication)</literal> method, which
allows interested classes to take into account this special type of authentication
status. The <classname>ExceptionTranslationFilter</classname> uses this interface in
processing <literal>AccessDeniedException</literal>s. If an
<literal>AccessDeniedException</literal> is thrown, and the authentication is of an
anonymous type, instead of throwing a 403 (forbidden) response, the filter will instead
commence the <interfacename>AuthenticationEntryPoint</interfacename> so the principal
can authenticate properly. This is a necessary distinction, otherwise principals would
always be deemed <quote>authenticated</quote> and never be given an opportunity to login
via form, basic, digest or some other normal authentication mechanism. </para>
<para> You will often see the <literal>ROLE_ANONYMOUS</literal> attribute in the above
interceptor configuration replaced with <literal>IS_AUTHENTICATED_ANONYMOUSLY</literal>,
which is effectively the same thing when defining access controls. This is an example of
the use of the <classname>AuthenticatedVoter</classname> which we will see in the <link
linkend="authz-authenticated-voter">authorization chapter</link>. It uses an
<interfacename>AuthenticationTrustResolver</interfacename> to process this particular
configuration attribute and grant access to anonymous users. The
<classname>AuthenticatedVoter</classname> approach is more powerful, since it allows you
to differentiate between anonymous, remember-me and fully-authenticated users. If you
don't need this functionality though, then you can stick with
<literal>ROLE_ANONYMOUS</literal>, which will be processed by Spring Security's standard
<classname>RoleVoter</classname>. </para>
</section>
</chapter>

View File

@ -1,197 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<appendix version="5.0" xml:id="appendix-schema" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude">
<info>
<title>Security Database Schema</title>
</info>
<para> There are various database schema used by the framework and this appendix provides a
single reference point to them all. You only need to provide the tables for the areas of
functonality you require. </para>
<para> DDL statements are given for the HSQLDB database. You can use these as a guideline for
defining the schema for the database you are using. </para>
<section>
<title>User Schema</title>
<para> The standard JDBC implementation of the
<interfacename>UserDetailsService</interfacename> (<classname>JdbcDaoImpl</classname>)
requires tables to load the password, account status (enabled or disabled) and a list of
authorities (roles) for the
user.<programlisting xml:id="db_schema_users_authorities">
create table users(
username varchar_ignorecase(50) not null primary key,
password varchar_ignorecase(50) not null,
enabled boolean not null);
create table authorities (
username varchar_ignorecase(50) not null,
authority varchar_ignorecase(50) not null,
constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);
</programlisting></para>
<section>
<title>Group Authorities</title>
<para> Spring Security 2.0 introduced support for group authorities in
<classname>JdbcDaoImpl</classname>. The table structure if groups are enabled is as
follows:<programlisting xml:id="db-schema-groups">
create table groups (
id bigint generated by default as identity(start with 0) primary key,
group_name varchar_ignorecase(50) not null);
create table group_authorities (
group_id bigint not null,
authority varchar(50) not null,
constraint fk_group_authorities_group foreign key(group_id) references groups(id));
create table group_members (
id bigint generated by default as identity(start with 0) primary key,
username varchar(50) not null,
group_id bigint not null,
constraint fk_group_members_group foreign key(group_id) references groups(id));
</programlisting></para>
<para>Remember that these tables are only required if you are using the provided JDBC
<interfacename>UserDetailsService</interfacename> implementation. If you write your
own or choose to implement <interfacename>AuthenticationProvider</interfacename>
without a <interfacename>UserDetailsService</interfacename>, then you have complete
freedom over how you store the data, as long as the interface contract is
satisfied.</para>
</section>
</section>
<section>
<title>Persistent Login (Remember-Me) Schema</title>
<para> This table is used to store data used by the more secure <link
linkend="remember-me-persistent-token">persistent token</link> remember-me
implementation. If you are using <classname>JdbcTokenRepositoryImpl</classname> either
directly or through the namespace, then you will need this table.
<programlisting xml:id="db-schema-remeber-me">
create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null);
</programlisting></para>
</section>
<section xml:id="dbschema-acl">
<title>ACL Schema</title>
<para>There are four tables used by the Spring Security <link linkend="domain-acls"
>ACL</link> implementation. <orderedlist>
<listitem>
<para><literal>acl_sid</literal> stores the security identities recognised by the
ACL system. These can be unique principals or authorities which may apply to
multiple principals.</para>
</listitem>
<listitem>
<para><literal>acl_class</literal> defines the domain object types to which ACLs
apply. The <literal>class</literal> column stores the Java class name of the
object. </para>
</listitem>
<listitem>
<para><literal>acl_object_identity</literal> stores the object identity definitions
of specific domai objects.</para>
</listitem>
<listitem>
<para><literal>acl_entry</literal> stores the ACL permissions which apply to a
specific object identity and security identity.</para>
</listitem>
</orderedlist></para>
<para>It is assumed that the database will auto-generate the primary keys for each of the
identities. The <literal>JdbcMutableAclService</literal> has to be able to retrieve
these when it has created a new row in the <literal>acl_sid</literal> or
<literal>acl_class</literal> tables. It has two properties which define the SQL needed
to retrieve these values <literal>classIdentityQuery</literal> and
<literal>sidIdentityQuery</literal>. Both of these default to <literal>call
identity()</literal></para>
<section>
<title>Hypersonic SQL</title>
<para>The default schema works with the embedded HSQLDB database that is used in unit
tests within the
framework.<programlisting xml:id="dbschema-acl-hsql">
create table acl_sid (
id bigint generated by default as identity(start with 100) not null primary key,
principal boolean not null,
sid varchar_ignorecase(100) not null,
constraint unique_uk_1 unique(sid,principal) );
create table acl_class (
id bigint generated by default as identity(start with 100) not null primary key,
class varchar_ignorecase(100) not null,
constraint unique_uk_2 unique(class) );
create table acl_object_identity (
id bigint generated by default as identity(start with 100) not null primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint not null,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object)references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class)references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid)references acl_sid(id) );
create table acl_entry (
id bigint generated by default as identity(start with 100) not null primary key,
acl_object_identity bigint not null,ace_order int not null,sid bigint not null,
mask integer not null,granting boolean not null,audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity)
references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id) );
</programlisting></para>
</section>
<section>
<title>PostgreSQL</title>
<para>
<programlisting language="ddl">create table acl_sid(
id bigserial not null primary key,
principal boolean not null,
sid varchar(100) not null,
constraint unique_uk_1 unique(sid,principal));
create table acl_class(
id bigserial not null primary key,
class varchar(100) not null,
constraint unique_uk_2 unique(class));
create table acl_object_identity(
id bigserial primary key,
object_id_class bigint not null,
object_id_identity bigint not null,
parent_object bigint,
owner_sid bigint,
entries_inheriting boolean not null,
constraint unique_uk_3 unique(object_id_class,object_id_identity),
constraint foreign_fk_1 foreign key(parent_object) references acl_object_identity(id),
constraint foreign_fk_2 foreign key(object_id_class) references acl_class(id),
constraint foreign_fk_3 foreign key(owner_sid) references acl_sid(id));
create table acl_entry(
id bigserial primary key,
acl_object_identity bigint not null,
ace_order int not null,
sid bigint not null,
mask integer not null,
granting boolean not null,
audit_success boolean not null,
audit_failure boolean not null,
constraint unique_uk_4 unique(acl_object_identity,ace_order),
constraint foreign_fk_4 foreign key(acl_object_identity)
references acl_object_identity(id),
constraint foreign_fk_5 foreign key(sid) references acl_sid(id));
</programlisting> </para>
<para>You will have to set the <literal>classIdentityQuery</literal> and
<literal>sidIdentityQuery</literal> properties of
<classname>JdbcMutableAclService</classname> to the following values,
respectively: <itemizedlist>
<listitem>
<para><literal>select currval(pg_get_serial_sequence('acl_class',
'id'))</literal></para>
</listitem>
<listitem>
<para><literal>select currval(pg_get_serial_sequence('acl_sid',
'id'))</literal></para>
</listitem>
</itemizedlist></para>
</section>
</section>
</appendix>

View File

@ -1,456 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<appendix version="5.0" xml:id="appendix-dependencies" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude">
<info>
<title>Spring Security Dependencies</title>
</info>
<para>
This appendix provides a reference of the modules in Spring Security and the additional
dependencies that they require in order to function in a running application. We don't include
dependenices that are only used when building or testing Spring Security itself. Nor do we include
transitive dependencies which are required by external dependencies.
</para>
<para>The version of Spring required is listed on the project website, so the specific versions
are omitted for Spring dependencies below. Note that some of the dependencies listed as
<quote>optional</quote> below may still be required for other non-security functionality in
a Spring application. Also dependencies listed as <quote>optional</quote> may not actually be marked
as such in the project's Maven pom files if they are used in most applications. They are
<quote>optional</quote> only in the sense that you don't need them unless you are using the
specified functionality.</para>
<para>Where a module depends on another Spring Security module, the non-optional dependencies of the
module it depends on are also assumed to be required and are not listed separately.
</para>
<section>
<title><literal>spring-security-core</literal></title>
<para>The core module must be included in any project using Spring Security.
<table xml:id="deps-core">
<title>Core Depenendencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>aopalliance</entry>
<entry>1.0</entry>
<entry><para>Required for method security implementation.</para></entry>
</row>
<row>
<entry>ehcache</entry>
<entry>1.6.2</entry>
<entry><para>Required if the ehcache-based user cache implementation is used (optional).</para></entry>
</row>
<row>
<entry>spring-aop</entry>
<entry></entry>
<entry><para>Method security is based on Spring AOP</para></entry>
</row>
<row>
<entry>spring-beans</entry>
<entry></entry>
<entry><para>Required for Spring configuration</para></entry>
</row>
<row>
<entry>spring-expression</entry>
<entry></entry>
<entry><para>Required for expression-based method security (optional)</para></entry>
</row>
<row>
<entry>spring-jdbc</entry>
<entry></entry>
<entry><para>Required if using a database to store user data (optional).</para></entry>
</row>
<row>
<entry>spring-tx</entry>
<entry></entry>
<entry><para>Required if using a database to store user data (optional).</para></entry>
</row>
<row>
<entry>aspectjrt</entry>
<entry>1.6.10</entry>
<entry><para>Required if using AspectJ support (optional).</para></entry>
</row>
<row>
<entry>jsr250-api</entry>
<entry>1.0</entry>
<entry><para>Required if you are using JSR-250 method-security annotations (optional).</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-remoting</literal></title>
<para>This module is typically required in web applications which use the Servlet API.
<table xml:id="deps-remoting">
<title>Remoting Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-web</entry>
<entry></entry>
<entry><para>Required for clients which use HTTP remoting support.</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-web</literal></title>
<para>This module is typically required in web applications which use the Servlet API.
<table xml:id="deps-web">
<title>Web Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-web</entry>
<entry></entry>
<entry><para>Spring web support classes are used extensively.</para></entry>
</row>
<row>
<entry>spring-jdbc</entry>
<entry></entry>
<entry><para>Required for JDBC-based persistent remember-me token repository (optional).</para></entry>
</row>
<row>
<entry>spring-tx</entry>
<entry></entry>
<entry><para>Required by remember-me persistent token repository implementations (optional).</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-ldap</literal></title>
<para>This module is only required if you are using LDAP authentication.
<table xml:id="deps-ldap">
<title>LDAP Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-ldap-core</entry>
<entry>1.3.0</entry>
<entry><para>LDAP support is based on Spring LDAP.</para></entry>
</row>
<row>
<entry>spring-tx</entry>
<entry></entry>
<entry><para>Data exception classes are required.</para></entry>
</row>
<row>
<entry>apache-ds <footnote><para>The modules <literal>apacheds-core</literal>,
<literal>apacheds-core-entry</literal>, <literal>apacheds-protocol-shared</literal>,
<literal>apacheds-protocol-ldap</literal> and <literal>apacheds-server-jndi</literal> are required.
</para></footnote></entry>
<entry>1.5.5</entry>
<entry><para>Required if you are using an embedded LDAP server (optional).</para></entry>
</row>
<row>
<entry>shared-ldap</entry>
<entry>0.9.15</entry>
<entry><para>Required if you are using an embedded LDAP server (optional).</para></entry>
</row>
<row>
<entry>ldapsdk</entry>
<entry>4.1</entry>
<entry>
<para>Mozilla LdapSDK. Used for decoding LDAP password policy controls if you are
using password-policy functionality with OpenLDAP, for example.
</para>
</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-config</literal></title>
<para>This module is required if you are using Spring Security namespace configuration.
<table xml:id="deps-config">
<title>Config Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-security-web</entry>
<entry></entry>
<entry>Required if you are using any web-related namespace configuration (optional).</entry>
</row>
<row>
<entry>spring-security-ldap</entry>
<entry></entry>
<entry>Required if you are using the LDAP namespace options (optional).</entry>
</row>
<row>
<entry>spring-security-openid</entry>
<entry></entry>
<entry>Required if you are using OpenID authentication (optional).</entry>
</row>
<row>
<entry>aspectjweaver</entry>
<entry>1.6.10</entry>
<entry><para>Required if using the protect-pointcut namespace syntax (optional).</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-acl</literal></title>
<para>The ACL module.
<table xml:id="deps-acl">
<title>ACL Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>ehcache</entry>
<entry>1.6.2</entry>
<entry><para>Required if the ehcache-based ACL cache implementation is used (optional if you are using your own implementation).</para></entry>
</row>
<row>
<entry>spring-jdbc</entry>
<entry></entry>
<entry><para>Required if you are using the default JDBC-based AclService (optional if you implement your own).</para></entry>
</row>
<row>
<entry>spring-tx</entry>
<entry></entry>
<entry><para>Required if you are using the default JDBC-based AclService (optional if you implement your own).</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-cas</literal></title>
<para>The CAS module provides integration with JA-SIG CAS.
<table xml:id="deps-cas">
<title>CAS Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-security-web</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>cas-client-core</entry>
<entry>3.1.12</entry>
<entry>The JA-SIG CAS Client. This is the basis of the Spring Security integration.</entry>
</row>
<row>
<entry>ehcache</entry>
<entry>1.6.2</entry>
<entry><para>Required if you are using the ehcache-based ticket cache (optional).</para></entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-openid</literal></title>
<para>The OpenID module.
<table xml:id="deps-openid">
<title>OpenID Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-security-web</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>openid4java-nodeps</entry>
<entry>0.9.6</entry>
<entry>Spring Security's OpenID integration uses OpenID4Java.</entry>
</row>
<row>
<entry>httpclient</entry>
<entry>4.1.1</entry>
<entry>openid4java-nodeps depends on HttpClient 4.</entry>
</row>
<row>
<entry>guice</entry>
<entry>2.0</entry>
<entry>openid4java-nodeps depends on Guice 2.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
<section>
<title><literal>spring-security-taglibs</literal></title>
<para>Provides Spring Security's JSP tag implementations.
<table xml:id="deps-taglibs">
<title>Taglib Dependencies</title>
<tgroup cols="3" align="left">
<colspec colnum="1" colname="col1" colwidth="2*"/>
<colspec colnum="2" colname="col2" colwidth="1*"/>
<colspec colnum="3" colname="col3" colwidth="3*"/>
<thead>
<row>
<entry align="center">Dependency</entry>
<entry align="center">Version</entry>
<entry align="center">Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>spring-security-core</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-security-web</entry>
<entry></entry>
<entry></entry>
</row>
<row>
<entry>spring-security-acl</entry>
<entry></entry>
<entry>
Required if you are using the <literal>accesscontrollist</literal> tag or
<literal>hasPermission()</literal> expressions with ACLs (optional).
</entry>
</row>
<row>
<entry>spring-expression</entry>
<entry></entry>
<entry>Required if you are using SPEL expressions in your tag access constraints.</entry>
</row>
</tbody>
</tgroup>
</table>
</para>
</section>
</appendix>

File diff suppressed because it is too large Load Diff

View File

@ -1,345 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="authz-arch"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Authorization Architecture</title>
</info>
<section xml:id="authz-authorities">
<info>
<title>Authorities</title>
</info>
<para>As we saw in the <link linkend="tech-granted-authority">technical overview</link>,
all <interfacename>Authentication</interfacename> implementations store a list of
<interfacename>GrantedAuthority</interfacename> objects. These represent the authorities
that have been granted to the principal. The
<interfacename>GrantedAuthority</interfacename> objects are inserted into the
<interfacename>Authentication</interfacename> object by the
<interfacename>AuthenticationManager</interfacename> and are later read by
<interfacename>AccessDecisionManager</interfacename>s when making authorization
decisions.</para>
<para><interfacename>GrantedAuthority</interfacename> is an interface with only one method:
<programlisting language="java">
String getAuthority();
</programlisting> This method allows
<interfacename>AccessDecisionManager</interfacename>s to obtain a precise
<literal>String</literal> representation of the
<interfacename>GrantedAuthority</interfacename>. By returning a representation as a
<literal>String</literal>, a <interfacename>GrantedAuthority</interfacename> can be
easily <quote>read</quote> by most
<interfacename>AccessDecisionManager</interfacename>s. If a
<interfacename>GrantedAuthority</interfacename> cannot be precisely represented as a
<literal>String</literal>, the <interfacename>GrantedAuthority</interfacename> is
considered <quote>complex</quote> and <literal>getAuthority()</literal> must return
<literal>null</literal>.</para>
<para>An example of a <quote>complex</quote> <interfacename>GrantedAuthority</interfacename>
would be an implementation that stores a list of operations and authority thresholds
that apply to different customer account numbers. Representing this complex
<interfacename>GrantedAuthority</interfacename> as a <literal>String</literal> would be
quite difficult, and as a result the <literal>getAuthority()</literal> method should
return <literal>null</literal>. This will indicate to any
<interfacename>AccessDecisionManager</interfacename> that it will need to specifically
support the <interfacename>GrantedAuthority</interfacename> implementation in order to
understand its contents.</para>
<para>Spring Security includes one concrete <interfacename>GrantedAuthority</interfacename>
implementation, <literal>GrantedAuthorityImpl</literal>. This allows any user-specified
<literal>String</literal> to be converted into a
<interfacename>GrantedAuthority</interfacename>. All
<classname>AuthenticationProvider</classname>s included with the security architecture
use <literal>GrantedAuthorityImpl</literal> to populate the
<interfacename>Authentication</interfacename> object.</para>
</section>
<section xml:id="authz-pre-invocation">
<info>
<title>Pre-Invocation Handling</title>
</info>
<para> As we've also seen in the <link linkend="secure-objects">Technical
Overview</link> chapter, Spring Security provides interceptors which control access to
secure objects such as method invocations or web requests. A pre-invocation decision on
whether the invocation is allowed to proceed is made by the
<interfacename>AccessDecisionManager</interfacename>. </para>
<section xml:id="authz-access-decision-manager">
<title>The AccessDecisionManager</title>
<para>The <interfacename>AccessDecisionManager</interfacename> is called by the
<classname>AbstractSecurityInterceptor</classname> and is responsible for making
final access control decisions. The
<interfacename>AccessDecisionManager</interfacename> interface contains three
methods:
<programlisting language="java">
void decide(Authentication authentication, Object secureObject,
Collection&lt;ConfigAttribute&gt; attrs) throws AccessDeniedException;
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
</programlisting>
The <interfacename>AccessDecisionManager</interfacename>'s
<methodname>decide</methodname> method is passed all the relevant information it
needs in order to make an authorization decision. In particular, passing the secure
<literal>Object</literal> enables those arguments contained in the actual secure
object invocation to be inspected. For example, let's assume the secure object was a
<classname>MethodInvocation</classname>. It would be easy to query the
<classname>MethodInvocation</classname> for any <literal>Customer</literal>
argument, and then implement some sort of security logic in the
<interfacename>AccessDecisionManager</interfacename> to ensure the principal is
permitted to operate on that customer. Implementations are expected to throw an
<literal>AccessDeniedException</literal> if access is denied.</para>
<para>The <literal>supports(ConfigAttribute)</literal> method is called by the
<classname>AbstractSecurityInterceptor</classname> at startup time to determine if
the <interfacename>AccessDecisionManager</interfacename> can process the passed
<literal>ConfigAttribute</literal>. The <literal>supports(Class)</literal> method is
called by a security interceptor implementation to ensure the configured
<interfacename>AccessDecisionManager</interfacename> supports the type of secure
object that the security interceptor will present.</para>
</section>
<section xml:id="authz-voting-based">
<title>Voting-Based AccessDecisionManager Implementations</title>
<para>Whilst users can implement their own
<interfacename>AccessDecisionManager</interfacename> to control all aspects of
authorization, Spring Security includes several
<interfacename>AccessDecisionManager</interfacename> implementations that are based
on voting. <xref linkend="authz-access-voting"/> illustrates the relevant
classes.</para>
<figure xml:id="authz-access-voting">
<title>Voting Decision Manager</title>
<mediaobject>
<imageobject>
<imagedata align="center" fileref="images/access-decision-voting.png"
format="PNG" scale="75"/>
</imageobject>
</mediaobject>
</figure>
<para>Using this approach, a series of
<interfacename>AccessDecisionVoter</interfacename> implementations are polled on an
authorization decision. The <interfacename>AccessDecisionManager</interfacename>
then decides whether or not to throw an <literal>AccessDeniedException</literal>
based on its assessment of the votes.</para>
<para>The <interfacename>AccessDecisionVoter</interfacename> interface has three
methods:
<programlisting language="java">
int vote(Authentication authentication, Object object, Collection&lt;ConfigAttribute&gt; attrs);
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
</programlisting>
Concrete implementations return an <literal>int</literal>, with possible values
being reflected in the <interfacename>AccessDecisionVoter</interfacename> static
fields <literal>ACCESS_ABSTAIN</literal>, <literal>ACCESS_DENIED</literal> and
<literal>ACCESS_GRANTED</literal>. A voting implementation will return
<literal>ACCESS_ABSTAIN</literal> if it has no opinion on an authorization decision.
If it does have an opinion, it must return either <literal>ACCESS_DENIED</literal>
or <literal>ACCESS_GRANTED</literal>.</para>
<para>There are three concrete <interfacename>AccessDecisionManager</interfacename>s
provided with Spring Security that tally the votes. The
<literal>ConsensusBased</literal> implementation will grant or deny access based on
the consensus of non-abstain votes. Properties are provided to control behavior in
the event of an equality of votes or if all votes are abstain. The
<literal>AffirmativeBased</literal> implementation will grant access if one or more
<literal>ACCESS_GRANTED</literal> votes were received (i.e. a deny vote will be
ignored, provided there was at least one grant vote). Like the
<literal>ConsensusBased</literal> implementation, there is a parameter that controls
the behavior if all voters abstain. The <literal>UnanimousBased</literal> provider
expects unanimous <literal>ACCESS_GRANTED</literal> votes in order to grant access,
ignoring abstains. It will deny access if there is any
<literal>ACCESS_DENIED</literal> vote. Like the other implementations, there is a
parameter that controls the behaviour if all voters abstain.</para>
<para>It is possible to implement a custom
<interfacename>AccessDecisionManager</interfacename> that tallies votes differently.
For example, votes from a particular
<interfacename>AccessDecisionVoter</interfacename> might receive additional
weighting, whilst a deny vote from a particular voter may have a veto effect.</para>
<section xml:id="authz-role-voter">
<title><classname>RoleVoter</classname></title>
<para> The most commonly used <interfacename>AccessDecisionVoter</interfacename>
provided with Spring Security is the simple <classname>RoleVoter</classname>,
which treats configuration attributes as simple role names and votes to grant
access if the user has been assigned that role.</para>
<para>It will vote if any <interfacename>ConfigAttribute</interfacename> begins with
the prefix <literal>ROLE_</literal>. It will vote to grant access if there is a
<interfacename>GrantedAuthority</interfacename> which returns a
<literal>String</literal> representation (via the
<literal>getAuthority()</literal> method) exactly equal to one or more
<literal>ConfigAttributes</literal> starting with the prefix
<literal>ROLE_</literal>. If there is no exact match of any
<literal>ConfigAttribute</literal> starting with <literal>ROLE_</literal>, the
<literal>RoleVoter</literal> will vote to deny access. If no
<literal>ConfigAttribute</literal> begins with <literal>ROLE_</literal>, the
voter will abstain.</para>
</section>
<section xml:id="authz-authenticated-voter">
<title><classname>AuthenticatedVoter</classname></title>
<para> Another voter which we've implicitly seen is the
<classname>AuthenticatedVoter</classname>, which can be used to differentiate
between anonymous, fully-authenticated and remember-me authenticated users. Many
sites allow certain limited access under remember-me authentication, but require
a user to confirm their identity by logging in for full access.</para>
<para>When we've used the attribute <literal>IS_AUTHENTICATED_ANONYMOUSLY</literal>
to grant anonymous access, this attribute was being processed by the
<classname>AuthenticatedVoter</classname>. See the Javadoc for this class for
more information. </para>
</section>
<section xml:id="authz-custom-voter">
<title>Custom Voters</title>
<para>Obviously, you can also implement a custom
<interfacename>AccessDecisionVoter</interfacename> and you can
put just about any access-control logic you want in it. It might
be specific to your application (business-logic related) or it
might implement some security administration logic. For example, you'll find
a <link xlink:href='http://blog.springsource.com/2009/01/02/spring-security-customization-part-2-adjusting-secured-session-in-real-time/'>
blog article</link> on the SpringSource web site which describes how to
use a voter to deny access in real-time to users whose accounts have
been suspended.
</para>
</section>
</section>
</section>
<section xml:id="authz-after-invocation-handling">
<info>
<title>After Invocation Handling</title>
</info>
<para>Whilst the <interfacename>AccessDecisionManager</interfacename> is called by the
<classname>AbstractSecurityInterceptor</classname> before proceeding with the secure
object invocation, some applications need a way of modifying the object actually
returned by the secure object invocation. Whilst you could easily implement your own AOP
concern to achieve this, Spring Security provides a convenient hook that has several
concrete implementations that integrate with its ACL capabilities.</para>
<para><xref linkend="authz-after-invocation"/> illustrates Spring Security's
<literal>AfterInvocationManager</literal> and its concrete implementations. <figure
xml:id="authz-after-invocation">
<title>After Invocation Implementation</title>
<mediaobject>
<imageobject>
<imagedata align="center" fileref="images/after-invocation.png" format="PNG"
scale="75"/>
</imageobject>
</mediaobject>
</figure></para>
<para>Like many other parts of Spring Security, <literal>AfterInvocationManager</literal>
has a single concrete implementation, <literal>AfterInvocationProviderManager</literal>,
which polls a list of <literal>AfterInvocationProvider</literal>s. Each
<literal>AfterInvocationProvider</literal> is allowed to modify the return object or
throw an <literal>AccessDeniedException</literal>. Indeed multiple providers can modify
the object, as the result of the previous provider is passed to the next in the
list.</para>
<para>Please be aware that if you're using <literal>AfterInvocationManager</literal>, you
will still need configuration attributes that allow the
<classname>MethodSecurityInterceptor</classname>'s
<interfacename>AccessDecisionManager</interfacename> to allow an operation. If you're
using the typical Spring Security included
<interfacename>AccessDecisionManager</interfacename> implementations, having no
configuration attributes defined for a particular secure method invocation will cause
each <interfacename>AccessDecisionVoter</interfacename> to abstain from voting. In turn,
if the <interfacename>AccessDecisionManager</interfacename> property
"<literal>allowIfAllAbstainDecisions</literal>" is <literal>false</literal>, an
<literal>AccessDeniedException</literal> will be thrown. You may avoid this potential
issue by either (i) setting "<literal>allowIfAllAbstainDecisions</literal>" to
<literal>true</literal> (although this is generally not recommended) or (ii) simply
ensure that there is at least one configuration attribute that an
<interfacename>AccessDecisionVoter</interfacename> will vote to grant access for. This
latter (recommended) approach is usually achieved through a <literal>ROLE_USER</literal>
or <literal>ROLE_AUTHENTICATED</literal> configuration attribute.</para>
<!-- TODO: Move to ACL section and add reference here -->
<!--
<section xml:id="after-invocation-acl-aware">
<info>
<title>ACL-Aware AfterInvocationProviders</title>
</info>
<para>A common services layer method we've all written at one stage or another looks like
this:</para>
<para>
<programlisting language="java">public Contact getById(Integer id);</programlisting>
</para>
<para>Quite often, only principals with permission to read the <literal>Contact</literal>
should be allowed to obtain it. In this situation the
<interfacename>AccessDecisionManager</interfacename> approach provided by the
<classname>AbstractSecurityInterceptor</classname> will not suffice. This is because the
identity of the <literal>Contact</literal> is all that is available before the secure object
is invoked. The <classname>AclEntryAfterInvocationProvider</classname> delivers a solution,
and is configured as follows: <programlisting language="xml"><![CDATA[
<bean id="afterAclRead"
class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationProvider">
<constructor-arg ref="aclService"/>
<constructor-arg>
<list>
<ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
<ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
</list>
</constructor-arg>
</bean>
]]></programlisting> In the above example, the <literal>Contact</literal> will be retrieved and
passed to the <classname>AclEntryAfterInvocationProvider</classname>. The provider will
thrown an <classname>AccessDeniedException</classname> if one of the listed
<literal>requirePermission</literal>s is not held by the
<interfacename>Authentication</interfacename>. The
<classname>AclEntryAfterInvocationProvider</classname> queries the acl service to
determine the ACL that applies for this domain object to this
<interfacename>Authentication</interfacename>.</para>
<para>Similar to the <classname>AclEntryAfterInvocationProvider</classname> is
<classname>AclEntryAfterInvocationCollectionFilteringProvider</classname>. It is designed
to remove <literal>Collection</literal> or array elements for which a principal does not
have access. It never thrown an <classname>AccessDeniedException</classname> - simply
silently removes the offending elements. The provider is configured as follows: <programlisting language="xml"><![CDATA[
<bean id="afterAclCollectionRead"
class="org.springframework.security.acls.afterinvocation.AclEntryAfterInvocationCollectionFilteringProvider">
<constructor-arg ref="aclService"/>
<constructor-arg>
<list>
<ref local="org.springframework.security.acls.domain.BasePermission.ADMINISTRATION"/>
<ref local="org.springframework.security.acls.domain.BasePermission.READ"/>
</list>
</constructor-arg>
</bean>
]]> </programlisting> As you can imagine, the returned <literal>Object</literal> must be a
<literal>Collection</literal> or array for this provider to operate. It will remove any
element if the <literal>AclManager</literal> indicates the
<interfacename>Authentication</interfacename> does not hold one of the listed
<literal>requirePermission</literal>s.</para>
<para>The Contacts sample application demonstrates these two
<literal>AfterInvocationProvider</literal>s.</para>
</section> -->
</section>
<section xml:id="authz-hierarchical-roles">
<title>Hierarchical Roles</title>
<para>
It is a common requirement that a particular role in an application should automatically
<quote>include</quote> other roles. For example, in an application which has the concept of
an <quote>admin</quote> and a <quote>user</quote> role, you may want an admin to be able to
do everything a normal user can. To achieve this, you can either make sure that all admin users
are also assigned the <quote>user</quote> role. Alternatively, you can modify every access constraint
which requires the <quote>user</quote> role to also include the <quote>admin</quote> role.
This can get quite complicated if you have a lot of different roles in your application.
</para>
<para>
The use of a role-hierarchy allows you to configure which roles (or authorities) should include others.
An extended version of Spring Security's <link linkend="authz-role-voter"><classname>RoleVoter</classname></link>,
<classname>RoleHierarchyVoter</classname>, is configured with a <interfacename>RoleHierarchy</interfacename>,
from which it obtains all the <quote>reachable authorities</quote> which the user is assigned.
A typical configuration might look like this:
<programlisting language="xml"><![CDATA[
<bean id="roleVoter" class="org.springframework.security.access.vote.RoleHierarchyVoter">
<constructor-arg ref="roleHierarchy" />
</bean>
<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_STAFF
ROLE_STAFF > ROLE_USER
ROLE_USER > ROLE_GUEST
</value>
</property>
</bean>]]>
</programlisting>
Here we have four roles in a hierarchy <literal>ROLE_ADMIN => ROLE_STAFF => ROLE_USER => ROLE_GUEST</literal>.
A user who is authenticated with <literal>ROLE_ADMIN</literal>, will behave as if they have all four roles when
security contraints are evaluated against an <interfacename>AccessDecisionManager</interfacename> cconfigured
with the above <classname>RoleHierarchyVoter</classname>. The <literal>&gt;</literal> symbol can be thought of
as meaning <quote>includes</quote>.
</para>
<para>
Role hierarchies offer a convenient means of simplifying the access-control configuration data for your
application and/or reducing the number of authorities which you need to assign to a user. For more
complex requirements you may wish to define a logical mapping between the specific access-rights your
application requires and the roles that are assigned to users, translating between the two when loading
the user information.
<!-- TODO: Extend when authority-mapping layer is added -->
</para>
</section>
</chapter>

View File

@ -1,169 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="basic">
<info>
<title>Basic and Digest Authentication</title>
</info>
<para>Basic and digest authentiation are alternative authentication mechanisms which are popular
in web applications. Basic authentication is often used with stateless clients which pass
their credentials on each request. It's quite common to use it in combination with
form-based authentication where an application is used through both a browser-based user
interface and as a web-service. However, basic authentication transmits the password as
plain text so it should only really be used over an encrypted transport layer such as
HTTPS.</para>
<section xml:id="basic-processing-filter">
<info>
<title><classname>BasicAuthenticationFilter</classname></title>
</info>
<para><literal>BasicAuthenticationFilter</literal> is responsible for processing basic
authentication credentials presented in HTTP headers. This can be used for
authenticating calls made by Spring remoting protocols (such as Hessian and Burlap), as
well as normal browser user agents (such as Firefox and Internet Explorer). The standard
governing HTTP Basic Authentication is defined by RFC 1945, Section 11, and
<literal>BasicAuthenticationFilter</literal> conforms with this RFC. Basic
Authentication is an attractive approach to authentication, because it is very widely
deployed in user agents and implementation is extremely simple (it's just a Base64
encoding of the username:password, specified in an HTTP header).</para>
<section xml:id="basic-config">
<info>
<title>Configuration</title>
</info>
<para>To implement HTTP Basic Authentication, you need to add a
<literal>BasicAuthenticationFilter</literal> to your filter chain. The application
context should contain <literal>BasicAuthenticationFilter</literal> and its required
collaborator:</para>
<para> <programlisting language="xml"><![CDATA[
<bean id="basicAuthenticationFilter"
class="org.springframework.security.web.authentication.www.BasicAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
</bean>
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<property name="realmName" value="Name Of Your Realm"/>
</bean>]]>
</programlisting> </para>
<para>The configured <interfacename>AuthenticationManager</interfacename> processes each
authentication request. If authentication fails, the configured
<interfacename>AuthenticationEntryPoint</interfacename> will be used to retry the
authentication process. Usually you will use the filter in combination with a
<literal>BasicAuthenticationEntryPoint</literal>, which returns a 401 response with
a suitable header to retry HTTP Basic authentication. If authentication is
successful, the resulting <interfacename>Authentication</interfacename> object will
be placed into the <classname>SecurityContextHolder</classname> as usual.</para>
<para>If the authentication event was successful, or authentication was not attempted
because the HTTP header did not contain a supported authentication request, the
filter chain will continue as normal. The only time the filter chain will be
interrupted is if authentication fails and the
<interfacename>AuthenticationEntryPoint</interfacename> is called.</para>
</section>
</section>
<section xml:id="digest-processing-filter">
<title><classname>DigestAuthenticationFilter</classname></title>
<para><classname>DigestAuthenticationFilter</classname> is capable of processing digest
authentication credentials presented in HTTP headers. Digest Authentication attempts to
solve many of the weaknesses of Basic authentication, specifically by ensuring
credentials are never sent in clear text across the wire. Many user agents support
Digest Authentication, including FireFox and Internet Explorer. The standard governing
HTTP Digest Authentication is defined by RFC 2617, which updates an earlier version of
the Digest Authentication standard prescribed by RFC 2069. Most user agents implement
RFC 2617. Spring Security's <classname>DigestAuthenticationFilter</classname> is
compatible with the "<literal>auth</literal>" quality of protection
(<literal>qop</literal>) prescribed by RFC 2617, which also provides backward
compatibility with RFC 2069. Digest Authentication is a more attractive option if you
need to use unencrypted HTTP (i.e. no TLS/HTTPS) and wish to maximise security of the
authentication process. Indeed Digest Authentication is a mandatory requirement for the
WebDAV protocol, as noted by RFC 2518 Section 17.1.</para>
<para>Digest Authentication is definitely the most secure choice between Form
Authentication, Basic Authentication and Digest Authentication, although extra security
also means more complex user agent implementations. Central to Digest Authentication is
a "nonce". This is a value the server generates. Spring Security's nonce adopts the
following format:</para>
<para>
<programlisting language="txt">
base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
expirationTime: The date and time when the nonce expires, expressed in milliseconds
key: A private key to prevent modification of the nonce token
</programlisting> </para>
<para>The <classname>DigestAuthenticatonEntryPoint</classname> has a property specifying the
<literal>key</literal> used for generating the nonce tokens, along with a
<literal>nonceValiditySeconds</literal> property for determining the expiration time
(default 300, which equals five minutes). Whist ever the nonce is valid, the digest is
computed by concatenating various strings including the username, password, nonce, URI
being requested, a client-generated nonce (merely a random value which the user agent
generates each request), the realm name etc, then performing an MD5 hash. Both the
server and user agent perform this digest computation, resulting in different hash codes
if they disagree on an included value (eg password). In Spring Security implementation,
if the server-generated nonce has merely expired (but the digest was otherwise valid),
the <classname>DigestAuthenticationEntryPoint</classname> will send a
<literal>"stale=true"</literal> header. This tells the user agent there is no need to
disturb the user (as the password and username etc is correct), but simply to try again
using a new nonce.</para>
<para>An appropriate value for <classname>DigestAuthenticationEntryPoint</classname>'s
<literal>nonceValiditySeconds</literal> parameter will depend on your application.
Extremely secure applications should note that an intercepted authentication header can
be used to impersonate the principal until the <literal>expirationTime</literal>
contained in the nonce is reached. This is the key principle when selecting an
appropriate setting, but it would be unusual for immensely secure applications to not be
running over TLS/HTTPS in the first instance.</para>
<para>Because of the more complex implementation of Digest Authentication, there are often
user agent issues. For example, Internet Explorer fails to present an
"<literal>opaque</literal>" token on subsequent requests in the same session. Spring
Security filters therefore encapsulate all state information into the
"<literal>nonce</literal>" token instead. In our testing, Spring Security's
implementation works reliably with FireFox and Internet Explorer, correctly handling
nonce timeouts etc.</para>
<section xml:id="digest-config">
<title>Configuration</title>
<para>Now that we've reviewed the theory, let's see how to use it. To implement HTTP
Digest Authentication, it is necessary to define
<literal>DigestAuthenticationFilter</literal> in the filter chain. The application
context will need to define the <literal>DigestAuthenticationFilter</literal> and
its required collaborators:</para>
<para> <programlisting language="xml"><![CDATA[
<bean id="digestFilter" class=
"org.springframework.security.web.authentication.www.DigestAuthenticationFilter">
<property name="userDetailsService" ref="jdbcDaoImpl"/>
<property name="authenticationEntryPoint" ref="digestEntryPoint"/>
<property name="userCache" ref="userCache"/>
</bean>
<bean id="digestEntryPoint" class=
"org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint">
<property name="realmName" value="Contacts Realm via Digest Authentication"/>
<property name="key" value="acegi"/>
<property name="nonceValiditySeconds" value="10"/>
</bean>]]>
</programlisting> </para>
<para>The configured <interfacename>UserDetailsService</interfacename> is needed because
<literal>DigestAuthenticationFilter</literal> must have direct access to the clear
text password of a user. Digest Authentication will NOT work if you are using
encoded passwords in your DAO <footnote><para>It is possible to encode the password in the
format HEX( MD5(username:realm:password) ) provided the
<code>DigestAuthenticationFilter.passwordAlreadyEncoded</code> is set to <code>true</code>.
However, other password encodings will not work with digest authentication.</para></footnote>.
The DAO collaborator, along with the <literal>UserCache</literal>, are typically shared directly
with a <classname>DaoAuthenticationProvider</classname>. The
<literal>authenticationEntryPoint</literal> property must be
<classname>DigestAuthenticationEntryPoint</classname>, so that
<classname>DigestAuthenticationFilter</classname> can obtain the correct
<literal>realmName</literal> and <literal>key</literal> for digest
calculations.</para>
<para>Like <literal>BasicAuthenticationFilter</literal>, if authentication is successful
an <interfacename>Authentication</interfacename> request token will be placed into
the <classname>SecurityContextHolder</classname>. If the authentication event was
successful, or authentication was not attempted because the HTTP header did not
contain a Digest Authentication request, the filter chain will continue as normal.
The only time the filter chain will be interrupted is if authentication fails and
the <interfacename>AuthenticationEntryPoint</interfacename> is called, as discussed
in the previous paragraph.</para>
<para>Digest Authentication's RFC offers a range of additional features to further
increase security. For example, the nonce can be changed on every request. Despite
this, Spring Security implementation was designed to minimise the complexity of the
implementation (and the doubtless user agent incompatibilities that would emerge),
and avoid needing to store server-side state. You are invited to review RFC 2617 if
you wish to explore these features in more detail. As far as we are aware, Spring
Security's implementation does comply with the minimum standards of this RFC.</para>
</section>
</section>
</chapter>

View File

@ -1,567 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="cas"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>CAS Authentication</title>
<section xml:id="cas-overview">
<title>Overview</title>
<para>JA-SIG produces an enterprise-wide single sign on system known as CAS. Unlike other
initiatives, JA-SIG's Central Authentication Service is open source, widely used, simple
to understand, platform independent, and supports proxy capabilities. Spring Security
fully supports CAS, and provides an easy migration path from single-application
deployments of Spring Security through to multiple-application deployments secured by an
enterprise-wide CAS server.</para>
<para>You can learn more about CAS at <literal>http://www.ja-sig.org/cas</literal>. You will
also need to visit this site to download the CAS Server files.</para>
</section>
<section xml:id="cas-how-it-works">
<info>
<title>How CAS Works</title>
</info>
<para>Whilst the CAS web site contains documents that detail the architecture of CAS, we
present the general overview again here within the context of Spring Security. Spring Security
3.x supports CAS 3. At the time of writing, the CAS server was at version 3.4.</para>
<para>Somewhere in your enterprise you will need to setup a CAS server. The CAS server is
simply a standard WAR file, so there isn't anything difficult about setting up your
server. Inside the WAR file you will customise the login and other single sign on pages
displayed to users.</para>
<para>When deploying a CAS 3.4 server, you will also need to specify an
<literal>AuthenticationHandler</literal> in the
<filename>deployerConfigContext.xml</filename> included with CAS. The
<literal>AuthenticationHandler</literal> has a simple method that returns a boolean as to
whether a given set of Credentials is valid. Your <literal>AuthenticationHandler</literal>
implementation will need to link into some type of backend authentication repository, such as
an LDAP server or database. CAS itself includes numerous
<literal>AuthenticationHandler</literal>s out of the box to assist with this. When you
download and deploy the server war file, it is set up to successfully authenticate users who
enter a password matching their username, which is useful for testing.</para>
<para>Apart from the CAS server itself, the other key players are of course the secure web
applications deployed throughout your enterprise. These web applications are known as
"services". There are three types of services. Those that authenticate service tickets, those that
can obtain proxy tickets, and those that authenticate proxy tickets. Authenticating a proxy ticket
differs because the list of proxies must be validated and often times a proxy ticket can be reused.</para>
<section xml:id="cas-sequence">
<title>Spring Security and CAS Interaction Sequence</title>
<para>The basic interaction between a web browser, CAS server and a
Spring Security-secured service is as follows:</para>
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>The web user is browsing the service's public pages. CAS or
Spring Security is not involved.</para>
</listitem>
<listitem>
<para>The user eventually requests a page that is either secure or
one of the beans it uses is secure. Spring Security's
<classname>ExceptionTranslationFilter</classname> will detect the
<classname>AccessDeniedException</classname> or <classname>AuthenticationException</classname>.</para>
</listitem>
<listitem>
<para>Because the user's <interfacename>Authentication</interfacename> object (or lack
thereof) caused an <classname>AuthenticationException</classname>, the
<classname>ExceptionTranslationFilter</classname> will call the configured
<interfacename>AuthenticationEntryPoint</interfacename>. If using CAS, this will be
the <classname>CasAuthenticationEntryPoint</classname> class.</para>
</listitem>
<listitem>
<para>The <classname>CasAuthenticationEntryPoint</classname> will redirect the user's browser
to the CAS server. It will also indicate a <literal>service</literal> parameter, which
is the callback URL for the Spring Security service (your application). For example, the
URL to which the browser is redirected might be
<literal>https://my.company.com/cas/login?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_spring_cas_security_check</literal>.</para>
</listitem>
<listitem>
<para>After the user's browser redirects to CAS, they will be
prompted for their username and password. If the user presents a
session cookie which indicates they've previously logged on, they
will not be prompted to login again (there is an exception to this
procedure, which we'll cover later). CAS will use the
<interfacename>PasswordHandler</interfacename> (or
<interfacename>AuthenticationHandler</interfacename> if using CAS 3.0)
discussed above to decide whether the username and password is
valid.</para>
</listitem>
<listitem>
<para>Upon successful login, CAS will redirect the user's browser
back to the original service. It will also include a
<literal>ticket</literal> parameter, which is an opaque string
representing the "service ticket". Continuing our earlier example,
the URL the browser is redirected to might be
<literal>https://server3.company.com/webapp/j_spring_cas_security_check?ticket=ST-0-ER94xMJmn6pha35CQRoZ</literal>.</para>
</listitem>
<listitem>
<para>Back in the service web application, the <classname>CasAuthenticationFilter</classname> is
always listening for requests to <literal>/j_spring_cas_security_check</literal> (this
is configurable, but we'll use the defaults in this introduction). The processing filter
will construct a <classname>UsernamePasswordAuthenticationToken</classname> representing the
service ticket. The principal will be equal to
<literal>CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER</literal>, whilst the credentials
will be the service ticket opaque value. This authentication request will then be handed
to the configured <interfacename>AuthenticationManager</interfacename>.</para>
</listitem>
<listitem>
<para>The <interfacename>AuthenticationManager</interfacename> implementation
will be the <classname>ProviderManager</classname>, which is in turn
configured with the <classname>CasAuthenticationProvider</classname>.
The <classname>CasAuthenticationProvider</classname> only responds to
<classname>UsernamePasswordAuthenticationToken</classname>s containing
the CAS-specific principal (such as
<literal>CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER</literal>)
and <classname>CasAuthenticationToken</classname>s (discussed
later).</para>
</listitem>
<listitem>
<para><classname>CasAuthenticationProvider</classname> will validate the service ticket using a
<interfacename>TicketValidator</interfacename> implementation. This will typically be a
<classname>Cas20ServiceTicketValidator</classname> which is one of the classes
included in the CAS client library. In the event the application needs to validate proxy tickets, the
<classname>Cas20ProxyTicketValidator</classname> is used. The
<interfacename>TicketValidator</interfacename> makes an HTTPS request to the CAS server in order to
validate the service ticket. It may also include a proxy callback URL, which is included in this example:
<literal>https://my.company.com/cas/proxyValidate?service=https%3A%2F%2Fserver3.company.com%2Fwebapp%2Fj_spring_cas_security_check&amp;ticket=ST-0-ER94xMJmn6pha35CQRoZ&amp;pgtUrl=https://server3.company.com/webapp/j_spring_cas_security_proxyreceptor</literal>.
</para>
</listitem>
<listitem>
<para>Back on the CAS server, the validation request will be
received. If the presented service ticket matches the service URL
the ticket was issued to, CAS will provide an affirmative response
in XML indicating the username. If any proxy was involved in the
authentication (discussed below), the list of proxies is also
included in the XML response.</para>
</listitem>
<listitem>
<para>[OPTIONAL] If the request to the CAS validation service included the proxy callback
URL (in the <literal>pgtUrl</literal> parameter), CAS will include a
<literal>pgtIou</literal> string in the XML response. This <literal>pgtIou</literal>
represents a proxy-granting ticket IOU. The CAS server will then create its own HTTPS
connection back to the <literal>pgtUrl</literal>. This is to mutually authenticate the
CAS server and the claimed service URL. The HTTPS connection will be used to send a
proxy granting ticket to the original web application. For example,
<literal>https://server3.company.com/webapp/j_spring_cas_security_proxyreceptor?pgtIou=PGTIOU-0-R0zlgrl4pdAQwBvJWO3vnNpevwqStbSGcq3vKB2SqSFFRnjPHt&amp;pgtId=PGT-1-si9YkkHLrtACBo64rmsi3v2nf7cpCResXg5MpESZFArbaZiOKH</literal>.</para>
</listitem>
<listitem>
<para>The <classname>Cas20TicketValidator</classname> will parse the XML received from the
CAS server. It will return to the <classname>CasAuthenticationProvider</classname> a
<literal>TicketResponse</literal>, which includes the username (mandatory), proxy list
(if any were involved), and proxy-granting ticket IOU (if the proxy callback was
requested).</para>
</listitem>
<listitem>
<para>Next <literal>CasAuthenticationProvider</literal> will call
a configured <literal>CasProxyDecider</literal>. The
<literal>CasProxyDecider</literal> indicates whether the proxy
list in the <literal>TicketResponse</literal> is acceptable to the
service. Several implementations are provided with Spring
Security: <literal>RejectProxyTickets</literal>,
<literal>AcceptAnyCasProxy</literal> and
<literal>NamedCasProxyDecider</literal>. These names are largely
self-explanatory, except <literal>NamedCasProxyDecider</literal>
which allows a <literal>List</literal> of trusted proxies to be
provided.</para>
</listitem>
<listitem>
<para><classname>CasAuthenticationProvider</classname> will next
request a <interfacename>AuthenticationUserDetailsService</interfacename> to load the
<interfacename>GrantedAuthority</interfacename> objects that apply to the user
contained in the <interfacename>Assertion</interfacename>.</para>
</listitem>
<listitem>
<para>If there were no problems,
<classname>CasAuthenticationProvider</classname> constructs a
<classname>CasAuthenticationToken</classname> including the details
contained in the <interfacename>TicketResponse</interfacename> and the
<interfacename>GrantedAuthority</interfacename>s.</para>
</listitem>
<listitem>
<para>Control then returns to
<classname>CasAuthenticationFilter</classname>, which places the created
<classname>CasAuthenticationToken</classname> in the security context.</para>
</listitem>
<listitem>
<para>The user's browser is redirected to the original page that
caused the <classname>AuthenticationException</classname> (or a
<link linkend="form-login-flow-handling">custom destination</link> depending on
the configuration).</para>
</listitem>
</orderedlist>
<para>It's good that you're still here! Let's now look at how this is configured</para>
</section>
</section>
<section xml:id="cas-client">
<info>
<title>Configuration of CAS Client</title>
</info>
<para>The web application side of CAS is made easy due to Spring Security. It is assumed you
already know the basics of using Spring Security, so these are not covered again below.
We'll assume a namespace based configuration is being used and add in the CAS beans as
required. Each section builds upon the previous section. A full
<link linkend="cas-sample">CAS sample application</link> can be found in the Spring
Security Samples.</para>
<section xml:id="cas-st">
<info>
<title>Service Ticket Authentication</title>
</info>
<para>This section describes how to setup Spring Security to authenticate Service Tickets. Often times
this is all a web application requires. You will need to add a <classname>ServiceProperties</classname>
bean to your application context. This represents your CAS service:</para>
<para> <programlisting language="xml"><![CDATA[
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
<property name="service"
value="https://localhost:8443/cas-sample/j_spring_cas_security_check"/>
<property name="sendRenew" value="false"/>
</bean>]]>
</programlisting> </para>
<para>The <literal>service</literal> must equal a URL that will be monitored by the
<literal>CasAuthenticationFilter</literal>. The <literal>sendRenew</literal> defaults to
false, but should be set to true if your application is particularly sensitive. What
this parameter does is tell the CAS login service that a single sign on login is
unacceptable. Instead, the user will need to re-enter their username and password in
order to gain access to the service.</para>
<para>The following beans should be configured to commence the CAS authentication process
(assuming you're using a namespace configuration):</para>
<para> <programlisting language="xml"><![CDATA[
<security:http entry-point-ref="casEntryPoint">
...
<security:custom-filter position="CAS_FILTER" ref="casFilter" />
</security:http>
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
<bean id="casEntryPoint"
class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
<property name="loginUrl" value="https://localhost:9443/cas/login"/>
<property name="serviceProperties" ref="serviceProperties"/>
</bean>
]]>
</programlisting> </para>
<para>For CAS to operate, the <classname>ExceptionTranslationFilter</classname> must have
its <literal>authenticationEntryPoint</literal> property set to the
<classname>CasAuthenticationEntryPoint</classname> bean. This can easily be done using
<link linkend="ns-entry-point-ref"><literal>entry-point-ref</literal></link> as is
done in the example above. The <classname>CasAuthenticationEntryPoint</classname> must refer to the
<classname>ServiceProperties</classname> bean (discussed above), which provides the URL
to the enterprise's CAS login server. This is where the user's browser will be
redirected.</para>
<para>The <classname>CasAuthenticationFilter</classname> has very similar properties to the
<classname>UsernamePasswordAuthenticationFilter</classname> (used for form-based
logins). You can use these properties to customize things like behavior for authentication
success and failure.</para>
<para>Next you need to add a <classname>CasAuthenticationProvider</classname> and its
collaborators: <programlisting language="xml"><![CDATA[
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="casAuthenticationProvider" />
</security:authentication-manager>
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
<property name="authenticationUserDetailsService">
<bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<constructor-arg ref="userService" />
</bean>
</property>
<property name="serviceProperties" ref="serviceProperties" />
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
<constructor-arg index="0" value="https://localhost:9443/cas" />
</bean>
</property>
<property name="key" value="an_id_for_this_auth_provider_only"/>
</bean>
<security:user-service id="userService">
<security:user name="joe" password="joe" authorities="ROLE_USER" />
...
</security:user-service>]]>
</programlisting> The <classname>CasAuthenticationProvider</classname> uses a
<interfacename>UserDetailsService</interfacename> instance to load the authorities for a
user, once they have been authenticated by CAS. We've shown a simple in-memory setup
here. Note that the <classname>CasAuthenticationProvider</classname> does not actually use
the password for authentication, but it does use the authorities.</para>
<para>The beans are all reasonably self-explanatory if you refer back to the
<link linkend="cas-how-it-works">How CAS Works</link> section.</para>
<para>This completes the most basic configuration for CAS. If you haven't made any
mistakes, your web application should happily work within the
framework of CAS single sign on. No other parts of Spring Security
need to be concerned about the fact CAS handled authentication. In the following sections
we will discuss some (optional) more advanced configurations.</para>
</section>
<section xml:id="cas-singlelogout">
<info>
<title>Single Logout</title>
</info>
<para>The CAS protocol supports Single Logout and can be easily added to your Spring
Security configuration. Below are updates to the Spring Security configuration
that handle Single Logout <programlisting language="xml"><![CDATA[
<security:http entry-point-ref="casEntryPoint">
...
<security:logout logout-success-url="/cas-logout.jsp"/>
<security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
<security:custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
</security:http>
<!-- This filter handles a Single Logout Request from the CAS Server -->
<bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
<!-- This filter redirects to the CAS Server to signal Single Logout should be performed -->
<bean id="requestSingleLogoutFilter"
class="org.springframework.security.web.authentication.logout.LogoutFilter">
<constructor-arg value="https://localhost:9443/cas/logout"/>
<constructor-arg>
<bean class=
"org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>
</constructor-arg>
<property name="filterProcessesUrl" value="/j_spring_cas_security_logout"/>
</bean>
]]></programlisting> The <literal>logout</literal> element logs the user out of the local application, but
does not terminate the session with the CAS server or any other applications that have been logged
into. The <literal>requestSingleLogoutFilter</literal> filter will allow the url of
<literal>/spring_security_cas_logout</literal> to be requested to redirect the application to the
configured CAS Server logout url. Then the CAS Server will send a Single Logout request to all the
services that were signed into. The <literal>singleLogoutFilter</literal> handles the Single Logout
request by looking up the <literal>HttpSession</literal> in a static <interfacename>Map</interfacename>
and then invalidating it.</para>
<para>It might be confusing why both the <literal>logout</literal> element and the
<literal>singleLogoutFilter</literal> are needed. It is considered best practice to logout locally
first since the <literal>SingleSignOutFilter</literal> just stores the
<interfacename>HttpSession</interfacename> in a static <interfacename>Map</interfacename> in order to
call invalidate on it. With the configuration above, the flow of logout would be:
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>The user requests <literal>/j_spring_security_logout</literal> which would log the user
out of the local application and send the user to the logout success page.</listitem>
<listitem>The logout success page, <literal>/cas-logout.jsp</literal>, should instruct the user
to click a link pointing to <literal>/j_spring_cas_security_logout</literal> in order to logout
out of all applications.</listitem>
<listitem>When the user clicks the link, the user is redirected to the CAS single logout URL
(<literal>https://localhost:9443/cas/logout</literal>).</listitem>
<listitem>On the CAS Server side, the CAS single logout URL then submits single logout requests to
all the CAS Services. On the CAS Service side, JASIG's
<classname>SingleSignOutFilter</classname> processes the logout request by invaliditing the
original session.</listitem>
</orderedlist>
</para>
<para>The next step is to add the following to your web.xml
<programlisting language="xml"><![CDATA[
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>]]></programlisting></para>
<para>When using the SingleSignOutFilter you might encounter some encoding issues. Therefore it is
recommended to add the <classname>CharacterEncodingFilter</classname> to ensure that the character
encoding is correct when using the <classname>SingleSignOutFilter</classname>. Again, refer to JASIG's
documentation for details. The <classname>SingleSignOutHttpSessionListener</classname> ensures that
when an <interfacename>HttpSession</interfacename> expires, the mapping used for single logout is
removed.</para>
</section>
<section xml:id="cas-pt-client">
<info>
<title>Authenticating to a Stateless Service with CAS</title>
</info>
<para>This section describes how to authenticate to a service using CAS. In other words,
this section discusses how to setup a client that uses a service that authenticates with
CAS. The next section describes how to setup a stateless service to Authenticate
using CAS.</para>
<section xml:id="cas-pt-client-config">
<info>
<title>Configuring CAS to Obtain Proxy Granting Tickets</title>
</info>
<para>In order to authenticate to a stateless service, the application needs to obtain a proxy granting ticket
(PGT). This section describes how to configure Spring Security to obtain a PGT building upon then
<link xlink:href="cas-st">Service Ticket Authentication</link> configuration.</para>
<para>The first step is to include a <classname>ProxyGrantingTicketStorage</classname> in your Spring Security
configuration. This is used to store PGT's that are obtained by the
<classname>CasAuthenticationFilter</classname> so that they can be used to obtain proxy tickets. An example
configuration is shown below <programlisting language="xml"><![CDATA[
<!--
NOTE: In a real application you should not use an in memory implementation. You will also want
to ensure to clean up expired tickets by calling ProxyGrantingTicketStorage.cleanup()
-->
<bean id="pgtStorage" class="org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl"/>
]]></programlisting></para>
<para>The next step is to update the <classname>CasAuthenticationProvider</classname> to be able to obtain proxy
tickets. To do this replace the <classname>Cas20ServiceTicketValidator</classname> with a
<classname>Cas20ProxyTicketValidator</classname>. The <literal>proxyCallbackUrl</literal> should be set to
a URL that the application will receive PGT's at. Last, the configuration should also reference the
<classname>ProxyGrantingTicketStorage</classname> so it can use a PGT to obtain proxy tickets.
You can find an example of the configuration changes that should be made below.
<programlisting language="xml"><![CDATA[
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
...
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator">
<constructor-arg value="https://localhost:9443/cas"/>
<property name="proxyCallbackUrl"
value="https://localhost:8443/cas-sample/j_spring_cas_security_proxyreceptor"/>
<property name="proxyGrantingTicketStorage" ref="pgtStorage"/>
</bean>
</property>
</bean>
]]></programlisting></para>
<para>The last step is to update the <classname>CasAuthenticationFilter</classname> to accept PGT and to store them
in the <classname>ProxyGrantingTicketStorage</classname>. It is important the the <literal>proxyReceptorUrl</literal>
matches the <literal>proxyCallbackUrl</literal> of the <classname>Cas20ProxyTicketValidator</classname>. An example
configuration is shown below.
<programlisting language="xml"><![CDATA[
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
...
<property name="proxyGrantingTicketStorage" ref="pgtStorage"/>
<property name="proxyReceptorUrl" value="/j_spring_cas_security_proxyreceptor"/>
</bean>
]]></programlisting></para>
</section>
<section xml:id="cas-pt-client-sample">
<info>
<title>Calling a Stateless Service Using a Proxy Ticket</title>
</info>
<para>Now that Spring Security obtains PGTs, you can use them to create proxy tickets which can be used to authenticate
to a stateless service. The <link linkend="cas-sample">CAS sample application</link> contains a working example in
the <classname>ProxyTicketSampleServlet</classname>. Example code can be found below:
<programlisting language="xml"><![CDATA[
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// NOTE: The CasAuthenticationToken can also be obtained using
// SecurityContextHolder.getContext().getAuthentication()
final CasAuthenticationToken token = (CasAuthenticationToken) request.getUserPrincipal();
// proxyTicket could be reused to make calls to the CAS service even if the
// target url differs
final String proxyTicket = token.getAssertion().getPrincipal().getProxyTicketFor(targetUrl);
// Make a remote call using the proxy ticket
final String serviceUrl = targetUrl+"?ticket="+URLEncoder.encode(proxyTicket, "UTF-8");
String proxyResponse = CommonUtils.getResponseFromServer(serviceUrl, "UTF-8");
...
}
]]></programlisting></para>
</section>
</section>
<section xml:id="cas-pt">
<info>
<title>Proxy Ticket Authentication</title>
</info>
<para>The <classname>CasAuthenticationProvider</classname> distinguishes
between stateful and stateless clients. A stateful client is
considered any that submits to the <literal>filterProcessUrl</literal> of the
<classname>CasAuthenticationFilter</classname>. A stateless client is any that
presents an authentication request to <classname>CasAuthenticationFilter</classname>
on a URL other than the <literal>filterProcessUrl</literal>.</para>
<para>Because remoting protocols have no way of presenting themselves
within the context of an <classname>HttpSession</classname>, it isn't
possible to rely on the default practice of storing the security context in the
session between requests. Furthermore, because the CAS server invalidates a
ticket after it has been validated by the <literal>TicketValidator</literal>,
presenting the same proxy ticket on subsequent requests will not
work.</para>
<para>One obvious option is to not use CAS at all for remoting
protocol clients. However, this would eliminate many of the desirable
features of CAS. As a middle-ground, the
<literal>CasAuthenticationProvider</literal> uses a
<literal>StatelessTicketCache</literal>. This is used solely for stateless clients
which use a principal equal to
<literal>CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER</literal>. What
happens is the <literal>CasAuthenticationProvider</literal> will store
the resulting <literal>CasAuthenticationToken</literal> in the
<literal>StatelessTicketCache</literal>, keyed on the proxy ticket.
Accordingly, remoting protocol clients can present the same proxy
ticket and the <literal>CasAuthenticationProvider</literal> will not
need to contact the CAS server for validation (aside from the first
request). Once authenticated, the proxy ticket could be used for URLs other than the
original target service.</para>
<para>This section builds upon the previous sections to accomodate proxy ticket authentication.
The first step is to specify to authenticate all artifacts as shown below.
<programlisting language="xml"><![CDATA[
<bean id="serviceProperties"
class="org.springframework.security.cas.ServiceProperties">
...
<property name="authenticateAllArtifacts" value="true"/>
</bean>
]]></programlisting></para>
<para>The next step is to specify <literal>serviceProperties</literal> and the
<literal>authenticationDetailsSource</literal> for the <classname>CasAuthenticationFilter</classname>.
The <literal>serviceProperties</literal> property instructs the
<classname>CasAuthenticationFilter</classname> to attempt to authenticate all artifacts instead of only
ones present on the <literal>filterProcessUrl</literal>. The
<classname>ServiceAuthenticationDetailsSource</classname> creates a
<interfacename>ServiceAuthenticationDetails</interfacename> that ensures the current URL, based
upon the <literal>HttpServletRequest</literal>, is used as the service URL when validating the ticket.
The method for generating the service URL can be customized by injecting a custom
<literal>AuthenticationDetailsSource</literal> that returns a custom
<interfacename>ServiceAuthenticationDetails</interfacename>.<programlisting language="xml"><![CDATA[
<bean id="casFilter"
class="org.springframework.security.cas.web.CasAuthenticationFilter">
...
<property name="serviceProperties" ref="serviceProperties"/>
<property name="authenticationDetailsSource">
<bean class=
"org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource"/>
</property>
</bean>
]]></programlisting></para>
<para>You will also need to update the <classname>CasAuthenticationProvider</classname> to handle proxy tickets.
To do this replace the <classname>Cas20ServiceTicketValidator</classname> with a
<classname>Cas20ProxyTicketValidator</classname>. You will need to configure the
<literal>statelessTicketCache</literal> and which proxies you want to accept. You can find an example of the updates
required to accept all proxies below.
<programlisting language="xml"><![CDATA[
<bean id="casAuthenticationProvider"
class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
...
<property name="ticketValidator">
<bean class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator">
<constructor-arg value="https://localhost:9443/cas"/>
<property name="acceptAnyProxy" value="true"/>
</bean>
</property>
<property name="statelessTicketCache">
<bean class="org.springframework.security.cas.authentication.EhCacheBasedTicketCache">
<property name="cache">
<bean class="net.sf.ehcache.Cache"
init-method="initialise" destroy-method="dispose">
<constructor-arg value="casTickets"/>
<constructor-arg value="50"/>
<constructor-arg value="true"/>
<constructor-arg value="false"/>
<constructor-arg value="3600"/>
<constructor-arg value="900"/>
</bean>
</property>
</bean>
</property>
</bean>
]]></programlisting></para>
</section>
</section>
</chapter>

View File

@ -1,140 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="channel-security"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Channel Security</title>
</info>
<section xml:id="channel-security-overview">
<info>
<title>Overview</title>
</info>
<para>In addition to coordinating the authentication and authorization requirements of your
application, Spring Security is also able to ensure unauthenticated web requests have
certain properties. These properties may include being of a particular transport type,
having a particular <literal>HttpSession</literal> attribute set and so on. The most
common requirement is for your web requests to be received using a particular transport
protocol, such as HTTPS.</para>
<para>An important issue in considering transport security is that of session hijacking.
Your web container manages a <literal>HttpSession</literal> by reference to a
<literal>jsessionid</literal> that is sent to user agents either via a cookie or URL
rewriting. If the <literal>jsessionid</literal> is ever sent over HTTP, there is a
possibility that session identifier can be intercepted and used to impersonate the user
after they complete the authentication process. This is because most web containers
maintain the same session identifier for a given user, even after they switch from HTTP
to HTTPS pages.</para>
<para>If session hijacking is considered too significant a risk for your particular
application, the only option is to use HTTPS for every request. This means the
<literal>jsessionid</literal> is never sent across an insecure channel. You will need to
ensure your <literal>web.xml</literal>-defined <literal>&lt;welcome-file&gt;</literal>
points to an HTTPS location, and the application never directs the user to an HTTP
location. Spring Security provides a solution to assist with the latter.</para>
</section>
<section xml:id="channel-security-config">
<info>
<title>Configuration</title>
</info>
<para>Channel security is supported by the <link linkend="ns-requires-channel">security
namespace</link> by means of the <literal>requires-channel</literal> attribute on the
<literal>&lt;intercept-url&gt;</literal> element and this is the simplest (and
recommended approach).</para>
<para>To configure channel security explicitly, you would define the following the filter in
your application context: <programlisting language="xml"><![CDATA[
<bean id="channelProcessingFilter"
class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager"/>
<property name="securityMetadataSource">
<security:filter-security-metadata-source path-type="regex">
<security:intercept-url pattern="\A/secure/.*\Z"
access="REQUIRES_SECURE_CHANNEL"/>
<security:intercept-url pattern="\A/acegilogin.jsp.*\Z"
access="REQUIRES_SECURE_CHANNEL"/>
<security:intercept-url pattern="\A/j_spring_security_check.*\Z"
access="REQUIRES_SECURE_CHANNEL"/>
<security:intercept-url pattern="\A/.*\Z" access="ANY_CHANNEL"/>
</security:filter-security-metadata-source>
</property>
</bean>
<bean id="channelDecisionManager"
class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref bean="secureChannelProcessor"/>
<ref bean="insecureChannelProcessor"/>
</list>
</property>
</bean>
<bean id="secureChannelProcessor"
class="org.springframework.security.web.access.channel.SecureChannelProcessor"/>
<bean id="insecureChannelProcessor"
class="org.springframework.security.web.access.channel.InsecureChannelProcessor"/>]]>
</programlisting>
Like <classname>FilterSecurityInterceptor</classname>, Apache Ant style paths are also
supported by the <literal>ChannelProcessingFilter</literal>.</para>
<para>The <literal>ChannelProcessingFilter</literal> operates by filtering all web requests
and determining the configuration attributes that apply. It then delegates to the
<literal>ChannelDecisionManager</literal>. The default implementation,
<literal>ChannelDecisionManagerImpl</literal>, should suffice in most cases. It simply
delegates to the list of configured <literal>ChannelProcessor</literal> instances. The
attribute <literal>ANY_CHANNEL</literal> can be used to override this behaviour and skip
a particular URL. Otherwise, a <literal>ChannelProcessor</literal> will review the
request, and if it is unhappy with the request (e.g. if it was received across the
incorrect transport protocol), it will perform a redirect, throw an exception or take
whatever other action is appropriate.</para>
<para>Included with Spring Security are two concrete <literal>ChannelProcessor</literal>
implementations: <literal>SecureChannelProcessor</literal> ensures requests with a
configuration attribute of <literal>REQUIRES_SECURE_CHANNEL</literal> are received over
HTTPS, whilst <literal>InsecureChannelProcessor</literal> ensures requests with a
configuration attribute of <literal>REQUIRES_INSECURE_CHANNEL</literal> are received
over HTTP. Both implementations delegate to a <literal>ChannelEntryPoint</literal> if
the required transport protocol is not used. The two
<literal>ChannelEntryPoint</literal> implementations included with Spring Security
simply redirect the request to HTTP and HTTPS as appropriate. Appropriate defaults are
assigned to the <literal>ChannelProcessor</literal> implementations for the
configuration attribute keywords they respond to and the
<interfacename>ChannelEntryPoint</interfacename> they delegate to, although you have the
ability to override these using the application context.</para>
<para>Note that the redirections are absolute (eg
<literal>http://www.company.com:8080/app/page</literal>), not relative (eg
<literal>/app/page</literal>). During testing it was discovered that Internet Explorer 6
Service Pack 1 has a bug whereby it does not respond correctly to a redirection
instruction which also changes the port to use. Accordingly, absolute URLs are used in
conjunction with bug detection logic in the <classname>PortResolverImpl</classname> that
is wired up by default to many Spring Security beans. Please refer to the JavaDocs for
<classname>PortResolverImpl</classname> for further details.</para>
<para>You should note that using a secure channel is recommended if usernames and passwords
are to be kept secure during the login process. If you do decide to use
<classname>ChannelProcessingFilter</classname> with form-based login, please ensure that
your login page is set to <literal>REQUIRES_SECURE_CHANNEL</literal>, and that the
<literal>LoginUrlAuthenticationEntryPoint.forceHttps</literal> property is
<literal>true</literal>.</para>
</section>
<section xml:id="channel-security-conclusion">
<info>
<title>Conclusion</title>
</info>
<para>Once configured, using the channel security filter is very easy. Simply request pages
without regard to the protocol (ie HTTP or HTTPS) or port (eg 80, 8080, 443, 8443 etc).
Obviously you'll still need a way of making the initial request (probably via the
<literal>web.xml</literal> <literal>&lt;welcome-file&gt;</literal> or a well-known home
page URL), but once this is done the filter will perform redirects as defined by your
application context.</para>
<para>You can also add your own <literal>ChannelProcessor</literal> implementations to the
<literal>ChannelDecisionManagerImpl</literal>. For example, you might set a
<literal>HttpSession</literal> attribute when a human user is detected via a "enter the
contents of this graphic" procedure. Your <literal>ChannelProcessor</literal> would
respond to say <literal>REQUIRES_HUMAN_USER</literal> configuration attributes and
redirect to an appropriate entry point to start the human user validation process if the
<literal>HttpSession</literal> attribute is not currently set.</para>
<para>To decide whether a security check belongs in a <literal>ChannelProcessor</literal> or
an <interfacename>AccessDecisionVoter</interfacename>, remember that the former is
designed to handle unauthenticated requests, whilst the latter is designed to handle
authenticated requests. The latter therefore has access to the granted authorities of
the authenticated principal. In addition, problems detected by a
<literal>ChannelProcessor</literal> will generally cause an HTTP/HTTPS redirection so
its requirements can be met, whilst problems detected by an
<interfacename>AccessDecisionVoter</interfacename> will ultimately result in an
<literal>AccessDeniedException</literal> (depending on the governing
<interfacename>AccessDecisionManager</interfacename>).</para>
</section>
</chapter>

View File

@ -1,43 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Run with xsltproc class-index-html.xsl classindex.xml > class-index.html -->
<xsl:variable name="src-xref-base">http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/</xsl:variable>
<xsl:variable name="ref-man-base">http://static.springsource.org/spring-security/site/docs/3.0.x/reference/</xsl:variable>
<xsl:template match="index">
<html>
<head>
<title>Spring Security Class and Interface Index</title>
</head>
<body>
<h2>Class and Interface Index</h2>
<p>An list of classes and interfaces used in Spring Security with links to the sections in the Spring Security manual which
refer to them.</p>
<div id="classindex">
<xsl:apply-templates />
</div>
</body>
</html>
</xsl:template>
<xsl:template match="class">
<div class="index-class">
<xsl:choose>
<xsl:when test="@src-xref">
<h4><xsl:element name="a"><xsl:attribute name="href"><xsl:value-of select="concat($src-xref-base, @src-xref)"/></xsl:attribute><xsl:value-of select="@name"/></xsl:element></h4>
</xsl:when>
<xsl:otherwise>
<h4><span class="classname"><xsl:value-of select="@name"/></span></h4>
</xsl:otherwise>
</xsl:choose>
<table>
<xsl:for-each select="link">
<tr><td><xsl:element name="a"><xsl:attribute name="href"><xsl:value-of select="concat($ref-man-base, @href)"/></xsl:attribute><xsl:value-of select="@title"/></xsl:element></td>
</tr>
</xsl:for-each>
</table>
</div>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,127 +0,0 @@
#! /usr/bin/perl
# Intended to generate an index of classnames to references in the manual (using the interfacename and classname elements).
#
# Builds an index of classnames to Javadoc (or src xref) links, from the allclasses-frame.html file.
# Processes the ref manual docbook files, building an index of classname to section ids where the class is referenced
#
#
use strict;
# Get list of links to class src packages from Javadoc
#system("curl http://static.springsource.org/spring-security/site/docs/3.0.x/apidocs/allclasses-frame.html > allclasses-frame.html");
# Manual front page gives us section numbers
#system("curl http://static.springsource.org/spring-security/site/docs/3.0.x/reference/springsecurity.html > springsecurity.html");
my $index_page = `cat springsecurity.html`;
my @all_classes = `cat allclasses-frame.html`;
$#all_classes > 0 || die "No lines in Javadoc";
# Src XREF format
#<a href="org/springframework/security/vote/AbstractAccessDecisionManager.html" target="classFrame">AbstractAccessDecisionManager</a>
# Javadoc format
#<A HREF="org/springframework/security/acls/afterinvocation/AbstractAclProvider.html" title="class in org.springframework.security.acls.afterinvocation" target="classFrame">AbstractAclProvider</A>
my %classnames_to_src;
print "Extracting classnames to links map from Javadoc...\n";
while ($_ = pop @all_classes) {
chomp;
# Get rid of the italic tags round interface names
$_ =~ s/<I>//;
$_ =~ s/<\/I>//;
next unless $_ =~ /<A HREF="(.*)" title=.*>(([a-zA-Z0-9_]+?))<\/A>.*/;
# print "Adding class $1, $2\n";
$classnames_to_src{$2} = $1;
}
#my @docbook = glob("*.xml");
# The list of docbook files xincluded in the manual
my @docbook;
print "Building list of docbook source files...\n";
# Read the includes rather than using globbing to get the ordering right for the index.
open MAINDOC, "<springsecurity.xml";
while(<MAINDOC>) {
if (/href="(.*\.xml)"/) {
push @docbook, $1;
}
}
# Hash of xml:id (i.e. anchor) to filename.html#anchor
my %id_to_html;
# Build map of html pages links
print "Building map of section xml:ids to reference manual links...\n";
while (my $file = pop @docbook) {
open FILE, $file or die "$!";
# print "\nProcessing: $file\n\n";
my $file_id;
while(<FILE>) {
if (/.* xml:id="([a-z0-9-]+?)"/) {
$file_id = $1;
last;
}
}
$id_to_html{$file_id} = "$file_id.html";
while (<FILE>) {
next unless /.* xml:id="([a-z0-9-]+?)"/;
# print "$1\n";
$id_to_html{$1} = "$file_id.html#$1";
}
close FILE;
}
# Get the list of class/interface names and their section ids/titles
print "Obtaining class and interface references from manual...\n";
my @class_references = split /;/,`xsltproc --xinclude index-classes.xsl springsecurity.xml`;
# Get unique values
my %seen = ();
@class_references = grep { !$seen{$_}++} @class_references;
print "There are $#class_references references to classes and interfaces.\n";
my %id_to_title;
my %classnames_to_ids = ();
foreach my $class_id_title (@class_references) {
(my $class, my $id, my $title) = split /:/, $class_id_title;
$title =~ s/</&lt;/;
$title =~ s/>/&gt;/;
$id_to_title{$id} = $title;
push( @{$classnames_to_ids{$class}}, $id );
}
print "Writing index file...\n";
open INDEX, ">classindex.xml" || die "Couldn't open output file\n";
print INDEX "<index>\n";
foreach my $class (sort keys %classnames_to_ids) {
print INDEX "<class name='$class'";
if (exists $classnames_to_src{$class}) {
print INDEX " src-xref='$classnames_to_src{$class}'";
}
print INDEX ">\n";
foreach my $id (@{$classnames_to_ids{$class}}) {
my $href = $id_to_html{$id};
$index_page =~ /$href">([AB0-9\.]* )/;
my $section = $1 ? "$1" : "";
# print "$id $href $section\n";
my $title = $id_to_title{$id};
# print "$section$title\n";
print INDEX " <link href='$href' title='$section$title'/>\n";
}
print INDEX "</class>\n"
}
print INDEX "</index>\n";
close INDEX;
print "Generating HTML file...\n"; 
system("xsltproc class-index-html.xsl classindex.xml > class-index.html");

View File

@ -1,48 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="community"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Spring Security Community</title>
</info>
<section xml:id="jira">
<info>
<title>Issue Tracking</title>
</info>
<para>Spring Security uses JIRA to manage bug reports and enhancement requests. If you find
a bug, please log a report using JIRA. Do not log it on the support forum, mailing list
or by emailing the project's developers. Such approaches are ad-hoc and we prefer to
manage bugs using a more formal process.</para>
<para>If possible, in your issue report please provide a JUnit test that demonstrates any
incorrect behaviour. Or, better yet, provide a patch that corrects the issue. Similarly,
enhancements are welcome to be logged in the issue tracker, although we only accept
enhancement requests if you include corresponding unit tests. This is necessary to
ensure project test coverage is adequately maintained.</para>
<para>You can access the issue tracker at <link
xlink:href="http://jira.springsource.org/browse/SEC"
>http://jira.springsource.org/browse/SEC</link>. </para>
</section>
<section xml:id="becoming-involved">
<info>
<title>Becoming Involved</title>
</info>
<para>We welcome your involvement in the Spring Security project. There are many ways of
contributing, including reading the forum and responding to questions from other people,
writing new code, improving existing code, assisting with documentation, developing
samples or tutorials, or simply making suggestions.</para>
<!-- TODO: Not currently there on SSec 2.0
<para>Please read our project policies web page that is available on
Spring Security home page. This explains the path to become a
committer, and the administration approaches we use within the
project.</para>
-->
</section>
<section xml:id="further-info">
<info>
<title>Further Information</title>
</info>
<para>Questions and comments on Spring Security are welcome. You can use the Spring
Community Forum web site at <uri xlink:href="http://forum.springsource.org"
>http://forum.springsource.org</uri> to discuss Spring Security with other users of the
framework. Remember to use JIRA for bug reports, as explained above.</para>
</section>
</chapter>

View File

@ -1,339 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="core-web-filters"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Core Security Filters</title>
<para> There are some key filters which will always be used in a web application which uses
Spring Security, so we'll look at these and their supporting classes and interfaces first.
We won't cover every feature, so be sure to look at the Javadoc for them if you want to get
the complete picture.</para>
<section xml:id="filter-security-interceptor">
<title><classname>FilterSecurityInterceptor</classname></title>
<para>We've already seen <classname>FilterSecurityInterceptor</classname> briefly when
discussing <link linkend="tech-intro-access-control">access-control in
general</link>, and we've already used it with the namespace where the
<literal>&lt;intercept-url></literal> elements are combined to configure it internally.
Now we'll see how to explicitly configure it for use with a
<classname>FilterChainProxy</classname>, along with its companion filter
<classname>ExceptionTranslationFilter</classname>. A typical configuration example is
shown below: <programlisting language="xml"><![CDATA[
<bean id="filterSecurityInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="securityMetadataSource">
<security:filter-security-metadata-source>
<security:intercept-url pattern="/secure/super/**" access="ROLE_WE_DONT_HAVE"/>
<security:intercept-url pattern="/secure/**" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
</security:filter-security-metadata-source>
</property>
</bean>]]></programlisting></para>
<para><classname>FilterSecurityInterceptor</classname> is responsible for handling the
security of HTTP resources. It requires a reference to an
<interfacename>AuthenticationManager</interfacename> and an
<interfacename>AccessDecisionManager</interfacename>. It is also supplied with
configuration attributes that apply to different HTTP URL requests. Refer back to <link
linkend="tech-intro-config-attributes">the original discussion on these</link> in
the technical introduction.</para>
<para>The <classname>FilterSecurityInterceptor</classname> can be configured with
configuration attributes in two ways. The first, which is shown above, is using the
<literal>&lt;filter-security-metadata-source&gt;</literal> namespace element. This is
similar to the <literal>&lt;http&gt;</literal> element from the namespace chapter
but the <literal>&lt;intercept-url&gt;</literal>
child elements only use the <literal>pattern</literal> and <literal>access</literal>
attributes. Commas are used to delimit the different configuration attributes that apply
to each HTTP URL. The second option is to write your own
<interfacename>SecurityMetadataSource</interfacename>, but this is beyond the scope of
this document. Irrespective of the approach used, the
<interfacename>SecurityMetadataSource</interfacename> is responsible for returning a
<literal>List&lt;ConfigAttribute&gt;</literal> containing all of the configuration
attributes associated with a single secure HTTP URL.</para>
<para>It should be noted that the
<literal>FilterSecurityInterceptor.setSecurityMetadataSource()</literal> method actually
expects an instance of <interfacename>FilterInvocationSecurityMetadataSource</interfacename>. This
is a marker interface which subclasses
<interfacename>SecurityMetadataSource</interfacename>. It simply denotes the
<interfacename>SecurityMetadataSource</interfacename> understands
<classname>FilterInvocation</classname>s. In the interests of simplicity we'll continue
to refer to the <interfacename>FilterInvocationSecurityMetadataSource</interfacename> as
a <interfacename>SecurityMetadataSource</interfacename>, as the distinction is of little
relevance to most users.</para>
<para>The <interfacename>SecurityMetadataSource</interfacename> created by the namespace
syntax obtains the configuration attributes for a particular
<classname>FilterInvocation</classname> by matching the request URL against the
configured <literal>pattern</literal> attributes. This behaves in the same way as it
does for namespace configuration. The default is to treat all expressions as Apache Ant
paths and regular expressions are also supported for more complex cases. The
<literal>path-type</literal> attribute is used to specify the type of pattern being
used. It is not possible to mix expression syntaxes within the same definition. As an
example, the previous configuration using regular expressions instead of Ant paths would
be written as follows:</para>
<programlisting language="xml"><![CDATA[
<bean id="filterInvocationInterceptor"
class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="runAsManager" ref="runAsManager"/>
<property name="securityMetadataSource">
<security:filter-security-metadata-source path-type="regex">
<security:intercept-url pattern="\A/secure/super/.*\Z" access="ROLE_WE_DONT_HAVE"/>
<security:intercept-url pattern="\A/secure/.*\" access="ROLE_SUPERVISOR,ROLE_TELLER"/>
</security:filter-security-metadata-source>
</property>
</bean>]]> </programlisting>
<para>Patterns are always evaluated in the order they are defined. Thus it is important that
more specific patterns are defined higher in the list than less specific patterns. This
is reflected in our example above, where the more specific
<literal>/secure/super/</literal> pattern appears higher than the less specific
<literal>/secure/</literal> pattern. If they were reversed, the
<literal>/secure/</literal> pattern would always match and the
<literal>/secure/super/</literal> pattern would never be evaluated.</para>
<!--
TODO: Put in FAQ instead. Or drop.
<para>As with other security interceptors, the <literal>validateConfigAttributes</literal>
property is observed. When set to <literal>true</literal> (the default), at startup time
the <classname>FilterSecurityInterceptor</classname> will evaluate if the provided
configuration attributes are valid. It does this by checking each configuration
attribute can be processed by either the
<interfacename>AccessDecisionManager</interfacename> or the
<literal>RunAsManager</literal>. If neither of these can process a particular
configuration attribute, an exception is thrown.</para>
-->
</section>
<section xml:id="exception-translation-filter">
<title> <classname>ExceptionTranslationFilter</classname></title>
<para>The <classname>ExceptionTranslationFilter</classname> sits above the
<classname>FilterSecurityInterceptor</classname> in the security filter stack. It
doesn't do any actual security enforcement itself, but handles exceptions thrown by the
security interceptors and provides suitable and HTTP responses. <programlisting language="xml"><![CDATA[
<bean id="exceptionTranslationFilter"
class="org.springframework.security.web.access.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
<property name="accessDeniedHandler" ref="accessDeniedHandler"/>
</bean>
<bean id="authenticationEntryPoint"
class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<property name="loginFormUrl" value="/login.jsp"/>
</bean>
<bean id="accessDeniedHandler"
class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.htm"/>
</bean>
]]></programlisting></para>
<section xml:id="auth-entry-point">
<title><interfacename>AuthenticationEntryPoint</interfacename></title>
<para> The <interfacename>AuthenticationEntryPoint</interfacename> will be called if the
user requests a secure HTTP resource but they are not authenticated. An appropriate
<exceptionname>AuthenticationException</exceptionname> or
<exceptionname>AccessDeniedException</exceptionname> will be thrown by a security
interceptor further down the call stack, triggering the
<methodname>commence</methodname> method on the entry point. This does the job of
presenting the appropriate response to the user so that authentication can begin.
The one we've used here is <classname>LoginUrlAuthenticationEntryPoint</classname>,
which redirects the request to a different URL (typically a login page). The actual
implementation used will depend on the authentication mechanism you want to be used
in your application. </para>
</section>
<section xml:id="access-denied-handler">
<title><interfacename>AccessDeniedHandler</interfacename></title>
<para>What happens if a user is already authenticated and they try to access a protected
resource? In normal usage, this shouldn't happen because the application workflow
should be restricted to operations to which a user has access. For example, an HTML
link to an administration page might be hidden from users who do not have an admin
role. You can't rely on hiding links for security though, as there's always a
possibility that a user will just enter the URL directly in an attempt to bypass the
restrictions. Or they might modify a RESTful URL to change some of the argument
values. Your application must be protected against these scenarios or it will
definitely be insecure. You will typically use simple web layer security to apply
constraints to basic URLs and use more specific method-based security on your
service layer interfaces to really nail down what is permissible.</para>
<para>If an <exceptionname>AccessDeniedException</exceptionname> is thrown and a user
has already been authenticated, then this means that an operation has been attempted
for which they don't have enough permissions. In this case,
<classname>ExceptionTranslationFilter</classname> will invoke a second strategy, the
<interfacename>AccessDeniedHandler</interfacename>. By default, an
<classname>AccessDeniedHandlerImpl</classname> is used, which just sends a 403
(Forbidden) response to the client. Alternatively you can configure an instance
explicitly (as in the above example) and set an error page URL which it will
forwards the request to <footnote>
<para>We use a forward so that the SecurityContextHolder still contains details of
the principal, which may be useful for displaying to the user. In old releases
of Spring Security we relied upon the servlet container to handle a 403 error
message, which lacked this useful contextual information.</para>
</footnote>. This can be a simple <quote>access denied</quote> page, such as a JSP,
or it could be a more complex handler such as an MVC controller. And of course, you
can implement the interface yourself and use your own implementation. </para>
<para>It's also possible to supply a custom
<interfacename>AccessDeniedHandler</interfacename> when you're using the namespace
to configure your application. See <link linkend="nsa-access-denied-handler">the
namespace appendix</link> for more details.</para>
</section>
<section xml:id="request-caching">
<title><interfacename>SavedRequest</interfacename>s and the <interfacename>RequestCache</interfacename> Interface</title>
<para>Another of <classname>ExceptionTranslationFilter</classname>'s responsibilities is
to save the current request before invoking the <interfacename>AuthenticationEntryPoint</interfacename>.
This allows the request to be restored after the use has authenticated (see previous overview
of <link linkend="tech-intro-web-authentication">web authentication</link>).
A typical example would be where the user logs in with a form, and is then redirected to the
original URL by the default <classname>SavedRequestAwareAuthenticationSuccessHandler</classname>
(see <link linkend="form-login-flow-handling">below</link>).
</para>
<para>The <interfacename>RequestCache</interfacename> encapsulates the functionality required for storing
and retrieving <interfacename>HttpServletRequest</interfacename> instances. By default
the <classname>HttpSessionRequestCache</classname> is used, which stores the request
in the <interfacename>HttpSession</interfacename>. The <classname>RequestCacheFilter</classname>
has the job of actually restoring the saved request from the cache when the user is redirected to
the original URL.
</para>
<para>Under normal circumstances, you shouldn't need to modify any of this functionality, but the
saved-request handling is a <quote>best-effort</quote> approach and there may be situations which
the default configuration isn't able to handle. The use of these interfaces makes it fully pluggable
from Spring Security 3.0 onwards.
</para>
</section>
</section>
<section xml:id="security-context-persistence-filter">
<title><classname>SecurityContextPersistenceFilter</classname></title>
<para> We covered the purpose of this all-important filter in the <link
linkend="tech-intro-sec-context-persistence">Technical Overview</link> chapter so
you might want to re-read that section at this point. Let's first take a look at how you
would configure it for use with a <classname>FilterChainProxy</classname>. A basic
configuration only requires the bean itself <programlisting language="xml"><![CDATA[
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter"/>
]]></programlisting> As we saw previously, this filter has two main tasks. It is responsible for
storage of the <classname>SecurityContext</classname> contents between HTTP requests and
for clearing the <classname>SecurityContextHolder</classname> when a request is
completed. Clearing the <classname>ThreadLocal</classname> in which the context is
stored is essential, as it might otherwise be possible for a thread to be replaced into
the servlet container's thread pool, with the security context for a particular user
still attached. This thread might then be used at a later stage, performing operations
with the wrong credentials. </para>
<section xml:id="security-context-repository">
<title><interfacename>SecurityContextRepository</interfacename></title>
<para>From Spring Security 3.0, the job of loading and storing the security context is
now delegated to a separate strategy interface:
<programlisting language="java">
public interface SecurityContextRepository {
SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder);
void saveContext(SecurityContext context, HttpServletRequest request,
HttpServletResponse response);
}
</programlisting>
The <classname>HttpRequestResponseHolder</classname> is simply a container for the
incoming request and response objects, allowing the implementation to replace these
with wrapper classes. The returned contents will be passed to the filter chain. </para>
<para> The default implementation is
<classname>HttpSessionSecurityContextRepository</classname>, which stores the
security context as an <interfacename>HttpSession</interfacename> attribute <footnote>
<para>In Spring Security 2.0 and earlier, this filter was called
<classname>HttpSessionContextIntegrationFilter</classname> and performed all the
work of storing the context was performed by the filter itself. If you were
familiar with this class, then most of the configuration options which were
available can now be found on
<classname>HttpSessionSecurityContextRepository</classname>. </para>
</footnote>. The most important configuration parameter for this implementation is
the <literal>allowSessionCreation</literal> property, which defaults to
<literal>true</literal>, thus allowing the class to create a session if it needs one
to store the security context for an authenticated user (it won't create one unless
authentication has taken place and the contents of the security context have
changed). If you don't want a session to be created, then you can set this property
to <literal>false</literal>: <programlisting language="xml"><![CDATA[
<bean id="securityContextPersistenceFilter"
class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
<property name='securityContextRepository'>
<bean class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
<property name='allowSessionCreation' value='false' />
</bean>
</property>
</bean>
]]></programlisting> Alternatively you could provide an instance of
<classname>NullSecurityContextRepository</classname>, a <quote><link
xlink:href="http://en.wikipedia.org/wiki/Null_Object_pattern">null object</link></quote>
implementation, which will prevent the security context from being stored, even if a
session has already been created during the request. </para>
</section>
</section>
<section xml:id="form-login-filter">
<title><classname>UsernamePasswordAuthenticationFilter</classname></title>
<para>We've now seen the three main filters which are always present in a Spring Security
web configuration. These are also the three which are automatically created by the
namespace <literal>&lt;http&gt;</literal> element and cannot be substituted with
alternatives. The only thing that's missing now is an actual authentication mechanism,
something that will allow a user to authenticate. This filter is the most commonly used
authentication filter and the one that is most often customized <footnote>
<para>For historical reasons, prior to Spring Security 3.0, this filter was called
<classname>AuthenticationProcessingFilter</classname> and the entry point was called
<classname>AuthenticationProcessingFilterEntryPoint</classname>. Since the framework
now supports many different forms of authentication, they have both been given more
specific names in 3.0.</para>
</footnote>. It also provides the implementation used by the
<literal>&lt;form-login&gt;</literal> element from the namespace. There are three stages
required to configure it. <orderedlist>
<listitem>
<para>Configure a <classname>LoginUrlAuthenticationEntryPoint</classname> with the
URL of the login page, just as we did above, and set it on the
<classname>ExceptionTranslationFilter</classname>. </para>
</listitem>
<listitem>
<para>Implement the login page (using a JSP or MVC controller).</para>
</listitem>
<listitem>
<para>Configure an instance of
<classname>UsernamePasswordAuthenticationFilter</classname> in the application
context</para>
</listitem>
<listitem>
<para>Add the filter bean to your filter chain proxy (making sure you pay attention
to the order). <!-- TODO: link --></para>
</listitem>
</orderedlist> The login form simply contains <literal>j_username</literal> and
<literal>j_password</literal> input fields, and posts to the URL that is monitored by
the filter (by default this is <literal>/j_spring_security_check</literal>). The basic
filter configuration looks something like this: <programlisting language="xml"><![CDATA[
<bean id="authenticationFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
</bean> ]]>
</programlisting></para>
<section xml:id="form-login-flow-handling">
<title>Application Flow on Authentication Success and Failure</title>
<para> The filter calls the configured
<interfacename>AuthenticationManager</interfacename> to process each authentication
request. The destination following a successful authentication or an authentication
failure is controlled by the
<interfacename>AuthenticationSuccessHandler</interfacename> and
<interfacename>AuthenticationFailureHandler</interfacename> strategy interfaces,
respectively. The filter has properties which allow you to set these so you can
customize the behaviour completely <footnote>
<para>In versions prior to 3.0, the application flow at this point had evolved to a
stage was controlled by a mix of properties on this class and strategy plugins.
The decision was made for 3.0 to refactor the code to make these two strategies
entirely responsible. </para>
</footnote>. Some standard implementations are supplied such as
<classname>SimpleUrlAuthenticationSuccessHandler</classname>,
<classname>SavedRequestAwareAuthenticationSuccessHandler</classname>,
<classname>SimpleUrlAuthenticationFailureHandler</classname> and
<classname>ExceptionMappingAuthenticationFailureHandler</classname>. Have a look at
the Javadoc for these classes and also for <classname>AbstractAuthenticationProcessingFilter</classname>
to get an overview of how they work and the supported features.
</para>
<para>If authentication is successful, the resulting
<interfacename>Authentication</interfacename> object will be placed into the
<classname>SecurityContextHolder</classname>. The configured
<interfacename>AuthenticationSuccessHandler</interfacename> will then be called to
either redirect or forward the user to the appropriate destination. By default a
<classname>SavedRequestAwareAuthenticationSuccessHandler</classname> is used, which
means that the user will be redirected to the original destination they requested
before they were asked to login. <note>
<para> The <classname>ExceptionTranslationFilter</classname> caches the original
request a user makes. When the user authenticates, the request handler makes use
of this cached request to obtain the original URL and redirect to it. The
original request is then rebuilt and used as an alternative. </para>
</note> If authentication fails, the configured
<interfacename>AuthenticationFailureHandler</interfacename> will be invoked. </para>
</section>
</section>
</chapter>

View File

@ -1,318 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="core-services"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Core Services</title>
<para> Now that we have a high-level overview of the Spring Security architecture and its core
classes, let's take a closer look at one or two of the core interfaces and their
implementations, in particular the <interfacename>AuthenticationManager</interfacename>,
<interfacename>UserDetailsService</interfacename> and the
<interfacename>AccessDecisionManager</interfacename>. These crop up regularly throughout the
remainder of this document so it's important you know how they are configured and how they
operate. </para>
<section xml:id="core-services-authentication-manager">
<title>The <interfacename>AuthenticationManager</interfacename>,
<classname>ProviderManager</classname> and
<classname>AuthenticationProvider</classname>s</title>
<para>The <interfacename>AuthenticationManager</interfacename> is just an interface, so the
implementation can be anything we choose, but how does it work in practice? What if we
need to check multiple authentication databases or a combination of different
authentication services such as a database and an LDAP server?</para>
<para>The default implementation in Spring Security is called
<classname>ProviderManager</classname> and rather than handling the authentication
request itself, it delegates to a list of configured
<classname>AuthenticationProvider</classname>s, each of which is queried in turn to see
if it can perform the authentication. Each provider will either throw an exception or
return a fully populated <interfacename>Authentication</interfacename> object. Remember
our good friends, <interfacename>UserDetails</interfacename> and
<interfacename>UserDetailsService</interfacename>? If not, head back to the previous
chapter and refresh your memory. The most common approach to verifying an authentication
request is to load the corresponding <interfacename>UserDetails</interfacename> and
check the loaded password against the one that has been entered by the user. This is the
approach used by the <classname>DaoAuthenticationProvider</classname> (see below). The
loaded <interfacename>UserDetails</interfacename> object - and particularly the
<literal>GrantedAuthority</literal>s it contains - will be used when building the fully
populated <interfacename>Authentication</interfacename> object which is returned from a
successful authentication and stored in the <classname>SecurityContext</classname>. </para>
<para> If you are using the namespace, an instance of <classname>ProviderManager</classname>
is created and maintained internally, and you add providers to it by using the namespace
authentication provider elements (see <link linkend="ns-auth-manager">the namespace
chapter</link>). In this case, you should not declare a
<classname>ProviderManager</classname> bean in your application context. However, if you
are not using the namespace then you would declare it like so: <programlisting language="xml"><![CDATA[
<bean id="authenticationManager"
class="org.springframework.security.authentication.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider"/>
<ref local="anonymousAuthenticationProvider"/>
<ref local="ldapAuthenticationProvider"/>
</list>
</property>
</bean>]]></programlisting></para>
<para>In the above example we have three providers. They are tried in the order shown (which
is implied by the use of a <literal>List</literal>), with each provider able to attempt
authentication, or skip authentication by simply returning <literal>null</literal>. If
all implementations return null, the <literal>ProviderManager</literal> will throw a
<exceptionname>ProviderNotFoundException</exceptionname>. If you're interested in
learning more about chaining providers, please refer to the
<literal>ProviderManager</literal> JavaDocs.</para>
<para> Authentication mechanisms such as a web form-login processing filter are injected
with a reference to the <interfacename>ProviderManager</interfacename> and will call it
to handle their authentication requests. The providers you require will sometimes be
interchangeable with the authentication mechanisms, while at other times they will
depend on a specific authentication mechanism. For example,
<classname>DaoAuthenticationProvider</classname> and
<classname>LdapAuthenticationProvider</classname> are compatible with any mechanism
which submits a simple username/password authentication request and so will work with
form-based logins or HTTP Basic authentication. On the other hand, some authentication
mechanisms create an authentication request object which can only be interpreted by a
single type of <classname>AuthenticationProvider</classname>. An example of this would
be JA-SIG CAS, which uses the notion of a service ticket and so can therefore only be
authenticated by a <classname>CasAuthenticationProvider</classname>. You needn't be too
concerned about this, because if you forget to register a suitable provider, you'll
simply receive a <literal>ProviderNotFoundException</literal> when an attempt to
authenticate is made.</para>
<section xml:id="core-services-erasing-credentials">
<title>Erasing Credentials on Successful Authentication</title>
<para> By default (from Spring Security 3.1 onwards) the
<classname>ProviderManager</classname> will attempt to clear any sensitive
credentials information from the <interfacename>Authentication</interfacename>
object which is returned by a successful authentication request. This prevents
information like passwords being retained longer than necessary. </para>
<para> This may cause issues when you are using a cache of user objects, for example, to
improve performance in a stateless application. If the
<interfacename>Authentication</interfacename> contains a reference to an object in
the cache (such as a <interfacename>UserDetails</interfacename> instance) and this
has its credentials removed, then it will no longer be possible to authenticate
against the cached value. You need to take this into account if you are using a
cache. An obvious solution is to make a copy of the object first, either in the
cache implementation or in the <interfacename>AuthenticationProvider</interfacename>
which creates the returned <interfacename>Authentication</interfacename> object.
Alternatively, you can disable the
<literal>eraseCredentialsAfterAuthentication</literal> property on
<classname>ProviderManager</classname>. See the Javadoc for more information.
</para>
</section>
<section xml:id="core-services-dao-provider">
<title><literal>DaoAuthenticationProvider</literal></title>
<para>The simplest <interfacename>AuthenticationProvider</interfacename> implemented by
Spring Security is <literal>DaoAuthenticationProvider</literal>, which is also one
of the earliest supported by the framework. It leverages a
<interfacename>UserDetailsService</interfacename> (as a DAO) in order to lookup the
username, password and <interfacename>GrantedAuthority</interfacename>s. It
authenticates the user simply by comparing the password submitted in a
<classname>UsernamePasswordAuthenticationToken</classname> against the one loaded by
the <interfacename>UserDetailsService</interfacename>. Configuring the provider is
quite simple: <programlisting language="xml"><![CDATA[
<bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl"/>
<property name="passwordEncoder" ref="passwordEncoder"/>
</bean>]]></programlisting> The <interfacename>PasswordEncoder</interfacename> is optional. A
<interfacename>PasswordEncoder</interfacename> provides encoding and decoding of
passwords presented in the <interfacename>UserDetails</interfacename> object that is
returned from the configured <interfacename>UserDetailsService</interfacename>. This
will be discussed in more detail <link linkend="core-services-password-encoding"
>below</link>. </para>
</section>
</section>
<section>
<title><interfacename>UserDetailsService</interfacename> Implementations</title>
<para>As mentioned in the earlier in this reference guide, most authentication providers
take advantage of the <interfacename>UserDetails</interfacename> and
<interfacename>UserDetailsService</interfacename> interfaces. Recall that the contract
for <interfacename>UserDetailsService</interfacename> is a single method:</para>
<para>
<programlisting language="java">
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
</programlisting>
</para>
<para>The returned <interfacename>UserDetails</interfacename> is an interface that provides
getters that guarantee non-null provision of authentication information such as the
username, password, granted authorities and whether the user account is enabled or
disabled. Most authentication providers will use a
<interfacename>UserDetailsService</interfacename>, even if the username and password are
not actually used as part of the authentication decision. They may use the returned
<interfacename>UserDetails</interfacename> object just for its
<literal>GrantedAuthority</literal> information, because some other system (like LDAP or
X.509 or CAS etc) has undertaken the responsibility of actually validating the
credentials.</para>
<para>Given <interfacename>UserDetailsService</interfacename> is so simple to implement, it
should be easy for users to retrieve authentication information using a persistence
strategy of their choice. Having said that, Spring Security does include a couple of
useful base implementations, which we'll look at below.</para>
<section xml:id="core-services-in-memory-service">
<title>In-Memory Authentication</title>
<para>Is easy to use create a custom <interfacename>UserDetailsService</interfacename>
implementation that extracts information from a persistence engine of choice, but
many applications do not require such complexity. This is particularly true if
you're building a prototype application or just starting integrating Spring
Security, when you don't really want to spend time configuring databases or writing
<interfacename>UserDetailsService</interfacename> implementations. For this sort of
situation, a simple option is to use the <literal>user-service</literal> element
from the security <link linkend="ns-minimal">namespace</link>: <programlisting language="xml"><![CDATA[
<user-service id="userDetailsService">
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
]]>
</programlisting> This also supports the use of an external properties
file: <programlisting language="xml"><![CDATA[
<user-service id="userDetailsService" properties="users.properties"/>
]]></programlisting> The properties file should contain entries in the form
<programlisting language="txt">username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]</programlisting>
For example
<programlisting language="txt">
jimi=jimispassword,ROLE_USER,ROLE_ADMIN,enabled
bob=bobspassword,ROLE_USER,enabled</programlisting></para>
</section>
<section xml:id="core-services-jdbc-user-service">
<title><literal>JdbcDaoImpl</literal></title>
<para>Spring Security also includes a <interfacename>UserDetailsService</interfacename>
that can obtain authentication information from a JDBC data source. Internally
Spring JDBC is used, so it avoids the complexity of a fully-featured object
relational mapper (ORM) just to store user details. If your application does use an
ORM tool, you might prefer to write a custom
<interfacename>UserDetailsService</interfacename> to reuse the mapping files you've
probably already created. Returning to <literal>JdbcDaoImpl</literal>, an example
configuration is shown below:</para>
<para>
<programlisting language="xml"><![CDATA[
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
<property name="username" value="sa"/>
<property name="password" value=""/>
</bean>
<bean id="userDetailsService"
class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean> ]]> </programlisting>
</para>
<para>You can use different relational database management systems by modifying the
<literal>DriverManagerDataSource</literal> shown above. You can also use a global
data source obtained from JNDI, as with any other Spring configuration.</para>
<section>
<title>Authority Groups</title>
<para>By default, <classname>JdbcDaoImpl</classname> loads the authorities for a
single user with the assumption that the authorities are mapped directly to
users (see the <link linkend="appendix-schema">database schema
appendix</link>). An alternative approach is to partition the authorities into
groups and assign groups to the user. Some people prefer this approach as a
means of administering user rights. See the <classname>JdbcDaoImpl</classname>
Javadoc for more information on how to enable the use of group authorities. The
group schema is also included in the appendix.</para>
</section>
<!--
<para>If the default schema is unsuitable for your needs, <literal>JdbcDaoImpl</literal>
provides properties that allow customisation of the SQL statements. Please refer to the
JavaDocs for details, but note that the class is not intended for complex custom
subclasses. If you have a complex schema or would like a custom
<interfacename>UserDetails</interfacename> implementation returned, you'd be better off
writing your own <interfacename>UserDetailsService</interfacename>. The base
implementation provided with Spring Security is intended for typical situations, rather
than catering for all possible requirements.</para>
-->
</section>
</section>
<section xml:id="core-services-password-encoding">
<title>Password Encoding</title>
<para linkend="spring-security-crypto-passwordencoders">Spring Security's
<interfacename>PasswordEncoder</interfacename> interface is used to support the use of
passwords which are encoded in some way in persistent storage. You should never store
passwords in plain text. Always use a one-way password hashing algorithm such as bcrypt
which uses a built-in salt value which is different for each stored password. Do not use
a plain hash function such as MD5 or SHA, or even a salted version. Bcrypt is deliberately
designed to be slow and to hinder offline password cracking, whereas standard hash algorithms
are fast and can easily be used to test thousands of passwords in parallel on custom
hardware. You might think this doesn't apply to you since your password database is
secure and offline attacks aren't a risk. If so, do some research and read up on all
the high-profile sites which have been compromised in this way and have been pilloried
for storing their passwords insecurely. It's best to be on the safe side. Using
<code>org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"</code>
is a good choice for security. There are also compatible implementations in other common
programming languages so it a good choice for interoperability too.</para>
<para>
If you are using a legacy system which already has hashed passwords, then you will
need to use an encoder which matches your current algorithm, at least until you can
migrate your users to a more secure scheme (usually this will involve asking the user
to set a new password, since hashes are irreversible). Spring Security has a package
containing legacy password encoding implementation, namely,
<literal>org.springframework.security.authentication.encoding</literal>.
The <classname>DaoAuthenticationProvider</classname> can be injected
with either the new or legacy <interfacename>PasswordEncoder</interfacename>
types.</para>
<section>
<title>What is a hash?</title>
<para>Password hashing is not unique to Spring Security but is a common source of
confusion for users who are not familiar with the concept. A hash (or digest)
algorithm is a one-way function which produces a piece of fixed-length output data
(the hash) from some input data, such as a password. As an example, the MD5 hash of
the string <quote>password</quote> (in hexadecimal) is
<programlisting language="txt">
5f4dcc3b5aa765d61d8327deb882cf99
</programlisting>
A hash is <quote>one-way</quote> in the sense that it is very difficult (effectively
impossible) to obtain the original input given the hash value, or indeed any
possible input which would produce that hash value. This property makes hash values
very useful for authentication purposes. They can be stored in your user database as
an alternative to plaintext passwords and even if the values are compromised they do
not immediately reveal a password which can be used to login. Note that this also
means you have no way of recovering the password once it is encoded.</para>
</section>
<section>
<title>Adding Salt to a Hash</title>
<para> One potential problem with the use of password hashes that it is relatively easy
to get round the one-way property of the hash if a common word is used for the
input. People tend to choose similar passwords and huge dictionaries of these from
previously hacked sites are available online. For example, if you search for the hash value
<literal>5f4dcc3b5aa765d61d8327deb882cf99</literal> using google, you will quickly
find the original word <quote>password</quote>. In a similar way, an attacker can
build a dictionary of hashes from a standard word list and use this to lookup the
original password. One way to help prevent this is to have a suitably strong
password policy to try to prevent common words from being used. Another is to use a
<quote>salt</quote> when calculating the hashes. This is an additional string of
known data for each user which is combined with the password before calculating the
hash. Ideally the data should be as random as possible, but in practice any salt
value is usually preferable to none. Using a salt means that an attacker has to
build a separate dictionary of hashes for each salt value, making the attack more
complicated (but not impossible).</para>
<para>Bcrypt automatically generates a random salt value for each password when it
is encoded, and stores it in the bcrypt string in a standard format.
<note><para>The legacy approach to handling salt was to inject a
<interfacename>SaltSource</interfacename> into the
<classname>DaoAuthenticationProvider</classname>, which would obtain a salt
value for a particular user and pass it to the
<interfacename>PasswordEncoder</interfacename>. Using bcrypt means you don't have
worry about the details of salt handling (such as where the the value is stored),
as it is all done internally. So we'd strongly recommend you use bcrypt
unless you already have a system in place which stores the salt separately.</para>
</note></para>
</section>
<section>
<title> Hashing and Authentication</title>
<para>When an authentication provider (such as Spring Security's
<classname>DaoAuthenticationProvider</classname>) needs to check the password in a
submitted authentication request against the known value for a user, and the stored
password is encoded in some way, then the submitted value must be encoded using
exactly the same algorithm. It's up to you to check that these are compatible as
Spring Security has no control over the persistent values. If you add password
hashing to your authentication configuration in Spring Security, and your database
contains plaintext passwords, then there is no way authentication can succeed. Even
if you are aware that your database is using MD5 to encode the passwords, for
example, and your application is configured to use Spring Security's
<classname>Md5PasswordEncoder</classname>, there are still things that can go wrong.
The database may have the passwords encoded in Base 64, for example while the
encoder is using hexadecimal strings (the default). Alternatively your database may
be using upper-case while the output from the encoder is lower-case. Make sure you
write a test to check the output from your configured password encoder with a known
password and salt combination and check that it matches the database value before
going further and attempting to authenticate through your application. Using a standard
like bcrypt will avoid these issues.
</para>
<para>If you want to generate encoded passwords directly in Java for storage in your
user database, then you can use the <methodname>encode</methodname> method on the
<interfacename>PasswordEncoder</interfacename>.</para>
</section>
</section>
</chapter>

View File

@ -1,138 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="crypto" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Spring Security Crypto Module</title>
<section xml:id="spring-security-crypto-introduction">
<title>Introduction</title>
<para>
The Spring Security Crypto module provides support for symmetric encryption, key generation, and password encoding.
The code is distributed as part of the core module but has no dependencies on any other Spring Security (or Spring) code.
</para>
</section>
<section xml:id="spring-security-crypto-encryption">
<title>Encryptors</title>
<para>
The Encryptors class provides factory methods for constructing symmetric encryptors.
Using this class, you can create ByteEncryptors to encrypt data in raw byte[] form.
You can also construct TextEncryptors to encrypt text strings.
Encryptors are thread safe.
</para>
<section xml:id="spring-security-crypto-encryption-bytes">
<title>BytesEncryptor</title>
<para>
Use the Encryptors.standard factory method to construct a "standard" BytesEncryptor:
<programlisting language="java"><![CDATA[
Encryptors.standard("password", "salt");]]>
</programlisting>
The "standard" encryption method is 256-bit AES using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2).
This method requires Java 6.
The password used to generate the SecretKey should be kept in a secure place and not be shared.
The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised.
A 16-byte random initialization vector is also applied so each encrypted message is unique.
</para>
<para>
The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length.
Such a salt may be generated using a KeyGenerator:
<programlisting language="java"><![CDATA[
String salt = KeyGenerators.string().generateKey(); // generates a random 8-byte salt that is then hex-encoded]]>
</programlisting>
</para>
</section>
<section xml:id="spring-security-crypto-encryption-text">
<title>TextEncryptor</title>
<para>
Use the Encryptors.text factory method to construct a standard TextEncryptor:
<programlisting language="java"><![CDATA[
Encryptors.text("password", "salt");]]>
</programlisting>
A TextEncryptor uses a standard BytesEncryptor to encrypt text data.
Encrypted results are returned as hex-encoded strings for easy storage on the filesystem or in the database.
</para>
<para>
Use the Encryptors.queryableText factory method to construct a "queryable" TextEncryptor:
<programlisting language="java"><![CDATA[
Encryptors.queryableText("password", "salt");]]>
</programlisting>
The difference between a queryable TextEncryptor and a standard TextEncryptor has to do with initialization vector (iv) handling.
The iv used in a queryable TextEncryptor#encrypt operation is shared, or constant, and is not randomly generated.
This means the same text encrypted multiple times will always produce the same encryption result.
This is less secure, but necessary for encrypted data that needs to be queried against.
An example of queryable encrypted text would be an OAuth apiKey.
</para>
</section>
</section>
<section xml:id="spring-security-crypto-keygenerators">
<title>Key Generators</title>
<para>
The KeyGenerators class provides a number of convenience factory methods for constructing different types of key generators.
Using this class, you can create a BytesKeyGenerator to generate byte[] keys.
You can also construct a StringKeyGenerator to generate string keys.
KeyGenerators are thread safe.
</para>
<section>
<title>BytesKeyGenerator</title>
<para>
Use the KeyGenerators.secureRandom factory methods to generate a BytesKeyGenerator backed by a SecureRandom instance:
<programlisting language="java"><![CDATA[
KeyGenerator generator = KeyGenerators.secureRandom();
byte[] key = generator.generateKey();]]>
</programlisting>
</para>
<para>
The default key length is 8 bytes.
There is also a KeyGenerators.secureRandom variant that provides control over the key length:
<programlisting language="java"><![CDATA[
KeyGenerators.secureRandom(16);]]>
</programlisting>
</para>
<para>
Use the KeyGenerators.shared factory method to construct a BytesKeyGenerator that always returns the same key on every invocation:
<programlisting language="java"><![CDATA[
KeyGenerators.shared(16);]]>
</programlisting>
</para>
</section>
<section>
<title>StringKeyGenerator</title>
<para>
Use the KeyGenerators.string factory method to construct a 8-byte, SecureRandom KeyGenerator that hex-encodes each key as a String:
<programlisting language="java"><![CDATA[
KeyGenerators.string();]]>
</programlisting>
</para>
</section>
</section>
<section xml:id="spring-security-crypto-passwordencoders">
<title>Password Encoding</title>
<para>
The password package of the spring-security-crypto module provides support for encoding passwords.
<interfacename>PasswordEncoder</interfacename> is the central service interface and has the following signature:
<programlisting language="java"><![CDATA[
public interface PasswordEncoder {
String encode(String rawPassword);
boolean matches(String rawPassword, String encodedPassword);
}]]>
</programlisting>
The matches method returns true if the rawPassword, once encoded, equals the encodedPassword.
This method is designed to support password-based authentication schemes.
</para>
<para>
The <classname>BCryptPasswordEncoder</classname> implementation uses the widely supported "bcrypt" algorithm
to hash the passwords. Bcrypt uses a random 16 byte salt value and is a deliberately slow algorithm, in order to
hinder password crackers. The amount of work it does can be tuned using the "strength" parameter which takes values
from 4 to 31. The higher the value, the more work has to be done to calculate the hash. The default value is 10.
You can change this value in your deployed system without affecting existing passwords, as the value is also stored
in the encoded hash.
</para>
<programlisting language="java"><![CDATA[
// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));]]>
</programlisting>
</section>
</chapter>

View File

@ -1,331 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="csrf"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Cross Site Request Forgery (CSRF)</title>
</info>
<para>This section discusses Spring Security's <link xlink:href="http://en.wikipedia.org/wiki/Cross-site_request_forgery">
Cross Site Request Forgery (CSRF)</link> support.</para>
<section>
<title>CSRF Attacks</title>
<para>Before we discuss how Spring Security can protect applications from CSRF attacks, we will explain what a CSRF
attack is. Let's take a look at a concrete example to get a better understanding.</para>
<para>Assume that your bank's website provides a form that allows transferring money from the currently logged in user
to another bank account. For example, the HTTP request might look like:</para>
<programlisting><![CDATA[POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account=9876
]]></programlisting>
<para>Now pretend you authenticate to your bank's website and then, without logging out, visit an evil website. The evil
website contains an HTML page with the following form:</para>
<programlisting language="xml"><![CDATA[<form action="https://bank.example.com/transfer" method="post">
<input type="hidden"
name="amount"
value="100.00"/>
<input type="hidden"
name="routingNumber"
value="evilsRoutingNumber"/>
<input type="hidden"
name="account"
value="evilsAccountNumber"/>
<input type="submit"
value="Win Money!"/>
</form>]]></programlisting>
<para>You like to win money, so you click on the submit button. In the process, you have unintentionally transferred $100 to
a malicious user. This happens because, while the evil website cannot see your cookies, the cookies associated with your
bank are still sent along with the request.</para>
<para>Worst yet, this whole process could have been automated using JavaScript. This means you didn't even need to click on the
button. So how do we protect ourselves from such attacks?</para>
</section>
<section>
<title>Synchronizer Token Pattern</title>
<para>The issue is that the HTTP request from the bank's website and the request from the evil website are exactly the same. This
means there is no way to reject requests coming from the evil website and allow requests coming from the bank's website. To
protect against CSRF attacks we need to ensure there is something in the request that the evil site is unable to provide.</para>
<para>One solution is to use the
<link xlink:href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#General_Recommendation:_Synchronizer_Token_Pattern">Synchronizer
Token Pattern</link>. This solution is to ensure that each request requires, in addition to our session cookie, a randomly
generated token as an HTTP parameter. When a request is submitted, the server must look up the expected value for the parameter
and compare it against the actual value in the request. If the values do not match, the request should fail.</para>
<para>We can relax the expectations to only require the token for each HTTP request that updates state. This can be safely done
since the same origin policy ensures the evil site cannot read the response. Additionally, we do not want to include the random
token in HTTP GET as this can cause the tokens to be leaked.</para>
<para>Let's take a look at how our example would change. Assume the randomly generated token is present in an HTTP parameter named
_csrf. For example, the request to transfer money would look like this:</para>
<programlisting><![CDATA[POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid; Domain=bank.example.com; Secure; HttpOnly
Content-Type: application/x-www-form-urlencoded
amount=100.00&routingNumber=1234&account=9876&_csrf=<secure-random>
]]></programlisting>
<para>You will notice that we added the _csrf parameter with a random value. Now the evil website will not be able to guess the
correct value for the _csrf parameter (which must be explicitly provided on the evil website) and the transfer will fail when the
server compares the actual token to the expected token.</para>
</section>
<section>
<title>When to use CSRF protection</title>
<para>When you use CSRF protection? Our recommendation is to use CSRF protection for any request that could be processed by a browser by normal users. If you are only creating
a service that is used by non-browser clients, you will likely want to disable CSRF protection.</para>
<section>
<title>CSRF protection and JSON</title>
<para>A common question is, but do I need to protect JSON requests made by javascript? The short answer is, it depends. However, you must be very careful as there
are CSRF exploits that can impact JSON requests. For example, a malicious user can create a
<link xlink:href="http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html" >CSRF with JSON using the following form</link>:</para>
<programlisting language="xml"><![CDATA[<form action="https://bank.example.com/transfer" method="post" enctype="text/plain">
<input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
<input type="submit"
value="Win Money!"/>
</form>]]></programlisting>
<para>This will produce the following JSON structure</para>
<programlisting language="javascript"><![CDATA[{ "amount":100,
"routingNumber": "evilsRoutingNumber",
"account": "evilsAccountNumber",
"ignore_me": "=test"
}]]></programlisting>
<para>If an application were not validating the Content-Type, then it would be exposed to this exploit. Depending on the setup, a Spring MVC application that validates the
Content-Type could still be exploited by updating the URL suffix to end with ".json" as shown below:</para>
<programlisting language="xml"><![CDATA[<form action="https://bank.example.com/transfer.json" method="post" enctype="text/plain">
<input name='{"amount":100,"routingNumber":"evilsRoutingNumber","account":"evilsAccountNumber", "ignore_me":"' value='test"}' type='hidden'>
<input type="submit"
value="Win Money!"/>
</form>]]></programlisting>
</section>
<section>
<title>CSRF and Stateless Browser Applications</title>
<para>What if my application is stateless? That doesn't necessarily mean you are protected. In fact, if a user does not need to perform any actions in the web browser for a given
request, they are likely still vulnerable to CSRF attacks.</para>
<para>For example, consider an application uses a custom cookie that contains all the state within it for authentication instead of the JSESSIONID. When the CSRF attack is made
the custom cookie will be sent with the request in the same manner that the JSESSIONID cookie was sent in our previous example.</para>
<para>User's using basic authentication are also vulnerable to CSRF attacks since the browser will automatically include the username password in any requests in the same manner that
the JSESSIONID cookie was sent in our previous example.</para>
</section>
</section>
<section xml:id="csrf-using">
<title>Using Spring Security CSRF Protection</title>
<para>So what are the steps necessary to use Spring Security's to protect our site against CSRF attacks? The steps to using Spring
Security's CSRF protection are outlined below:</para>
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para><link linkend="csrf-use-proper-verbs">Use proper HTTP verbs</link></para>
</listitem>
<listitem>
<para><link linkend="csrf-configure">Configure CSRF Protection</link></para>
</listitem>
<listitem>
<para><link linkend="csrf-include-csrf-token">Include the CSRF Token</link></para>
</listitem>
</orderedlist>
<section xml:id="csrf-use-proper-verbs">
<title>Use proper HTTP verbs</title>
<para>The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. Specifically, before Spring
Security's CSRF support can be of use, you need to be certain that your application is using PATCH, POST, PUT, and/or DELETE for anything
that modifies state.</para>
<para>This is not a limitation of Spring Security's support, but instead a general requirement for proper CSRF prevention. The reason is that
including private information in an HTTP GET can cause the information to be leaked. See
<link xlink:href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3">RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's</link> for
general guidance on using POST instead of GET for sensitive information.</para>
</section>
<section xml:id="csrf-configure">
<title>Configure CSRF Protection</title>
<para>The next step is to include Spring Security's CSRF protection within your application. Some frameworks handle invalid CSRF tokens by invaliding the user's
session, but this causes <link linkend="csrf-logout">its own problems</link>. Instead by default Spring Security's CSRF protection will produce an HTTP 403 access denied.
This can be customized by configuring the <link linkend="access-denied-handler">AccessDeniedHandler</link> to process <classname>InvalidCsrfTokenException</classname>
differently.</para>
<para>For passivity reasons, if you are using the XML configuration, CSRF protection must be explicitly enabled using the <link linkend="nsa-csrf">&lt;csrf&gt;</link> element. Refer to the
<link linkend="nsa-csrf">&lt;csrf&gt;</link> element's documentation for additional customizations.</para>
<note>
<para><link xlink:href="https://jira.springsource.org/browse/SEC-2347">SEC-2347</link> is logged to ensure Spring
Security 4.x's XML namespace configuration will enable CSRF protection by default.</para>
</note>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<csrf />
</http>
]]></programlisting>
<para>CSRF protection is enabled by default with Java configuration. If you would like to disable CSRF, the corresponding Java configuration can be
seen below. Refer to the Javadoc of csrf() for additional customizations in how CSRF protection is configured.</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable();
}
}]]></programlisting>
</section>
<section xml:id="csrf-include-csrf-token">
<title>Include the CSRF Token</title>
<section xml:id="csrf-include-csrf-token-form">
<title>Form Submissions</title>
<para>The last step is to ensure that you include the CSRF token in all PATCH, POST, PUT, and DELETE methods. This can be done using
the _csrf request attribute to obtain the current CsrfToken. An example of doing this with a JSP is shown below:</para>
<programlisting language="xml"><![CDATA[<c:url var="logoutUrl" value="/logout"/>
<form action="${logoutUrl}"
method="post">
<input type="submit"
value="Log out" />
<input type="hidden"
name="${_csrf.parameterName}"
value="${_csrf.token}"/>
</form>]]></programlisting>
<note>
<para>If you are using Spring MVC &lt;form:form&gt; tag, the <interfacename>CsrfToken</interfacename> is automatically included for you using the CsrfRequestDataValueProcessor.</para>
</note>
</section>
<section xml:id="csrf-include-csrf-token-ajax">
<title>Ajax and JSON Requests</title>
<para>If you using JSON, then it is not possible to submit the CSRF token within an HTTP parameter. Instead you can submit the token within a HTTP header.
A typical pattern would be to include the CSRF token within your meta tags. An example with a JSP is shown below:</para>
<programlisting language="xml"><![CDATA[<html>
<head>
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<!-- ... -->]]></programlisting>
<para>You can then include the token within all your Ajax requests. If you were using jQuery, this could be done with the following:</para>
<programlisting language="javascript"><![CDATA[$(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});]]></programlisting>
<para>As a alternative to jQuery, we recommend using <ulink url="http://cujojs.com/">cujoJSs</ulink> rest.js. <ulink url="https://github.com/cujojs/rest">rest.js</ulink> provides
advanced support for working with HTTP request and responses in RESTful ways. A core capability is the ability to contextualize the HTTP client adding behavior as needed by
chaining interceptors on to the client.</para>
<programlisting language="javascript"><![CDATA[var client = rest.chain(csrf, {
token: $("meta[name='_csrf']").attr("content"),
name: $("meta[name='_csrf_header']").attr("content")
});]]></programlisting>
<para>The configured client can be shared with any component of the application that needs to make a request to the CSRF protected resource. One significant different between rest.js
and jQuery is that only requests made with the configured client will contain the CSRF token, vs jQuery where <emphasis>all</emphasis> requests will include the token. The ability
to scope which requests receive the token helps guard against leaking the CSRF token to a third party. Please refer to the
<ulink url="https://github.com/cujojs/rest/tree/master/docs">rest.js reference documentation</ulink> for more information on rest.js.</para>
</section>
</section>
</section>
<section xml:id="csrf-caveats">
<title>CSRF Caveats</title>
<para>There are a few caveats when implementing CSRF.</para>
<section xml:id="csrf-timeouts">
<title>Timeouts</title>
<para>One issue is that the expected CSRF token is stored in the HttpSession, so as soon as the HttpSession expires your configured
<interfacename>AccessDeniedHandler</interfacename> will receive a InvalidCsrfTokenException. If you are using the default
<interfacename>AccessDeniedHandler</interfacename>, the browser will get an HTTP 403 and display a poor error message.</para>
<note>
<para>One might ask why the expected <interfacename>CsrfToken</interfacename> isn't stored in a cookie. This is because there are known exploits in which headers
(i.e. specify the cookies) can be set by another domain. This is the same reason Ruby on Rails
<link xlink:href="http://weblog.rubyonrails.org/2011/2/8/csrf-protection-bypass-in-ruby-on-rails/">no longer skips CSRF checks when the header X-Requested-With
is present</link>. See <link xlink:href="http://lists.webappsec.org/pipermail/websecurity_lists.webappsec.org/2011-February/007533.html">this webappsec.org thread</link>
for details on how to perform the exploit. Another disadvantage is that by removing the state (i.e. the timeout) you lose the ability
to forcibly terminate the token if something got compromised.</para>
</note>
<para>A simple way to mitigate an active user experiencing a timeout is to have some JavaScript that lets the user know their session is about to expire.
The user can click a button to continue and refresh the session.</para>
<para>Alternatively, specifying a custom <interfacename>AccessDeniedHandler</interfacename> allows you to process the <classname>InvalidCsrfTokenException</classname>
anyway you like. For an example of how to customize the <interfacename>AccessDeniedHandler</interfacename> refer to the provided links for both
<link linkend="#nsa-access-denied-handler">xml</link> and
<link xlink:href="https://github.com/spring-projects/spring-security/blob/3.2.0.RC1/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpAccessDeniedHandlerTests.groovy#L64">Java
configuration</link>.</para>
</section>
<section xml:id="csrf-login">
<title>Logging In</title>
<para>In order to protect against forging log in requests the log in form should be protected against CSRF attacks too. Since the <interfacename>CsrfToken</interfacename> is stored in
HttpSession, this means an HttpSession will be created as soon as <interfacename>CsrfToken</interfacename> token attribute is accessed. While this sounds bad in
a RESTful / stateless architecture the reality is that state is necessary to implement practical security. Without state, we have nothing we can do if a token is
compromised. Practically speaking, the CSRF token is quite small in size and should have a negligible impact on our architecture.</para>
</section>
<section xml:id="csrf-logout">
<title>Logging Out</title>
<para>Adding CSRF will update the LogoutFilter to only use HTTP POST. This ensures that log out requires a CSRF token and that a malicious user cannot forcibly
log out your users.</para>
<para>One approach is to use a form for log out. If you really want a link, you can use JavaScript to have the link perform a POST (i.e. maybe on a hidden form). For
browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that will perform the POST.</para>
</section>
<section xml:id="csrf-multipart">
<title>Multipart (file upload)</title>
<para>There are two options to using CSRF protection with multipart/form-data. Each option has its tradeoffs.
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para><link linkend="csrf-multipartfilter">Placing MultipartFilter before Spring Security</link></para>
</listitem>
<listitem>
<para><link linkend="csrf-include-csrf-token-in-action">Include CSRF token in action</link></para>
</listitem>
</orderedlist>
<note>
<para>More information about using multipart forms with Spring can be found within the
<link xlink:href="http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-multipart">17.10 Spring's multipart (file upload)
support</link> section of the Spring reference.</para>
</note></para>
<section xml:id="csrf-multipartfilter">
<title>Placing MultipartFilter before Spring Security</title>
<para>The first option is to ensure that the <classname>MultipartFilter</classname> is specified before the Spring
Security filter. Specifying the <classname>MultipartFilter</classname> after the Spring Security filter means that there is no authorization for invoking the
<classname>MultipartFilter</classname> which means anyone can place temporary files on your server. However, only authorized users will be able to submit a File that is processed
by your application. In general, this is the recommended approach because the temporary file upload should have a negligble impact on most servers.</para>
<para>To ensure <classname>MultipartFilter</classname> is specified before the Spring Security filter with java configuration, users can override beforeSpringSecurityFilterChain as
shown below:</para>
<programlisting language="java"><![CDATA[public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
@Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}]]></programlisting>
<para>To ensure <classname>MultipartFilter</classname> is specified before the Spring Security filter with XML configuration, users can ensure the &lt;filter-mapping&gt; element
of the <classname>MultipartFilter</classname> is placed before the springSecurityFilterChain within the web.xml as shown below:</para>
<programlisting language="xml"><![CDATA[<filter>
<filter-name>MultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>MultipartFilter</filter-name>
<servlet-name>/*</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
]]></programlisting>
</section>
<section xml:id="csrf-include-csrf-token-in-action">
<title>Include CSRF token in action</title>
<para>If allowing unauthorized users to upload temporariy files is not acceptable, an alternative is to place the <classname>MultipartFilter</classname> after the Spring Security
filter and include the CSRF as a query parameter in the action attribute of the form. An example with a jsp is shown below</para>
<programlisting language="xml"><![CDATA[<form action="./upload?${_csrf.parameterName}=${_csrf.token}" method="post" enctype="multipart/form-data">]]></programlisting>
<para>The disadvantage to this approach is that query parameters can be leaked. More genearlly,
it is considered best practice to place sensitive data within the body or headers to ensure it is not leaked. Additional information can be found in
<link xlink:href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3">RFC 2616 Section 15.1.3 Encoding Sensitive Information in URI's</link>.</para>
</section>
</section>
<section>
<title>HiddenHttpMethodFilter</title>
<para>The HiddenHttpMethodFilter should be placed before the Spring Security filter. In general this is true, but it could have additional implications when
protecting against CSRF attacks.</para>
<para>Note that the HiddenHttpMethodFilter only overrides the HTTP method on a POST, so this is actually unlikely to cause any real problems. However, it is still
best practice to ensure it is placed before Spring Security's filters.</para>
</section>
</section>
<section>
<title>Overriding Defaults</title>
<para>Spring Security's goal is to provide defaults that protect your users from exploits. This does not mean that you are forced to accept all of its defaults.</para>
<para>For example, you can provide a custom CsrfTokenRepository to override the way in which the <interfacename>CsrfToken</interfacename> is stored.</para>
<para>You can also specify a custom RequestMatcher to determine which requests are protected by CSRF (i.e. perhaps you don't care if log out is exploited). In short, if
Spring Security's CSRF protection doesn't behave exactly as you want it, you are able to customize the behavior. Refer to the <link linkend="nsa-csrf">&lt;csrf&gt;</link>
documentation for details on how to make these customizations with XML and the <classname>CsrfConfigurer</classname> javadoc for details on how to make these
customizations when using Java configuration.</para>
</section>
</chapter>

View File

@ -1,294 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="domain-acls">
<info>
<title>Domain Object Security (ACLs)</title>
</info>
<section xml:id="domain-acls-overview">
<info>
<title>Overview</title>
</info>
<para>Complex applications often will find the need to define access permissions not simply
at a web request or method invocation level. Instead, security decisions need to
comprise both who (<interfacename>Authentication</interfacename>), where
(<classname>MethodInvocation</classname>) and what
(<literal>SomeDomainObject</literal>). In other words, authorization decisions also need
to consider the actual domain object instance subject of a method invocation.</para>
<para>Imagine you're designing an application for a pet clinic. There will be two main
groups of users of your Spring-based application: staff of the pet clinic, as well as
the pet clinic's customers. The staff will have access to all of the data, whilst your
customers will only be able to see their own customer records. To make it a little more
interesting, your customers can allow other users to see their customer records, such as
their "puppy preschool" mentor or president of their local "Pony Club". Using Spring
Security as the foundation, you have several approaches that can be used:<orderedlist
inheritnum="ignore" continuation="restarts">
<listitem>
<para>Write your business methods to enforce the security. You could consult a
collection within the <literal>Customer</literal> domain object instance to
determine which users have access. By using the
<literal>SecurityContextHolder.getContext().getAuthentication()</literal>,
you'll be able to access the <interfacename>Authentication</interfacename>
object.</para>
</listitem>
<listitem>
<para>Write an <interfacename>AccessDecisionVoter</interfacename> to enforce the
security from the <literal>GrantedAuthority[]</literal>s stored in the
<interfacename>Authentication</interfacename> object. This would mean your
<interfacename>AuthenticationManager</interfacename> would need to populate the
<interfacename>Authentication</interfacename> with custom
<interfacename>GrantedAuthority</interfacename>[]s representing each of the
<literal>Customer</literal> domain object instances the principal has access
to.</para>
</listitem>
<listitem>
<para>Write an <interfacename>AccessDecisionVoter</interfacename> to enforce the
security and open the target <literal>Customer</literal> domain object directly.
This would mean your voter needs access to a DAO that allows it to retrieve the
<literal>Customer</literal> object. It would then access the
<literal>Customer</literal> object's collection of approved users and make the
appropriate decision.</para>
</listitem>
</orderedlist></para>
<para>Each one of these approaches is perfectly legitimate. However, the first couples your
authorization checking to your business code. The main problems with this include the
enhanced difficulty of unit testing and the fact it would be more difficult to reuse the
<literal>Customer</literal> authorization logic elsewhere. Obtaining the
<literal>GrantedAuthority[]</literal>s from the
<interfacename>Authentication</interfacename> object is also fine, but will not scale to
large numbers of <literal>Customer</literal>s. If a user might be able to access 5,000
<literal>Customer</literal>s (unlikely in this case, but imagine if it were a popular
vet for a large Pony Club!) the amount of memory consumed and time required to construct
the <interfacename>Authentication</interfacename> object would be undesirable. The final
method, opening the <literal>Customer</literal> directly from external code, is probably
the best of the three. It achieves separation of concerns, and doesn't misuse memory or
CPU cycles, but it is still inefficient in that both the
<interfacename>AccessDecisionVoter</interfacename> and the eventual business method
itself will perform a call to the DAO responsible for retrieving the
<literal>Customer</literal> object. Two accesses per method invocation is clearly
undesirable. In addition, with every approach listed you'll need to write your own
access control list (ACL) persistence and business logic from scratch.</para>
<para>Fortunately, there is another alternative, which we'll talk about below.</para>
</section>
<section xml:id="domain-acls-key-concepts">
<info>
<title>Key Concepts</title>
</info>
<para>Spring Security's ACL services are shipped in the
<literal>spring-security-acl-xxx.jar</literal>. You will need to add this JAR to your
classpath to use Spring Security's domain object instance security capabilities.</para>
<para>Spring Security's domain object instance security capabilities centre on the concept
of an access control list (ACL). Every domain object instance in your system has its own
ACL, and the ACL records details of who can and can't work with that domain object. With
this in mind, Spring Security delivers three main ACL-related capabilities to your application:<itemizedlist>
<listitem>
<para>A way of efficiently retrieving ACL entries for all of your domain objects
(and modifying those ACLs)</para>
</listitem>
<listitem>
<para>A way of ensuring a given principal is permitted to work with your objects,
before methods are called</para>
</listitem>
<listitem>
<para>A way of ensuring a given principal is permitted to work with your objects (or
something they return), after methods are called</para>
</listitem>
</itemizedlist></para>
<para>As indicated by the first bullet point, one of the main capabilities of the Spring
Security ACL module is providing a high-performance way of retrieving ACLs. This ACL
repository capability is extremely important, because every domain object instance in
your system might have several access control entries, and each ACL might inherit from
other ACLs in a tree-like structure (this is supported out-of-the-box by Spring
Security, and is very commonly used). Spring Security's ACL capability has been
carefully designed to provide high performance retrieval of ACLs, together with
pluggable caching, deadlock-minimizing database updates, independence from ORM
frameworks (we use JDBC directly), proper encapsulation, and transparent database
updating.</para>
<para>Given databases are central to the operation of the ACL module, let's explore the four
main tables used by default in the implementation. The tables are presented below in
order of size in a typical Spring Security ACL deployment, with the table with the most
rows listed last:</para>
<para> <itemizedlist>
<listitem>
<para>ACL_SID allows us to uniquely identify any principal or authority in the
system ("SID" stands for "security identity"). The only columns are the ID, a
textual representation of the SID, and a flag to indicate whether the textual
representation refers to a principal name or a
<interfacename>GrantedAuthority</interfacename>. Thus, there is a single row for
each unique principal or <interfacename>GrantedAuthority</interfacename>. When
used in the context of receiving a permission, a SID is generally called a
"recipient".</para>
</listitem>
<listitem>
<para>ACL_CLASS allows us to uniquely identify any domain object class in the
system. The only columns are the ID and the Java class name. Thus, there is a
single row for each unique Class we wish to store ACL permissions for.</para>
</listitem>
<listitem>
<para>ACL_OBJECT_IDENTITY stores information for each unique domain object instance
in the system. Columns include the ID, a foreign key to the ACL_CLASS table, a
unique identifier so we know which ACL_CLASS instance we're providing
information for, the parent, a foreign key to the ACL_SID table to represent the
owner of the domain object instance, and whether we allow ACL entries to inherit
from any parent ACL. We have a single row for every domain object instance we're
storing ACL permissions for.</para>
</listitem>
<listitem>
<para>Finally, ACL_ENTRY stores the individual permissions assigned to each
recipient. Columns include a foreign key to the ACL_OBJECT_IDENTITY, the
recipient (ie a foreign key to ACL_SID), whether we'll be auditing or not, and
the integer bit mask that represents the actual permission being granted or
denied. We have a single row for every recipient that receives a permission to
work with a domain object.</para>
</listitem>
</itemizedlist> </para>
<para>As mentioned in the last paragraph, the ACL system uses integer bit masking. Don't
worry, you need not be aware of the finer points of bit shifting to use the ACL system,
but suffice to say that we have 32 bits we can switch on or off. Each of these bits
represents a permission, and by default the permissions are read (bit 0), write (bit 1),
create (bit 2), delete (bit 3) and administer (bit 4). It's easy to implement your own
<literal>Permission</literal> instance if you wish to use other permissions, and the
remainder of the ACL framework will operate without knowledge of your extensions.</para>
<para>It is important to understand that the number of domain objects in your system has
absolutely no bearing on the fact we've chosen to use integer bit masking. Whilst you
have 32 bits available for permissions, you could have billions of domain object
instances (which will mean billions of rows in ACL_OBJECT_IDENTITY and quite probably
ACL_ENTRY). We make this point because we've found sometimes people mistakenly believe
they need a bit for each potential domain object, which is not the case.</para>
<para>Now that we've provided a basic overview of what the ACL system does, and what it
looks like at a table structure, let's explore the key interfaces. The key interfaces
are:</para>
<itemizedlist spacing="compact">
<listitem>
<para><literal>Acl</literal>: Every domain object has one and only one
<literal>Acl</literal> object, which internally holds the
<literal>AccessControlEntry</literal>s as well as knows the owner of the
<literal>Acl</literal>. An Acl does not refer directly to the domain object, but
instead to an <literal>ObjectIdentity</literal>. The <literal>Acl</literal> is
stored in the ACL_OBJECT_IDENTITY table.</para>
</listitem>
<listitem>
<para><literal>AccessControlEntry</literal>: An <literal>Acl</literal> holds
multiple <literal>AccessControlEntry</literal>s, which are often abbreviated as
ACEs in the framework. Each ACE refers to a specific tuple of
<literal>Permission</literal>, <literal>Sid</literal> and
<literal>Acl</literal>. An ACE can also be granting or non-granting and contain
audit settings. The ACE is stored in the ACL_ENTRY table.</para>
</listitem>
<listitem>
<para><literal>Permission</literal>: A permission represents a particular immutable
bit mask, and offers convenience functions for bit masking and outputting
information. The basic permissions presented above (bits 0 through 4) are
contained in the <literal>BasePermission</literal> class.</para>
</listitem>
<listitem>
<para><literal>Sid</literal>: The ACL module needs to refer to principals and
<literal>GrantedAuthority[]</literal>s. A level of indirection is provided by
the <literal>Sid</literal> interface, which is an abbreviation of "security
identity". Common classes include <literal>PrincipalSid</literal> (to represent
the principal inside an <interfacename>Authentication</interfacename> object)
and <literal>GrantedAuthoritySid</literal>. The security identity information is
stored in the ACL_SID table.</para>
</listitem>
<listitem>
<para><literal>ObjectIdentity</literal>: Each domain object is represented
internally within the ACL module by an <literal>ObjectIdentity</literal>. The
default implementation is called <literal>ObjectIdentityImpl</literal>.</para>
</listitem>
<listitem>
<para><literal>AclService</literal>: Retrieves the <literal>Acl</literal> applicable
for a given <literal>ObjectIdentity</literal>. In the included implementation
(<literal>JdbcAclService</literal>), retrieval operations are delegated to a
<literal>LookupStrategy</literal>. The <literal>LookupStrategy</literal>
provides a highly optimized strategy for retrieving ACL information, using
batched retrievals <literal>(BasicLookupStrategy</literal>) and supporting
custom implementations that leverage materialized views, hierarchical queries
and similar performance-centric, non-ANSI SQL capabilities.</para>
</listitem>
<listitem>
<para><literal>MutableAclService</literal>: Allows a modified <literal>Acl</literal>
to be presented for persistence. It is not essential to use this interface if
you do not wish.</para>
</listitem>
</itemizedlist>
<para>Please note that our out-of-the-box AclService and related database classes all use
ANSI SQL. This should therefore work with all major databases. At the time of writing,
the system had been successfully tested using Hypersonic SQL, PostgreSQL, Microsoft SQL
Server and Oracle.</para>
<para>Two samples ship with Spring Security that demonstrate the ACL module. The first is
the Contacts Sample, and the other is the Document Management System (DMS) Sample. We
suggest taking a look over these for examples.</para>
</section>
<section xml:id="domain-acls-getting-started">
<info>
<title>Getting Started</title>
</info>
<para>To get starting using Spring Security's ACL capability, you will need to store your
ACL information somewhere. This necessitates the instantiation of a
<literal>DataSource</literal> using Spring. The <literal>DataSource</literal> is then
injected into a <literal>JdbcMutableAclService</literal> and
<literal>BasicLookupStrategy</literal> instance. The latter provides high-performance
ACL retrieval capabilities, and the former provides mutator capabilities. Refer to one
of the samples that ship with Spring Security for an example configuration. You'll also
need to populate the database with the four ACL-specific tables listed in the last
section (refer to the ACL samples for the appropriate SQL statements).</para>
<para>Once you've created the required schema and instantiated
<literal>JdbcMutableAclService</literal>, you'll next need to ensure your domain model
supports interoperability with the Spring Security ACL package. Hopefully
<literal>ObjectIdentityImpl</literal> will prove sufficient, as it provides a large
number of ways in which it can be used. Most people will have domain objects that
contain a <literal>public Serializable getId()</literal> method. If the return type is
long, or compatible with long (eg an int), you will find you need not give further
consideration to <literal>ObjectIdentity</literal> issues. Many parts of the ACL module
rely on long identifiers. If you're not using long (or an int, byte etc), there is a
very good chance you'll need to reimplement a number of classes. We do not intend to
support non-long identifiers in Spring Security's ACL module, as longs are already
compatible with all database sequences, the most common identifier data type, and are of
sufficient length to accommodate all common usage scenarios.</para>
<para>The following fragment of code shows how to create an <literal>Acl</literal>, or
modify an existing
<literal>Acl</literal>:<programlisting language="java">// Prepare the information we'd like in our access control entry (ACE)
ObjectIdentity oi = new ObjectIdentityImpl(Foo.class, new Long(44));
Sid sid = new PrincipalSid("Samantha");
Permission p = BasePermission.ADMINISTRATION;
// Create or update the relevant ACL
MutableAcl acl = null;
try {
acl = (MutableAcl) aclService.readAclById(oi);
} catch (NotFoundException nfe) {
acl = aclService.createAcl(oi);
}
// Now grant some permissions via an access control entry (ACE)
acl.insertAce(acl.getEntries().length, p, sid, true);
aclService.updateAcl(acl);
</programlisting></para>
<para>In the example above, we're retrieving the ACL associated with the "Foo" domain object
with identifier number 44. We're then adding an ACE so that a principal named "Samantha"
can "administer" the object. The code fragment is relatively self-explanatory, except
the insertAce method. The first argument to the insertAce method is determining at what
position in the Acl the new entry will be inserted. In the example above, we're just
putting the new ACE at the end of the existing ACEs. The final argument is a boolean
indicating whether the ACE is granting or denying. Most of the time it will be granting
(true), but if it is denying (false), the permissions are effectively being
blocked.</para>
<para>Spring Security does not provide any special integration to automatically create,
update or delete ACLs as part of your DAO or repository operations. Instead, you will
need to write code like shown above for your individual domain objects. It's worth
considering using AOP on your services layer to automatically integrate the ACL
information with your services layer operations. We've found this quite an effective
approach in the past.</para>
<para>Once you've used the above techniques to store some ACL information in the database,
the next step is to actually use the ACL information as part of authorization decision
logic. You have a number of choices here. You could write your own
<interfacename>AccessDecisionVoter</interfacename> or
<literal>AfterInvocationProvider</literal> that respectively fires before or after a
method invocation. Such classes would use <literal>AclService</literal> to retrieve the
relevant ACL and then call <literal>Acl.isGranted(Permission[] permission, Sid[] sids,
boolean administrativeMode)</literal> to decide whether permission is granted or denied.
Alternately, you could use our <literal>AclEntryVoter</literal>,
<literal>AclEntryAfterInvocationProvider</literal> or
<literal>AclEntryAfterInvocationCollectionFilteringProvider</literal> classes. All of
these classes provide a declarative-based approach to evaluating ACL information at
runtime, freeing you from needing to write any code. Please refer to the sample
applications to learn how to use these classes.</para>
</section>
</chapter>

View File

@ -1,297 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="el-access"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Expression-Based Access Control</title>
<para> Spring Security 3.0 introduced the ability to use Spring EL expressions as an
authorization mechanism in addition to the simple use of configuration attributes and
access-decision voters which have seen before. Expression-based access control is built on
the same architecture but allows complicated boolean logic to be encapsulated in a single
expression.</para>
<section>
<title>Overview</title>
<para>Spring Security uses Spring EL for expression support and you should look at how that
works if you are interested in understanding the topic in more depth. Expressions are
evaluated with a <quote>root object</quote> as part of the evaluation context. Spring
Security uses specific classes for web and method security as the root object, in order
to provide built-in expressions and access to values such as the current
principal.</para>
<section xml:id="el-common-built-in">
<title>Common Built-In Expressions</title>
<para>The base class for expression root objects is
<classname>SecurityExpressionRoot</classname>. This provides some common expressions
which are available in both web and method security.</para>
<table frame="none">
<title>Common built-in expressions</title>
<tgroup cols="2">
<colspec colname="c1" colnum="1" colwidth="1.0*"/>
<colspec colname="c2" colnum="2" colwidth="2.0*"/>
<thead>
<row>
<entry>Expression</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><literal>hasRole([role])</literal></entry>
<entry>Returns <literal>true</literal> if the current principal has the
specified role.</entry>
</row>
<row>
<entry><literal>hasAnyRole([role1,role2])</literal></entry>
<entry>Returns <literal>true</literal> if the current principal has any
of the supplied roles (given as a comma-separated list of
strings)</entry>
</row>
<row>
<entry><literal>principal</literal></entry>
<entry>Allows direct access to the principal object representing the
current user</entry>
</row>
<row>
<entry><literal>authentication</literal></entry>
<entry>Allows direct access to the current
<interfacename>Authentication</interfacename> object obtained from
the <interfacename>SecurityContext</interfacename></entry>
</row>
<row>
<entry><literal>permitAll</literal></entry>
<entry>Always evaluates to <literal>true</literal></entry>
</row>
<row>
<entry><literal>denyAll</literal></entry>
<entry>Always evaluates to <literal>false</literal></entry>
</row>
<row>
<entry><literal>isAnonymous()</literal></entry>
<entry>Returns <literal>true</literal> if the current principal is an
anonymous user</entry>
</row>
<row>
<entry><literal>isRememberMe()</literal></entry>
<entry>Returns <literal>true</literal> if the current principal is a
remember-me user</entry>
</row>
<row>
<entry><literal>isAuthenticated()</literal></entry>
<entry>Returns <literal>true</literal> if the user is not
anonymous</entry>
</row>
<row>
<entry><literal>isFullyAuthenticated()</literal></entry>
<entry>Returns <literal>true</literal> if the user is not an anonymous
or a remember-me user</entry>
</row>
</tbody>
</tgroup>
</table>
</section>
</section>
<section xml:id="el-access-web">
<title>Web Security Expressions</title>
<para> To use expressions to secure individual URLs, you would first need to set the
<literal>use-expressions</literal> attribute in the <literal>&lt;http></literal> element
to <literal>true</literal>. Spring Security will then expect the
<literal>access</literal> attributes of the <literal>&lt;intercept-url></literal>
elements to contain Spring EL expressions. The expressions should evaluate to a boolean,
defining whether access should be allowed or not. For example:<programlisting language="xml"><![CDATA[
<http use-expressions="true">
<intercept-url pattern="/admin*"
access="hasRole('admin') and hasIpAddress('192.168.1.0/24')"/>
...
</http>
]]></programlisting>Here we have defined that the <quote>admin</quote> area of an application
(defined by the URL pattern) should only be available to users who have the granted
authority <quote>admin</quote> and whose IP address matches a local subnet. We've
already seen the built-in <literal>hasRole</literal> expression in the previous section.
The expression <literal>hasIpAddress</literal> is an additional built-in expression
which is specific to web security. It is defined by the
<classname>WebSecurityExpressionRoot</classname> class, an instance of which is used as
the expression root object when evaluation web-access expressions. This object also
directly exposed the <interfacename>HttpServletRequest</interfacename> object under the
name <literal>request</literal> so you can invoke the request directly in an
expression.</para>
<para>If expressions are being used, a <classname>WebExpressionVoter</classname> will be
added to the <interfacename>AccessDecisionManager</interfacename> which is used by the
namespace. So if you aren't using the namespace and want to use expressions, you will
have to add one of these to your configuration.</para>
</section>
<section>
<title>Method Security Expressions</title>
<para>Method security is a bit more complicated than a simple allow or deny rule. Spring
Security 3.0 introduced some new annotations in order to allow comprehensive support for
the use of expressions.</para>
<section xml:id="el-pre-post-annotations">
<title><literal>@Pre</literal> and <literal>@Post</literal> Annotations</title>
<para>There are four annotations which support expression attributes to allow pre and
post-invocation authorization checks and also to support filtering of submitted
collection arguments or return values. They are <literal>@PreAuthorize</literal>,
<literal>@PreFilter</literal>, <literal>@PostAuthorize</literal> and
<literal>@PostFilter</literal>. Their use is enabled through the
<literal>global-method-security</literal> namespace
element:<programlisting language="xml"><![CDATA[<global-method-security pre-post-annotations="enabled"/>]]></programlisting></para>
<section>
<title>Access Control using <literal>@PreAuthorize</literal> and
<literal>@PostAuthorize</literal></title>
<para>The most obviously useful annotation is <literal>@PreAuthorize</literal> which
decides whether a method can actually be invoked or not. For example (from the
<quote>Contacts</quote> sample
application)<programlisting language="java">
@PreAuthorize("hasRole('ROLE_USER')")
public void create(Contact contact);</programlisting>which
means that access will only be allowed for users with the role "ROLE_USER".</para>
<section xml:id="el-pre-post-annotations-arguments">
<title>Resolving method arguments</title>
<para>Obviously the same thing could easily be achieved using a traditional
configuration and a simple configuration attribute for the required role. But
what
about:<programlisting language="java">
@PreAuthorize("hasPermission(#contact, 'admin')")
public void deletePermission(Contact contact, Sid recipient, Permission permission);</programlisting>Here
we're actually using a method argument as part of the expression to decide
whether the current user has the <quote>admin</quote>permission for the given
contact. The built-in <literal>hasPermission()</literal> expression is linked
into the Spring Security ACL module through the application context, as we'll
<link linkend="el-permission-evaluator">see below</link>. You can access any
of the method arguments by name as expression variables.</para>
<para>There are a number of ways in which Spring Security can resolve the method arguments. Spring Security
uses <classname>DefaultSecurityParameterNameDiscoverer</classname> to discover the parameter names. By default,
the following options are tried for a method as a whole.
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>If Spring Security's <literal>@P</literal> annotation is present on a single argument to the method,
the value will be used. This is useful for interfaces compiled with a JDK prior to JDK 8 which do not contain
any information about the parameter names. For example: <programlisting language="java">
import org.springframework.security.access.method.P;
...
@PreAuthorize("#c.name == authentication.name")
public void doSomething(@P("c") Contact contact);</programlisting></para>
<para>Behind the scenes this use implemented using <classname>AnnotationParameterNameDiscoverer</classname> which
can be customized to support the value attribute of any specified annotation.</para>
</listitem>
<listitem>
<para>If Spring Data's <literal>@Param</literal> annotation is present on at least one parameter for the method,
the value will be used. This is useful for interfaces compiled with a JDK prior to JDK 8 which do not contain
any information about the parameter names. For example: <programlisting language="java">
import org.springframework.data.repository.query.Param;
...
@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);</programlisting></para>
<para>Behind the scenes this use implemented using <classname>AnnotationParameterNameDiscoverer</classname> which
can be customized to support the value attribute of any specified annotation.</para>
</listitem>
<listitem>
<para>If JDK 8 was used to compile the source with the -parameters argument and Spring 4+ is being used, then
the standard JDK reflection API is used to discover the parameter names. This works on both classes and
interfaces.</para>
</listitem>
<listitem>
<para>Last, if the code was compiled with the debug symbols, the parameter names will be discovered using
the debug symbols. This will not work for interfaces since they do not have debug information about the
parameter names. For interfaces, annotations or the JDK 8 approach must be used.</para>
</listitem>
</orderedlist></para>
</section>
<section xml:id="el-pre-post-annotations-spel">
<title>Method Expressions and SpEL</title>
<para>Any Spring-EL functionality is available within
the expression, so you can also access properties on the arguments. For example,
if you wanted a particular method to only allow access to a user whose username
matched that of the contact, you could write</para>
<programlisting language="java">
@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);</programlisting>
<para>Here we are accessing another builtin expression, <literal>authentication</literal>,
which is the <interfacename>Authentication</interfacename> stored in the
security context. You can also access its <quote>principal</quote> property
directly, using the expression <literal>principal</literal>. The value will
often be a <interfacename>UserDetails</interfacename> instance, so you might use an
expression like <literal>principal.username</literal> or
<literal>principal.enabled</literal>.</para>
</section>
<section xml:id="el-pre-post-annotations-post">
<title>Accessing the return value</title>
<para>Less commonly, you may wish to perform an access-control check after the
method has been invoked. This can be achieved using the
<literal>@PostAuthorize</literal> annotation. To access the return value from a
method, use the builtin name <literal>returnObject</literal> in the
expression.</para>
</section>
</section>
<section>
<title>Filtering using <literal>@PreFilter</literal> and
<literal>@PostFilter</literal></title>
<para>As you may already be aware, Spring Security supports filtering of collections
and arrays and this can now be achieved using expressions. This is most commonly
performed on the return value of a method. For
example:<programlisting language="java"> @PreAuthorize("hasRole('ROLE_USER')")
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List&lt;Contact> getAll();</programlisting>When
using the <literal>@PostFilter</literal> annotation, Spring Security iterates
through the returned collection and removes any elements for which the supplied
expression is false. The name <literal>filterObject</literal> refers to the
current object in the collection. You can also filter before the method call,
using <literal>@PreFilter</literal>, though this is a less common requirement.
The syntax is just the same, but if there is more than one argument which is a
collection type then you have to select one by name using the
<literal>filterTarget</literal> property of this annotation.</para>
<para>Note that filtering is obviously not a substitute for tuning your data
retrieval queries. If you are filtering large collections and removing many of
the entries then this is likely to be inefficient.</para>
</section>
</section>
<section xml:id="el-method-built-in">
<title>Built-In Expressions</title>
<para>There are some built-in expressions which are specific to method security, which
we have already seen in use above. The <literal>filterTarget</literal> and
<literal>returnValue</literal> values are simple enough, but the use of the
<literal>hasPermission()</literal> expression warrants a closer look.</para>
<section xml:id="el-permission-evaluator">
<title>The <interfacename>PermissionEvaluator</interfacename> interface</title>
<para><literal>hasPermission()</literal> expressions are delegated to an instance of
<interfacename>PermissionEvaluator</interfacename>. It is intended to bridge
between the expression system and Spring Security's ACL system, allowing you to
specify authorization constraints on domain objects, based on abstract
permissions. It has no explicit dependencies on the ACL module, so you could
swap that out for an alternative implementation if required. The interface has
two methods:
<programlisting language="java">
boolean hasPermission(Authentication authentication, Object targetDomainObject,
Object permission);
boolean hasPermission(Authentication authentication, Serializable targetId,
String targetType, Object permission);
</programlisting>which
map directly to the available versions of the expression, with the exception
that the first argument (the <interfacename>Authentication</interfacename>
object) is not supplied. The first is used in situations where the domain
object, to which access is being controlled, is already loaded. Then expression
will return true if the current user has the given permission for that object.
The second version is used in cases where the object is not loaded, but its
identifier is known. An abstract <quote>type</quote> specifier for the domain
object is also required, allowing the correct ACL permissions to be loaded. This
has traditionally been the Java class of the object, but does not have to be as
long as it is consistent with how the permissions are loaded.</para>
<para>To use <literal>hasPermission()</literal> expressions, you have to explicitly
configure a <interfacename>PermissionEvaluator</interfacename> in your
application context. This would look something like this: <programlisting language="xml"> <![CDATA[
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<bean id="expressionHandler" class=
"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="permissionEvaluator" ref="myPermissionEvaluator"/>
</bean>]]></programlisting>Where <literal>myPermissionEvaluator</literal> is the bean which
implements <interfacename>PermissionEvaluator</interfacename>. Usually this will
be the implementation from the ACL module which is called
<classname>AclPermissionEvaluator</classname>. See the <quote>Contacts</quote>
sample application configuration for more details.</para>
</section>
</section>
</section>
</chapter>

View File

@ -1,68 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="form">
<info>
<title>Form Authentication Mechanism</title>
</info>
<section xml:id="form-overview">
<info>
<title>Overview</title>
</info>
<para>HTTP Form Authentication involves using the
<literal>UsernamePasswordAuthenticationFilter</literal> to process a login form. This is
the most common way for an application to authenticate end users. Form-based
authentication is entirely compatible with the DAO, LDAP and JAAS authentication
providers.</para>
<para>This is also the mechanism used by the &lt;form-login&gt; element from the namespace
and it's recommended that you use that unless you have specific customization
requirements. </para>
</section>
<section xml:id="form-config">
<info>
<title>Configuration</title>
</info>
<para>The login form simply contains <literal>j_username</literal> and
<literal>j_password</literal> input fields, and posts to a URL that is monitored by the
filter (by default <literal>/j_spring_security_check</literal>). You should add an
<literal>UsernamePasswordAuthenticationFilter</literal> to your application context: <programlisting language="xml"><![CDATA[
<bean id="authenticationProcessingFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="filterProcessesUrl" value="/j_spring_security_check"/>
</bean> ]]>
</programlisting></para>
<para> The configured <interfacename>AuthenticationManager</interfacename> processes each
authentication request. The destination following a successful authentication or an
authentication failure is controlled by the
<interfacename>AuthenticationSuccessHandler</interfacename> and
<interfacename>AuthenticationFailureHandler</interfacename> interfaces, respectively.
The filter has properties which allow you to set these <footnote>
<para>In versions prior to 3.0, the application flow at this point had evolved to a
stage was controlled by a mix of properties on this class and strategy plugins. The
decision was made for 3.0 to refactor the code to make these two strategies entirely
responsible. </para>
</footnote>. Some standard implementations are supplied for these such as
<classname>SimpleUrlAuthenticationSuccessHandler</classname>,
<classname>SavedRequestAwareAuthenticationSuccessHandler</classname>,
<classname>SimpleUrlAuthenticationFailureHandler</classname> and
<classname>ExceptionMappingAuthenticationFailureHandler</classname>. Have a look at the
Javadoc for these classes to see how they work. </para>
<para>If authentication is successful, the resulting
<interfacename>Authentication</interfacename> object will be placed into the
<classname>SecurityContextHolder</classname>. The configured
AuthenticationSuccessHandler will then be called to either redirect or forward the user
to the appropriate destination. By default a
<classname>SavedRequestAwareAuthenticationSuccessHandler</classname> is used, which
means that the user will be redirected to the original destination they requested before
they were asked to login. <note>
<para> The <classname>ExceptionTranslationFilter</classname> caches the original request
a user makes. When the user authenticates, the request handler makes use of this
cached request to obtain the original URL and redirect to it. The original request
is then rebuilt and used as an alternative. </para>
</note> If authentication fails, the configured
<interfacename>AuthenticationFailureHandler</interfacename> will be invoked. </para>
</section>
</chapter>

View File

@ -1,436 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="headers"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Security Headers</title>
</info>
<para>This section discusses Spring Security's support for adding various security headers to the response.</para>
<section>
<title>Default Security Headers</title>
<para>Spring Security allows users to easily inject the default security headers to assist in protecting their
application. The following is a list of the current <emphasis>Default Security Headers</emphasis> provided
by Spring Security:
<itemizedlist>
<listitem>
<link linkend="headers-cache-control">Cache Control</link>
</listitem>
<listitem>
<link linkend="headers-content-type-options">Content Type Options</link>
</listitem>
<listitem>
<link linkend="headers-hsts">HTTP Strict Transport Security</link>
</listitem>
<listitem>
<link linkend="headers-frame-options">X-Frame-Options</link>
</listitem>
<listitem>
<link linkend="headers-xss-protection">X-XSS-Protection</link>
</listitem>
</itemizedlist></para>
<para>While each of these headers are considered best practice, it should be noted that not all clients
utilize the headers, so additional testing is encouraged. For passivity reasons, if you are using Spring Security's
XML namespace support, you must explicitly enable the security headers. All of the default headers can be easily added
using the <link linkend="nsa-headers">&lt;headers&gt;</link> element with no child elements:</para>
<note>
<para><link xlink:href="https://jira.springsource.org/browse/SEC-2348">SEC-2348</link> is logged to ensure Spring
Security 4.x's XML namespace configuration will enable Security headers by default.</para>
</note>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers />
</http>]]></programlisting>
<para>Alternatively, you can choose to explicitly list the headers you wish to include. For example, the following is
the same the previous configuration. Removing any of the elements will remove that header from the responses.</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<cache-control />
<content-type-options />
<hsts />
<frame-options />
<xss-protection />
</headers>
</http>]]></programlisting>
<para>If you are using Spring Security's Java configuration, all of the default security headers are added by default.
They can be disabled using the Java configuration below:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers().disable();
}
}]]></programlisting>
<para>As soon as you specify any headers that should be included, then only those headers will be include. For example, the
following configuration will include support for <link linkend="headers-cache-control">Cache Control</link> and
<link linkend="headers-frame-options">X-Frame-Options</link> only.</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.cacheControl()
.frameOptions();
}
}]]></programlisting>
<section xml:id="headers-cache-control">
<title>Cache Control</title>
<para>In the past Spring Security required you to provide your own cache control for your web application. This
seemed reasonable at the time, but browser caches have evolved to include caches for secure connections as
well. This means that a user may view an authenticated page, log out, and then a malicious user can use the
browser history to view the cached page. To help mitigate this Spring Security has added cache control support
which will insert the following headers into you response.</para>
<programlisting><![CDATA[Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0]]></programlisting>
<para>Simply adding the <link linkend="nsa-headers">&lt;headers&gt;</link> element with no child elements will
automatically add Cache Control and quite a few other protections. However, if you only want cache control, you can
enable this feature using Spring Security's XML namespace with the
<link linkend="nsa-cache-control">&lt;cache-control&gt;</link> element.</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<cache-control />
</headers>
</http>]]></programlisting>
<para>Similarly, you can enable only cache control within Java Configuration with the following:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.cacheControl();
}
}]]></programlisting>
<para>If you actually want to cache specific responses, your application can selectively invoke
<link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletResponse.html#setHeader(java.lang.String, java.lang.String)">HttpServletResponse.setHeader(String,String)</link>
to override the header set by Spring Security. This is useful to ensure things
like CSS, JavaScript, and images are properly cached.</para>
<para>When using Spring Web MVC, this is typically done within your configuration. For example, the following configuration will
ensure that the cache headers are set for all of your resources:</para>
<programlisting language="java"><![CDATA[@EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/resources/**")
.addResourceLocations("/resources/")
.setCachePeriod(31556926);
}
// ...
}]]></programlisting>
</section>
<section xml:id="headers-content-type-options">
<title>Content Type Options</title>
<para>Historically browsers, including Internet Explorer, would try to guess the content type of a request using
<link xlink:href="http://en.wikipedia.org/wiki/Content_sniffing">content sniffing</link>. This
allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type.
For example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content
type and then execute it.</para>
<note>
<para>There are many additional things one should do (i.e. only display the document in a distinct domain, ensure
Content-Type header is set, sanitize the document, etc) when allowing content to be uploaded. However, these measures
are out of the scope of what Spring Security provides. It is also important to point out when disabling content sniffing,
you must specify the content type in order for things to work properly.</para>
</note>
<para>The problem with content sniffing is that this allowed malicious users to use polyglots (i.e. a file that is valid as multiple content
types) to execute XSS attacks. For example, some sites may allow users to submit a valid postscript document to a website and view it. A malicious
user might create a <link xlink:href="http://webblaze.cs.berkeley.edu/papers/barth-caballero-song.pdf">postscript document that is also a valid
JavaScript file</link> and execute a XSS attack with it.</para>
<para>Content sniffing can be disabled by adding the following header to our response:</para>
<programlisting><![CDATA[X-Content-Type-Options: nosniff]]></programlisting>
<para>Just as with the cache control element, the nosniff directive is added by default when using the &lt;headers&gt; element with no child elements.
However, if you want more control over which headers are added you can use the
<link linkend="nsa-content-type-options">&lt;content-type-options&gt;</link> element as shown below:</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<content-type-options />
</headers>
</http>]]></programlisting>
<para>The X-Content-Type-Options header is added by default with Spring Security Java configuration. If you want more control over the headers, you can
explicitly specify the content type options with the following:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.contentTypeOptions();
}
}]]></programlisting>
</section>
<section xml:id="headers-hsts">
<title>HTTP Strict Transport Security (HSTS)</title>
<para>When you type in your bank's website, do you enter mybank.example.com or do you enter https://mybank.example.com? If you
omit the https protocol, you are potentially vulnerable to
<link xlink:href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack">Man in the Middle attacks</link>. Even if the website performs a redirect
to https://mybank.example.com a malicious user could intercept the initial HTTP request and manipulate the response (i.e.
redirect to https://mibank.example.com and steal their credentials).</para>
<para>Many users omit the https protocol and this is why <link xlink:href="http://tools.ietf.org/html/rfc6797">HTTP Strict Transport Security (HSTS)</link>
was created. Once mybank.example.com is added as a <link xlink:href="http://tools.ietf.org/html/rfc6797#section-5.1">HSTS host</link>, a browser can
know ahead of time that any request to mybank.example.com should be interpreted as
https://mybank.example.com. This greatly reduces the possibility of a Man in the Middle attack occurring.</para>
<note>
<para>In accordance with <link xlink:href="http://tools.ietf.org/html/rfc6797#section-7.2">RFC6797</link>, the HSTS header is only injected into HTTPS
responses. In order for the browser to acknowledge the header, the browser must first trust the CA that signed the SSL certificate used to make the
connection (not just the SSL certificate).</para>
</note>
<para>One way for a site to be marked as a HSTS host is to have the host preloaded into the browser. Another is to add the
"Strict-Transport-Security" header to the response. For example the following would instruct the browser to treat the domain as an HSTS
host for a year (there are approximately 31536000 seconds in a year):</para>
<programlisting><![CDATA[Strict-Transport-Security: max-age=31536000 ; includeSubDomains]]></programlisting>
<para>The optional includeSubDomains directive instructs Spring Security that subdomains (i.e. secure.mybank.example.com) should also be
treated as an HSTS domain.</para>
<para>As with the other headers, Spring Security adds the previous header to the response when the &lt;headers&gt; element is specified with
no child elements. It is also automatically added when you are using Java Configuration. You can also only use HSTS headers with the
<link linkend="nsa-hsts">&lt;hsts&gt;</link> element as shown below:</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<hsts />
</headers>
</http>]]></programlisting>
<para>Similarly, you can enable only HSTS headers with Java Configuration:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.hsts();
}
}]]></programlisting>
</section>
<section xml:id="headers-frame-options">
<title>X-Frame-Options</title>
<para>Allowing your website to be added to a frame can be a security issue. For example, using clever CSS styling users
could be tricked into clicking on something that they were not intending (
<link xlink:href="http://www.youtube.com/watch?v=3mk0RySeNsU">video demo</link>). For example, a user that is logged
into their bank might click a button that grants access to other users. This sort of attack is known as
<link xlink:href="http://en.wikipedia.org/wiki/Clickjacking">Clickjacking</link>.</para>
<note>
<para>Another modern approach to dealing with clickjacking is using a <link xlink:href="http://www.w3.org/TR/CSP/">Content
Security Policy</link>. Spring Security does not provide
support for this as the specification is not released and it is quite a bit more complicated. However, you could use the
<link linkend="headers-static">static headers</link> feature to implement this. To stay up to date with this
issue and to see how you can implement it with Spring Security refer to
<link xlink:href="https://jira.springsource.org/browse/SEC-2117">SEC-2117</link> </para>
</note>
<para>There are a number ways to mitigate clickjacking attacks. For example, to protect legacy browsers from clickjacking attacks you
can use
<link xlink:href="https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet#Best-for-now_Legacy_Browser_Frame_Breaking_Script">frame
breaking code</link>. While not perfect, the frame breaking code is the best you can do for the legacy browsers.</para>
<para>A more modern approach to address clickjacking is to use
<link xlink:href="https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options">X-Frame-Options</link> header:</para>
<programlisting><![CDATA[X-Frame-Options: DENY]]></programlisting>
<para>The X-Frame-Options response header instructs the browser to prevent any site with this header in the response from being rendered
within a frame. As with the other response headers, this is automatically included when the &lt;headers&gt; element is specified with no
child elements. You can also explicitly specify the <link linkend="nsa-frame-options">frame-options</link> element to control which headers
are added to the response.</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<frame-options />
</headers>
</http>]]></programlisting>
<para>Similarly, you can enable only frame options within Java Configuration with the following:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.frameOptions();
}
}]]></programlisting>
<para>If you want to change the value for the X-Frame-Options header, then you can use a
<link linkend="headers-headers-writer">XFrameOptionsHeaderWriter instance</link>.</para>
</section>
<section xml:id="headers-xss-protection">
<title>X-XSS-Protection</title>
<para>Some browsers have built in support for filtering out
<link xlink:href="https://www.owasp.org/index.php/Testing_for_Reflected_Cross_site_scripting_(OWASP-DV-001)">reflected
XSS attacks</link>. This is by no means full proof, but does assist in XSS protection.</para>
<para>The filtering is typically enabled by default, so adding the header typically just ensures it is enabled and
instructs the browser what to do when a XSS attack is detected. For example, the filter might try to change the
content in the least invasive way to still render everything. At times, this type of replacement can become a
<link xlink:href="http://hackademix.net/2009/11/21/ies-xss-filter-creates-xss-vulnerabilities/">XSS
vulnerability in itself</link>. Instead, it is best to block the content rather than attempt to fix it. To do this we can
add the following header:</para>
<programlisting><![CDATA[X-XSS-Protection: 1; mode=block]]></programlisting>
<para>This header is included by default when the &lt;headers&gt; element is specified with no child elements. We can explicitly
state it using the <link linkend="nsa-xss-protection">xss-protection</link> element as shown below:</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<xss-protection />
</headers>
</http>]]></programlisting>
<para>Similarly, you can enable only xss protection within Java Configuration with the following:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.xssProtection();
}
}]]></programlisting>
</section>
</section>
<section xml:id="headers-custom">
<title>Custom Headers</title>
<para>Spring Security has mechanisms to make it convenient to add the more common security headers to your application. However, it also provides
hooks to enable adding custom headers.</para>
<section xml:id="headers-static">
<title>Static Headers</title>
<para>There may be times you wish to inject custom security headers into your application that are not supported out of the box. For example, perhaps
you wish to have early support for <link xlink:href="http://www.w3.org/TR/CSP/">Content Security Policy</link> in order to ensure that resources
are only loaded from the same origin. Since support for Content Security Policy has not been finalized, browsers use one of two common extension headers
to implement the feature. This means we will need to inject the policy twice. An example of the headers can be seen below:</para>
<programlisting><![CDATA[X-Content-Security-Policy: default-src 'self'
X-WebKit-CSP: default-src 'self']]></programlisting>
<para>When using the XML namespace, these headers can be added to the response using the <link linkend="nsa-header">&lt;header&gt;</link> element as
shown below:</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<header name="X-Content-Security-Policy" value="default-src 'self'"/>
<header name="X-WebKit-CSP" value="default-src 'self'"/>
</headers>
</http>]]></programlisting>
<para>Similarly, the headers could be added to the response using Java Configuration as shown in the following:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.addHeaderWriter(new StaticHeaderWriter("X-Content-Security-Policy","default-src 'self'"))
.addHeaderWriter(new StaticHeaderWriter("X-WebKit-CSP","default-src 'self'"));
}
}]]></programlisting>
</section>
<section xml:id="headers-writer">
<title>Headers Writer</title>
<para>When the namespace or Java configuration does not support the headers you want, you can create a custom <interfacename>HeadersWriter</interfacename> instance
or even provide a custom implementation of the <interfacename>HeadersWriter</interfacename>.</para>
<para>Let's take a look at an example of using an custom instance of <classname>XFrameOptionsHeaderWriter</classname>. Perhaps you want to allow framing of content
for the same origin. This is easily supported by setting the <link linkend="nsa-frame-options-policy">policy</link>
attribute to "SAMEORIGIN", but let's take a look at a more explicit example using the <link linkend="nsa-header-ref">ref</link> attribute.</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<header ref="frameOptionsWriter"/>
</headers>
</http>
<!-- Requires the c-namespace.
See http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-c-namespace
-->
<beans:bean id="frameOptionsWriter"
class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"
c:frameOptionsMode="SAMEORIGIN"/>]]></programlisting>
<para>We could also restrict framing of content to the same origin with Java configuration:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsMode.SAMEORIGIN));
}
}]]></programlisting>
</section>
<section xml:id="headers-delegatingrequestmatcherheaderwriter">
<title>DelegatingRequestMatcherHeaderWriter</title>
<para>At times you may want to only write a header for certain requests. For example, perhaps you want to only protect your log in page from being framed. You could use the
<classname>DelegatingRequestMatcherHeaderWriter</classname> to do so. When using the XML namespace configuration, this can be done with the following:</para>
<programlisting language="xml"><![CDATA[<http>
<!-- ... -->
<headers>
<header header-ref="headerWriter"/>
</headers>
</http>
<beans:bean id="headerWriter"
class="org.springframework.security.web.header.writers.DelegatingRequestMatcherHeaderWriter">
<beans:constructor-arg>
<bean class="org.springframework.security.web.util.AntPathRequestMatcher"
c:pattern="/login"/>
</beans:constructor-arg>
<beans:constructor-arg>
<beans:bean
class="org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter"/>
</beans:constructor-arg>
</beans:bean>]]></programlisting>
<para>We could also prevent framing of content to the log in page using java configuration:</para>
<programlisting language="java"><![CDATA[@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
RequestMatcher matcher = new AntPathRequestMatcher("/login");
DelegatingRequestMatcherHeaderWriter headerWriter =
new DelegatingRequestMatcherHeaderWriter(matcher,new XFrameOptionsHeaderWriter());
http
// ...
.headers()
.addHeaderWriter(headerWriter);
}
}]]></programlisting>
</section>
</section>
</chapter>

View File

@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<t:templates xmlns:t="http://nwalsh.com/docbook/xsl/template/1.0"
xmlns:param="http://nwalsh.com/docbook/xsl/template/1.0/param"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- ==================================================================== -->
<t:titlepage t:element="book" t:wrapper="div" class="titlepage">
<t:titlepage-content t:side="recto">
<title/>
<subtitle/>
<corpauthor/>
<authorgroup/>
<author/>
<mediaobject/>
<othercredit/>
<productname/>
<releaseinfo/>
<copyright/>
<pubdate/>
<revision/>
<revhistory/>
<abstract/>
</t:titlepage-content>
<t:titlepage-content t:side="verso">
<legalnotice/>
</t:titlepage-content>
<t:titlepage-separator>
<hr/>
</t:titlepage-separator>
<t:titlepage-before t:side="recto">
</t:titlepage-before>
<t:titlepage-before t:side="verso">
</t:titlepage-before>
</t:titlepage>
</t:templates>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:db="http://docbook.org/ns/docbook">
<xsl:output method="text"/>
<xsl:template match="db:interfacename|db:classname">
<xsl:variable name="classname" select="."/>
<xsl:for-each select="ancestor::*[@xml:id][1]">
<xsl:variable name="title" select="db:info/db:title|db:title"/>
<xsl:value-of select="concat($classname, ':', @xml:id, ':', $title,';')"/>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()|@*|*">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,181 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<book version="5.0" xml:id="spring-security-reference-guide" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude">
<info>
<title>Spring Security</title>
<subtitle>Reference Documentation</subtitle>
<authorgroup>
<author>
<personname>Ben Alex</personname>
</author>
<author>
<personname>Luke Taylor</personname>
</author>
</authorgroup>
<productname>Spring Security</productname>
<releaseinfo>${version}</releaseinfo>
</info>
<toc/>
<preface xml:id="preface">
<title>Preface</title>
<partintro>
<para>Spring Security provides a comprehensive security solution for J2EE-based enterprise
software applications. As you will discover as you venture through this reference guide,
we have tried to provide you a useful and highly configurable security system.</para>
<para>Security is an ever-moving target, and it's important to pursue a comprehensive,
system-wide approach. In security circles we encourage you to adopt "layers of
security", so that each layer tries to be as secure as possible in its own right, with
successive layers providing additional security. The "tighter" the security of each
layer, the more robust and safe your application will be. At the bottom level you'll
need to deal with issues such as transport security and system identification, in order
to mitigate man-in-the-middle attacks. Next you'll generally utilise firewalls, perhaps
with VPNs or IP security to ensure only authorised systems can attempt to connect. In
corporate environments you may deploy a DMZ to separate public-facing servers from
backend database and application servers. Your operating system will also play a
critical part, addressing issues such as running processes as non-privileged users and
maximising file system security. An operating system will usually also be configured
with its own firewall. Hopefully somewhere along the way you'll be trying to prevent
denial of service and brute force attacks against the system. An intrusion detection
system will also be especially useful for monitoring and responding to attacks, with
such systems able to take protective action such as blocking offending TCP/IP addresses
in real-time. Moving to the higher layers, your Java Virtual Machine will hopefully be
configured to minimize the permissions granted to different Java types, and then your
application will add its own problem domain-specific security configuration. Spring
Security makes this latter area - application security - much easier. </para>
<para>Of course, you will need to properly address all security layers mentioned above,
together with managerial factors that encompass every layer. A non-exhaustive list of
such managerial factors would include security bulletin monitoring, patching, personnel
vetting, audits, change control, engineering management systems, data backup, disaster
recovery, performance benchmarking, load monitoring, centralised logging, incident
response procedures etc.</para>
<para>With Spring Security being focused on helping you with the enterprise application
security layer, you will find that there are as many different requirements as there are
business problem domains. A banking application has different needs from an ecommerce
application. An ecommerce application has different needs from a corporate sales force
automation tool. These custom requirements make application security interesting,
challenging and rewarding. </para>
<para>Please read <xref linkend="getting-started"/>, in its entirety to begin with. This
will introduce you to the framework and the namespace-based configuration system with
which you can get up and running quite quickly. To get more of an understanding of how
Spring Security works, and some of the classes you might need to use, you should then
read <xref linkend="overall-architecture"/>. The remaining parts of this guide are
structured in a more traditional reference style, designed to be read on an as-required
basis. We'd also recommend that you read up as much as possible on application security
issues in general. Spring Security is not a panacea which will solve all security
issues. It is important that the application is designed with security in mind from the
start. Attempting to retrofit it is not a good idea. In particular, if you are building
a web application, you should be aware of the many potential vulnerabilities such as
cross-site scripting, request-forgery and session-hijacking which you should be taking
into account from the start. The OWASP web site (http://www.owasp.org/) maintains a top
ten list of web application vulnerabilities as well as a lot of useful reference
information. </para>
<para>We hope that you find this reference guide useful, and we welcome your feedback and
<link linkend="jira">suggestions</link>. </para>
<para>Finally, welcome to the Spring Security <link linkend="community"
>community</link>. </para>
</partintro>
</preface>
<part xml:id="getting-started">
<title>Getting Started</title>
<partintro>
<para>The later parts of this guide provide an in-depth discussion of the framework
architecture and implementation classes, which you need to understand if you want to
do any serious customization. In this part, we'll introduce Spring Security 3.0,
give a brief overview of the project's history and take a slightly gentler look at
how to get started using the framework. In particular, we'll look at namespace
configuration which provides a much simpler way of securing your application
compared to the traditional Spring bean approach where you have to wire up all the
implementation classes individually. </para>
<para> We'll also take a look at the sample applications that are available. It's worth
trying to run these and experimenting with them a bit even before you read the later
sections - you can dip back into them as your understanding of the framework
increases. Please also check out the <link
xlink:href="http://static.springsource.org/spring-security/site/index.html"
>project website</link> as it has useful information on building the project,
plus links to articles, videos and tutorials. </para>
</partintro>
<xi:include href="introduction.xml"/>
<xi:include href="new-3-1.xml"/>
<xi:include href="namespace-config.xml"/>
<xi:include href="samples.xml"/>
<xi:include href="community.xml"/>
</part>
<part xml:id="overall-architecture">
<title>Architecture and Implementation</title>
<partintro>
<para>Once you are familiar with setting up and running some namespace-configuration
based applications, you may wish to develop more of an understanding of how the
framework actually works behind the namespace facade. Like most software, Spring
Security has certain central interfaces, classes and conceptual abstractions that
are commonly used throughout the framework. In this part of the reference guide we
will look at some of these and see how they work together to support authentication
and access-control within Spring Security.</para>
</partintro>
<xi:include href="technical-overview.xml"/>
<xi:include href="core-services.xml"/>
</part>
<part xml:id="web-app-security">
<title>Web Application Security</title>
<partintro>
<para> Most Spring Security users will be using the framework in applications which make
user of HTTP and the Servlet API. In this part, we'll take a look at how Spring
Security provides authentication and access-control features for the web layer of an
application. We'll look behind the facade of the namespace and see which classes and
interfaces are actually assembled to provide web-layer security. In some situations
it is necessary to use traditional bean configuration to provide full control over
the configuration, so we'll also see how to configure these classes directly without
the namespace.</para>
</partintro>
<xi:include href="security-filter-chain.xml"/>
<xi:include href="core-filters.xml"/>
<xi:include href="servlet-api.xml"/>
<xi:include href="basic-and-digest-auth.xml"/>
<xi:include href="remember-me-authentication.xml"/>
<xi:include href="csrf.xml"/>
<xi:include href="headers.xml"/>
<xi:include href="session-mgmt.xml"/>
<xi:include href="anon-auth-provider.xml"/>
</part>
<part xml:id="authorization">
<title>Authorization</title>
<partintro>
<para>The advanced authorization capabilities within Spring Security represent one of
the most compelling reasons for its popularity. Irrespective of how you choose to
authenticate - whether using a Spring Security-provided mechanism and provider, or
integrating with a container or other non-Spring Security authentication authority -
you will find the authorization services can be used within your application in a
consistent and simple way.</para>
<para>In this part we'll explore the different
<classname>AbstractSecurityInterceptor</classname> implementations, which were
introduced in Part I. We then move on to explore how to fine-tune authorization
through use of domain access control lists.</para>
</partintro>
<xi:include href="authorization-common.xml"/>
<xi:include href="secured-objects.xml"/>
<xi:include href="el-access.xml"/>
</part>
<part xml:id="advanced-topics">
<title>Additional Topics</title>
<!--
Essentially standalone features which do not have to follow on directly from earlier chapters
-->
<partintro>
<para> In this part we cover features which require a knowledge of previous chapters as
well as some of the more advanced and less-commonly used features of the
framework.</para>
</partintro>
<xi:include href="domain-acls.xml"/>
<xi:include href="preauth.xml"/>
<xi:include href="ldap-auth-provider.xml"/>
<xi:include href="taglibs.xml"/>
<xi:include href="jaas-auth-provider.xml"/>
<xi:include href="cas-auth-provider.xml"/>
<xi:include href="x509-auth-provider.xml"/>
<xi:include href="runas-auth-provider.xml"/>
<xi:include href="crypto.xml"/>
</part>
<xi:include href="appendix-db-schema.xml"/>
<xi:include href="appendix-namespace.xml"/>
<xi:include href="appendix-dependencies.xml"/>
</book>

View File

@ -1,308 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5.0" xml:id="introduction" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Introduction</title>
<section xml:id="what-is-acegi-security">
<title>What is Spring Security?</title>
<para>Spring Security provides comprehensive security services for J2EE-based enterprise
software applications. There is a particular emphasis on supporting projects built using
The Spring Framework, which is the leading J2EE solution for enterprise software
development. If you're not using Spring for developing enterprise applications, we
warmly encourage you to take a closer look at it. Some familiarity with Spring - and in
particular dependency injection principles - will help you get up to speed with Spring
Security more easily.</para>
<para>People use Spring Security for many reasons, but most are drawn to the project after
finding the security features of J2EE's Servlet Specification or EJB Specification lack
the depth required for typical enterprise application scenarios. Whilst mentioning these
standards, it's important to recognise that they are not portable at a WAR or EAR level.
Therefore, if you switch server environments, it is typically a lot of work to
reconfigure your application's security in the new target environment. Using Spring
Security overcomes these problems, and also brings you dozens of other useful,
customisable security features.</para>
<para>As you probably know two major areas of application security are
<quote>authentication</quote> and <quote>authorization</quote> (or
<quote>access-control</quote>). These are the two main areas that Spring Security
targets. <quote>Authentication</quote> is the process of establishing a principal is who
they claim to be (a <quote>principal</quote> generally means a user, device or some
other system which can perform an action in your application).
<quote>Authorization</quote> refers to the process of deciding whether a principal is
allowed to perform an action within your application. To arrive at the point where an
authorization decision is needed, the identity of the principal has already been
established by the authentication process. These concepts are common, and not at all
specific to Spring Security. </para>
<para>At an authentication level, Spring Security supports a wide range of authentication
models. Most of these authentication models are either provided by third parties, or are
developed by relevant standards bodies such as the Internet Engineering Task Force. In
addition, Spring Security provides its own set of authentication features. Specifically,
Spring Security currently supports authentication integration with all of these
technologies:</para>
<itemizedlist spacing="compact">
<listitem>
<para>HTTP BASIC authentication headers (an IETF RFC-based standard)</para>
</listitem>
<listitem>
<para>HTTP Digest authentication headers (an IETF RFC-based standard)</para>
</listitem>
<listitem>
<para>HTTP X.509 client certificate exchange (an IETF RFC-based standard)</para>
</listitem>
<listitem>
<para>LDAP (a very common approach to cross-platform authentication needs,
especially in large environments)</para>
</listitem>
<listitem>
<para>Form-based authentication (for simple user interface needs)</para>
</listitem>
<listitem>
<para>OpenID authentication</para>
</listitem>
<listitem>
<para>Authentication based on pre-established request headers (such as Computer
Associates Siteminder)</para>
</listitem>
<listitem>
<para>JA-SIG Central Authentication Service (otherwise known as CAS, which is a
popular open source single sign-on system)</para>
</listitem>
<listitem>
<para>Transparent authentication context propagation for Remote Method Invocation
(RMI) and HttpInvoker (a Spring remoting protocol)</para>
</listitem>
<listitem>
<para>Automatic "remember-me" authentication (so you can tick a box to avoid
re-authentication for a predetermined period of time)</para>
</listitem>
<listitem>
<para>Anonymous authentication (allowing every unauthenticated call to automatically assume a
particular security identity)</para>
</listitem>
<listitem>
<para>Run-as authentication (which is useful if one call should proceed with a
different security identity)</para>
</listitem>
<listitem>
<para>Java Authentication and Authorization Service (JAAS)</para>
</listitem>
<listitem>
<para>JEE container autentication (so you can still use Container Managed
Authentication if desired)</para>
</listitem>
<listitem>
<para>Kerberos</para>
</listitem>
<listitem>
<para>Java Open Source Single Sign On (JOSSO) *</para>
</listitem>
<listitem>
<para>OpenNMS Network Management Platform *</para>
</listitem>
<listitem>
<para>AppFuse *</para>
</listitem>
<listitem>
<para>AndroMDA *</para>
</listitem>
<listitem>
<para>Mule ESB *</para>
</listitem>
<listitem>
<para>Direct Web Request (DWR) *</para>
</listitem>
<listitem>
<para>Grails *</para>
</listitem>
<listitem>
<para>Tapestry *</para>
</listitem>
<listitem>
<para>JTrac *</para>
</listitem>
<listitem>
<para>Jasypt *</para>
</listitem>
<listitem>
<para>Roller *</para>
</listitem>
<listitem>
<para>Elastic Path *</para>
</listitem>
<listitem>
<para>Atlassian Crowd *</para>
</listitem>
<listitem>
<para>Your own authentication systems (see below)</para>
</listitem>
</itemizedlist>
<para>(* Denotes provided by a third party</para>
<!-- TODO: Reinstate web link to third-party integrations/users -->
<para>Many independent software vendors (ISVs) adopt Spring Security because of this
significant choice of flexible authentication models. Doing so allows them to quickly
integrate their solutions with whatever their end clients need, without undertaking a
lot of engineering or requiring the client to change their environment. If none of the
above authentication mechanisms suit your needs, Spring Security is an open platform and
it is quite simple to write your own authentication mechanism. Many corporate users of
Spring Security need to integrate with "legacy" systems that don't follow any particular
security standards, and Spring Security is happy to "play nicely" with such
systems.</para>
<para>Irrespective of the authentication mechanism, Spring Security provides a deep set
of authorization capabilities. There are three main areas of interest
- authorizing web requests, authorizing whether methods can be
invoked, and authorizing access to individual domain object instances. To help you
understand the differences, consider the authorization capabilities found in the Servlet
Specification web pattern security, EJB Container Managed Security and file system
security respectively. Spring Security provides deep capabilities in all of these
important areas, which we'll explore later in this reference guide.</para>
</section>
<section xml:id="history">
<title>History</title>
<para>Spring Security began in late 2003 as <quote>The Acegi Security System for
Spring</quote>. A question was posed on the Spring Developers' mailing list asking
whether there had been any consideration given to a Spring-based security
implementation. At the time the Spring community was relatively small (especially
compared with the size today!), and indeed Spring itself had only existed as a
SourceForge project from early 2003. The response to the question was that it was a
worthwhile area, although a lack of time currently prevented its exploration.</para>
<para>With that in mind, a simple security implementation was built and not released. A few
weeks later another member of the Spring community inquired about security, and at the
time this code was offered to them. Several other requests followed, and by January 2004
around twenty people were using the code. These pioneering users were joined by others
who suggested a SourceForge project was in order, which was duly established in March
2004.</para>
<para>In those early days, the project didn't have any of its own authentication modules.
Container Managed Security was relied upon for the authentication process, with Acegi
Security instead focusing on authorization. This was suitable at first, but as more and
more users requested additional container support, the fundamental limitation of
container-specific authentication realm interfaces became clear. There was also a
related issue of adding new JARs to the container's classpath, which was a common source
of end user confusion and misconfiguration.</para>
<para>Acegi Security-specific authentication services were subsequently introduced. Around a
year later, Acegi Security became an official Spring Framework subproject. The 1.0.0
final release was published in May 2006 - after more than two and a half years of active
use in numerous production software projects and many hundreds of improvements and
community contributions.</para>
<para>Acegi Security became an official Spring Portfolio project towards the end of 2007 and
was rebranded as <quote>Spring Security</quote>.</para>
<para>Today Spring Security enjoys a strong and active open source community. There are
thousands of messages about Spring Security on the support forums. There is an active
core of developers who work on the code itself and an active community which also
regularly share patches and support their peers.</para>
</section>
<section xml:id="release-numbering">
<title>Release Numbering</title>
<para>It is useful to understand how Spring Security release numbers work, as it will help
you identify the effort (or lack thereof) involved in migrating to future releases of
the project. Each release uses a standard triplet of integers: MAJOR.MINOR.PATCH. The
intent is that MAJOR versions are incompatible, large-scale upgrades of the API. MINOR
versions should largely retain source and binary compatibility with older minor
versions, thought there may be some design changes and incompatible udates. PATCH level
should be perfectly compatible, forwards and backwards, with the possible exception of
changes which are to fix bugs and defects.</para>
<para>The extent to which you are affected by changes will depend on how tightly integrated
your code is. If you are doing a lot of customization you are more likely to be affected
than if you are using a simple namespace configuration.</para>
<para>You should always test your application thoroughly before rolling out a new
version.</para>
</section>
<section xml:id="get-spring-security">
<title>Getting Spring Security</title>
<para>You can get hold of Spring Security in several ways. You can download a packaged
distribution from the main Spring <link
xlink:href="http://www.springsource.com/download/community?project=Spring%20Security"
>download page</link>, download individual jars (and sample WAR files) from the Maven
Central repository (or a SpringSource Maven repository for snapshot and milestone
releases) or, alternatively, you can build the project from source yourself. See the
project web site for more details. </para>
<section xml:id="modules">
<title>Project Modules</title>
<para>In Spring Security 3.0, the codebase has been sub-divided into separate jars which
more clearly separate different functionaltiy areas and third-party dependencies. If
you are using Maven to build your project, then these are the modules you will add
to your <filename>pom.xml</filename>. Even if you're not using Maven, we'd recommend
that you consult the <filename>pom.xml</filename> files to get an idea of
third-party dependencies and versions. Alternatively, a good idea is to examine the
libraries that are included in the sample applications.</para>
<section xml:id="spring-security-core">
<title>Core - <literal>spring-security-core.jar</literal></title>
<para>Contains core authentication and access-contol classes and interfaces,
remoting support and basic provisioning APIs. Required by any application which
uses Spring Security. Supports standalone applications, remote clients, method
(service layer) security and JDBC user provisioning. Contains the top-level packages:<itemizedlist>
<listitem>
<para><literal>org.springframework.security.core</literal></para>
</listitem>
<listitem>
<para><literal>org.springframework.security.access</literal></para>
</listitem>
<listitem>
<para><literal>org.springframework.security.authentication</literal></para>
</listitem>
<listitem>
<para><literal>org.springframework.security.provisioning</literal></para>
</listitem>
</itemizedlist></para>
</section>
<section xml:id="spring-security-remoting">
<title>Remoting - <literal>spring-security-remoting.jar</literal></title>
<para>Provides intergration with Spring Remoting. You don't need this unless you are
writing a remote client which uses Spring Remoting. The main package is
<literal>org.springframework.security.remoting</literal>.</para>
</section>
<section xml:id="spring-security-web">
<title>Web - <literal>spring-security-web.jar</literal></title>
<para>Contains filters and related web-security infrastructure code. Anything with a
servlet API dependency. You'll need it if you require Spring Security web
authentication services and URL-based access-control. The main package is
<literal>org.springframework.security.web</literal>.</para>
</section>
<section xml:id="spring-security-config">
<title>Config - <literal>spring-security-config.jar</literal></title>
<para>Contains the security namespace parsing code. You need it if you are using the
Spring Security XML namespace for configuration. The main package is
<literal>org.springframework.security.config</literal>. None of the
classes are intended for direct use in an application.</para>
</section>
<section xml:id="spring-security-ldap">
<title>LDAP - <literal>spring-security-ldap.jar</literal></title>
<para>LDAP authentication and provisioning code. Required if you need to use LDAP
authentication or manage LDAP user entries. The top-level package is
<literal>org.springframework.security.ldap</literal>.</para>
</section>
<section xml:id="spring-security-acl">
<title>ACL - <literal>spring-security-acl.jar</literal></title>
<para>Specialized domain object ACL implementation. Used to apply security to
specific domain object instances within your application. The top-level package
is <literal>org.springframework.security.acls</literal>.</para>
</section>
<section xml:id="spring-security-cas">
<title>CAS - <literal>spring-security-cas.jar</literal></title>
<para>Spring Security's CAS client integration. If you want to use Spring Security
web authentication with a CAS single sign-on server. The top-level package is
<literal>org.springframework.security.cas</literal>.</para>
</section>
<section xml:id="spring-security-openid">
<title>OpenID - <literal>spring-security-openid.jar</literal></title>
<para>OpenID web authentication support. Used to authenticate users against an
external OpenID server. <literal>org.springframework.security.openid</literal>.
Requires OpenID4Java.</para>
</section>
</section>
<section xml:id="get-source">
<title>Checking out the Source</title>
<para> Since Spring Security is an Open Source project, we'd strongly encourage you to
check out the source code using git. This will give you full access to all the
sample applications and you can build the most up to date version of the project
easily. Having the source for a project is also a huge help in debugging. Exception
stack traces are no longer obscure black-box issues but you can get straight to the
line that's causing the problem and work out what's happening. The source is the
ultimate documentation for a project and often the simplest place to find out how
something actually works. </para>
<para>To obtain the source for the project, use the following git command:
<programlisting language="txt">
git clone git://git.springsource.org/spring-security/spring-security.git
</programlisting>
</para>
<para>This will give you access to the entire project history (including all releases
and branches) on your local machine.</para>
</section>
</section>
</chapter>

View File

@ -1,232 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="jaas">
<info>
<title>Java Authentication and Authorization Service (JAAS) Provider</title>
</info>
<section xml:aid="jaas-overview">
<info>
<title>Overview</title>
</info>
<para>Spring Security provides a package able to delegate authentication requests to the
Java Authentication and Authorization Service (JAAS). This package is discussed in
detail below.</para>
</section>
<section xml:id="jaas-abstractjaasauthenticationprovider">
<info>
<title>AbstractJaasAuthenticationProvider</title>
</info>
<para>The <classname>AbstractJaasAuthenticationProvider</classname> is the basis for the
provided JAAS <interfacename>AuthenticationProvider</interfacename> implementations. Subclasses
must implement a method that creates the <classname>LoginContext</classname>. The
<classname>AbstractJaasAuthenticationProvider</classname> has a number of dependencies that can
be injected into it that are discussed below.</para>
<section xml:id="jaas-callbackhandler">
<info>
<title xml:id="jaas-callback-handler">JAAS CallbackHandler</title>
</info>
<para>Most JAAS <literal>LoginModule</literal>s require a callback of some sort. These
callbacks are usually used to obtain the username and password from the user.</para>
<para>In a Spring Security deployment, Spring Security is responsible for this user
interaction (via the authentication mechanism). Thus, by the time the authentication
request is delegated through to JAAS, Spring Security's authentication mechanism
will already have fully-populated an <interfacename>Authentication</interfacename>
object containing all the information required by the JAAS
<literal>LoginModule</literal>.</para>
<para>Therefore, the JAAS package for Spring Security provides two default callback
handlers, <literal>JaasNameCallbackHandler</literal> and
<literal>JaasPasswordCallbackHandler</literal>. Each of these callback handlers
implement <literal>JaasAuthenticationCallbackHandler</literal>. In most cases these
callback handlers can simply be used without understanding the internal
mechanics.</para>
<para>For those needing full control over the callback behavior, internally
<classname>AbstractJaasAuthenticationProvider</classname> wraps these
<literal>JaasAuthenticationCallbackHandler</literal>s with an
<literal>InternalCallbackHandler</literal>. The
<literal>InternalCallbackHandler</literal> is the class that actually implements
JAAS normal <literal>CallbackHandler</literal> interface. Any time that the JAAS
<literal>LoginModule</literal> is used, it is passed a list of application context
configured <literal>InternalCallbackHandler</literal>s. If the
<literal>LoginModule</literal> requests a callback against the
<literal>InternalCallbackHandler</literal>s, the callback is in-turn passed to the
<literal>JaasAuthenticationCallbackHandler</literal>s being wrapped.</para>
</section>
<section xml:id="jaas-authoritygranter">
<info>
<title xml:id="jaas-authority-granter">JAAS AuthorityGranter</title>
</info>
<para>JAAS works with principals. Even "roles" are represented as principals in JAAS.
Spring Security, on the other hand, works with
<interfacename>Authentication</interfacename> objects. Each
<interfacename>Authentication</interfacename> object contains a single principal,
and multiple <interfacename>GrantedAuthority</interfacename>s. To facilitate
mapping between these different concepts, Spring Security's JAAS package includes an
<literal>AuthorityGranter</literal> interface.</para>
<para>An <literal>AuthorityGranter</literal> is responsible for inspecting a JAAS
principal and returning a set of <literal>String</literal>s, representing the
authorities assigned to the principal. For each returned authority string, the
<classname>AbstractJaasAuthenticationProvider</classname> creates a
<classname>JaasGrantedAuthority</classname> (which implements Spring Securitys
<interfacename>GrantedAuthority</interfacename> interface) containing the authority
string and the JAAS principal that the
<interfacename>AuthorityGranter</interfacename> was passed. The
<classname>AbstractJaasAuthenticationProvider</classname> obtains the JAAS principals by
firstly successfully authenticating the users credentials using the JAAS
<literal>LoginModule</literal>, and then accessing the
<literal>LoginContext</literal> it returns. A call to
<literal>LoginContext.getSubject().getPrincipals()</literal> is made, with each
resulting principal passed to each <interfacename>AuthorityGranter</interfacename>
defined against the
<literal>AbstractJaasAuthenticationProvider.setAuthorityGranters(List)</literal>
property.</para>
<para>Spring Security does not include any production
<interfacename>AuthorityGranter</interfacename>s given that every JAAS principal has
an implementation-specific meaning. However, there is a
<literal>TestAuthorityGranter</literal> in the unit tests that demonstrates a simple
<literal>AuthorityGranter</literal> implementation.</para>
</section>
</section>
<section xml:id="jaas-defaultjaasauthenticationprovider">
<info>
<title>DefaultJaasAuthenticationProvider</title>
</info>
<para>The <classname>DefaultJaasAuthenticationProvider</classname> allows a JAAS
<classname>Configuration</classname> object to be injected into it as a dependency. It then
creates a <classname>LoginContext</classname> using the injected JAAS <classname>Configuration</classname>.
This means that <classname>DefaultJaasAuthenticationProvider</classname> is not bound any particular implementation
of <classname>Configuration</classname> as <classname>JaasAuthenticationProvider</classname> is.</para>
<section xml:id="jaas-inmemoryconfiguration">
<info>
<title>InMemoryConfiguration</title>
</info>
<para>In order to make it easy to inject a <classname>Configuration</classname> into
<classname>DefaultJaasAuthenticationProvider</classname>, a default in memory
implementation named <classname>InMemoryConfiguration</classname> is provided. The
implementation constructor accepts a <interfacename>Map</interfacename> where each key represents a
login configuration name and the value represents an <classname>Array</classname> of
<classname>AppConfigurationEntry</classname>s.
<classname>InMemoryConfiguration</classname> also supports a default
<classname>Array</classname> of <classname>AppConfigurationEntry</classname> objects that
will be used if no mapping is found within the provided <interfacename>Map</interfacename>. For
details, refer to the class level javadoc of <classname>InMemoryConfiguration</classname>.</para>
</section>
<section xml:id="jaas-djap-config">
<info>
<title>DefaultJaasAuthenticationProvider Example Configuration</title>
</info>
<para>While the Spring configuration for <classname>InMemoryConfiguration</classname> can be
more verbose than the standarad JAAS configuration files, using it in conjuction with
<classname>DefaultJaasAuthenticationProvider</classname> is more flexible than
<classname>JaasAuthenticationProvider</classname> since it not dependant on the default
<classname>Configuration</classname> implementation.</para>
<para>An example configuration of <classname>DefaultJaasAuthenticationProvider</classname> using
<classname>InMemoryConfiguration</classname> is provided below. Note that custom implementations of
<classname>Configuration</classname> can easily be injected into
<classname>DefaultJaasAuthenticationProvider</classname> as well.</para>
<programlisting language="xml"><![CDATA[
<bean id="jaasAuthProvider"
class="org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider">
<property name="configuration">
<bean class="org.springframework.security.authentication.jaas.memory.InMemoryConfiguration">
<constructor-arg>
<map>
<!--
SPRINGSECURITY is the default loginContextName
for AbstractJaasAuthenticationProvider
-->
<entry key="SPRINGSECURITY">
<array>
<bean class="javax.security.auth.login.AppConfigurationEntry">
<constructor-arg value="sample.SampleLoginModule" />
<constructor-arg>
<util:constant static-field=
"javax.security.auth.login.AppConfigurationEntry$LoginModuleControlFlag.REQUIRED"/>
</constructor-arg>
<constructor-arg>
<map></map>
</constructor-arg>
</bean>
</array>
</entry>
</map>
</constructor-arg>
</bean>
</property>
<property name="authorityGranters">
<list>
<!-- You will need to write your own implementation of AuthorityGranter -->
<bean class="org.springframework.security.authentication.jaas.TestAuthorityGranter"/>
</list>
</property>
</bean>
]]></programlisting>
</section>
</section>
<section xml:id="jaas-jaasauthenticationprovider">
<info>
<title>JaasAuthenticationProvider</title>
</info>
<para>The <classname>JaasAuthenticationProvider</classname> assumes the default <classname>Configuration</classname> is an instance of
<link xlink:href="http://download.oracle.com/javase/1.4.2/docs/guide/security/jaas/spec/com/sun/security/auth/login/ConfigFile.html">
ConfigFile</link>. This assumption is made in order to attempt to update the <classname>Configuration</classname>. The
<classname>JaasAuthenticationProvider</classname> then uses the default <classname>Configuration</classname> to create the
<classname>LoginContext</classname>.</para>
<para>Lets assume we have a JAAS login configuration file,
<literal>/WEB-INF/login.conf</literal>, with the following contents:
<programlisting language="txt">
JAASTest {
sample.SampleLoginModule required;
};</programlisting></para>
<para>Like all Spring Security beans, the <classname>JaasAuthenticationProvider</classname>
is configured via the application context. The following definitions would correspond to
the above JAAS login configuration file: <programlisting language="xml"><![CDATA[
<bean id="jaasAuthenticationProvider"
class="org.springframework.security.authentication.jaas.JaasAuthenticationProvider">
<property name="loginConfig" value="/WEB-INF/login.conf"/>
<property name="loginContextName" value="JAASTest"/>
<property name="callbackHandlers">
<list>
<bean
class="org.springframework.security.authentication.jaas.JaasNameCallbackHandler"/>
<bean
class="org.springframework.security.authentication.jaas.JaasPasswordCallbackHandler"/>
</list>
</property>
<property name="authorityGranters">
<list>
<bean class="org.springframework.security.authentication.jaas.TestAuthorityGranter"/>
</list>
</property>
</bean>
]]></programlisting></para>
</section>
<section xml:id="jaas-apiprovision">
<info>
<title xml:id="jaas-api-provision">Running as a Subject</title>
</info>
<para>If configured, the <classname>JaasApiIntegrationFilter</classname> will attempt to
run as the <literal>Subject</literal> on the
<classname>JaasAuthenticationToken</classname>. This means that the
<literal>Subject</literal> can be accessed using:
<programlisting language="java"><![CDATA[
Subject subject = Subject.getSubject(AccessController.getContext());
]]></programlisting>
This integration can easily be configured using the
<link linkend="nsa-http-jaas-api-provision">jaas-api-provision</link> attribute. This
feature is useful when integrating with legacy or external API's that rely on the
JAAS Subject being populated.</para>
</section>
</chapter>

View File

@ -1,432 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="ldap">
<info>
<title>LDAP Authentication</title>
</info>
<section xml:id="ldap-overview">
<info>
<title>Overview</title>
</info>
<para>LDAP is often used by organizations as a central repository for user information and
as an authentication service. It can also be used to store the role information for
application users.</para>
<para>There are many different scenarios for how an LDAP server may be configured so Spring
Security's LDAP provider is fully configurable. It uses separate strategy interfaces for
authentication and role retrieval and provides default implementations which can be
configured to handle a wide range of situations.</para>
<para>You should be familiar with LDAP before trying to use it with Spring Security. The
following link provides a good introduction to the concepts involved and a guide to
setting up a directory using the free LDAP server OpenLDAP: <uri
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="http://www.zytrax.com/books/ldap/">http://www.zytrax.com/books/ldap/</uri>.
Some familiarity with the JNDI APIs used to access LDAP from Java may also be useful. We
don't use any third-party LDAP libraries (Mozilla, JLDAP etc.) in the LDAP provider, but
extensive use is made of Spring LDAP, so some familiarity with that project may be
useful if you plan on adding your own customizations.</para>
<para>When using LDAP authentication, it is important to ensure that you configure LDAP connection
pooling properly. If you are unfamiliar with how to do this, you can refer to the
<uri xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="http://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html">Java LDAP
documentation</uri>.</para>
</section>
<section>
<info>
<title>Using LDAP with Spring Security</title>
</info>
<para> LDAP authentication in Spring Security can be roughly divided into the following
stages. <orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>Obtaining the unique LDAP <quote>Distinguished Name</quote>, or DN, from the
login name. This will often mean performing a search in the directory, unless
the exact mapping of usernames to DNs is known in advance. So a user might enter
the name <quote>joe</quote> when logging in, but the actual name used to
authenticate to LDAP will be the full DN, such as
<literal>uid=joe,ou=users,dc=springsource,dc=com</literal>.</para>
</listitem>
<listitem>
<para>Authenticating the user, either by <quote>binding</quote> as that user or by
performing a remote <quote>compare</quote> operation of the user's password
against the password attribute in the directory entry for the DN.</para>
</listitem>
<listitem>
<para>Loading the list of authorities for the user.</para>
</listitem>
</orderedlist> The exception is when the LDAP directory is just being used to retrieve
user information and authenticate against it locally. This may not be possible as
directories are often set up with limited read access for attributes such as user
passwords. </para>
<para> We will look at some configuration scenarios below. For full information on available
configuration options, please consult the security namespace schema (information from
which should be available in your XML editor). </para>
</section>
<section xml:id="ldap-server">
<info>
<title>Configuring an LDAP Server</title>
</info>
<para> The first thing you need to do is configure the server against which authentication
should take place. This is done using the <literal>&lt;ldap-server&gt;</literal> element
from the security namespace. This can be configured to point at an external LDAP server,
using the <literal>url</literal> attribute: <programlisting language="xml"><![CDATA[
<ldap-server url="ldap://springframework.org:389/dc=springframework,dc=org" />
]]>
</programlisting></para>
<section>
<info>
<title>Using an Embedded Test Server</title>
</info>
<para> The <literal>&lt;ldap-server&gt;</literal> element can also be used to create an
embedded server, which can be very useful for testing and demonstrations. In this
case you use it without the <literal>url</literal> attribute: <programlisting language="xml"><![CDATA[
<ldap-server root="dc=springframework,dc=org"/>
]]>
</programlisting> Here we've specified that the root DIT of the directory should be
<quote>dc=springframework,dc=org</quote>, which is the default. Used this way, the
namespace parser will create an embedded Apache Directory server and scan the
classpath for any LDIF files, which it will attempt to load into the server. You can
customize this behaviour using the <literal>ldif</literal> attribute, which defines
an LDIF resource to be loaded: <programlisting language="xml"><![CDATA[
<ldap-server ldif="classpath:users.ldif" />
]]></programlisting> This makes it a lot easier to get up and running with LDAP, since it
can be inconvenient to work all the time with an external server. It also insulates
the user from the complex bean configuration needed to wire up an Apache Directory
server. Using plain Spring Beans the configuration would be much more cluttered. You
must have the necessary Apache Directory dependency jars available for your
application to use. These can be obtained from the LDAP sample application. </para>
</section>
<section>
<info>
<title>Using Bind Authentication</title>
</info>
<para> This is the most common LDAP authentication scenario. <programlisting language="xml"><![CDATA[
<ldap-authentication-provider user-dn-pattern="uid={0},ou=people"/>
]]></programlisting> This simple example would obtain the DN for the user by
substituting the user login name in the supplied pattern and attempting to bind as
that user with the login password. This is OK if all your users are stored under a
single node in the directory. If instead you wished to configure an LDAP search
filter to locate the user, you could use the following: <programlisting language="xml"><![CDATA[
<ldap-authentication-provider user-search-filter="(uid={0})"
user-search-base="ou=people"/>
]]></programlisting> If used with the server definition above, this would
perform a search under the DN <literal>ou=people,dc=springframework,dc=org</literal>
using the value of the <literal>user-search-filter</literal> attribute as a filter.
Again the user login name is substituted for the parameter in the filter name, so it
will search for an entry with the <literal>uid</literal> attribute equal to the user
name. If <literal>user-search-base</literal> isn't supplied, the search will be
performed from the root. </para>
</section>
<section>
<info>
<title>Loading Authorities</title>
</info>
<para> How authorities are loaded from groups in the LDAP directory is controlled by the
following attributes. <itemizedlist>
<listitem>
<para> <literal>group-search-base</literal>. Defines the part of the directory
tree under which group searches should be performed.</para>
</listitem>
<listitem>
<para> <literal>group-role-attribute</literal>. The attribute which contains the
name of the authority defined by the group entry. Defaults to
<literal>cn</literal> </para>
</listitem>
<listitem>
<para> <literal>group-search-filter</literal>. The filter which is used to
search for group membership. The default is
<literal>uniqueMember={0}</literal>, corresponding to the
<literal>groupOfUniqueNames</literal> LDAP class <footnote><para>
Note that this is different from the default configuration of the
underlying <classname>DefaultLdapAuthoritiesPopulator</classname>
which uses <literal>member={0}</literal>.
</para></footnote>.
In this case, the substituted parameter is the full distinguished name
of the user. The parameter <literal>{1}</literal> can be used if you
want to filter on the login name.</para>
</listitem>
</itemizedlist> So if we used the following configuration <programlisting language="xml"><![CDATA[
<ldap-authentication-provider user-dn-pattern="uid={0},ou=people"
group-search-base="ou=groups" />
]]></programlisting> and authenticated successfully as user <quote>ben</quote>, the subsequent
loading of authorities would perform a search under the directory entry
<literal>ou=groups,dc=springframework,dc=org</literal>, looking for entries which
contain the attribute <literal>uniqueMember</literal> with value
<literal>uid=ben,ou=people,dc=springframework,dc=org</literal>. By default the
authority names will have the prefix <literal>ROLE_</literal> prepended. You can
change this using the <literal>role-prefix</literal> attribute. If you don't want
any prefix, use <literal>role-prefix="none"</literal>. For more information on
loading authorities, see the Javadoc for the
<classname>DefaultLdapAuthoritiesPopulator</classname> class. </para>
</section>
</section>
<section>
<info>
<title>Implementation Classes</title>
</info>
<para>The namespace configuration options we've used above are simple to use and much more
concise than using Spring beans explicitly. There are situations when you may need to
know how to configure Spring Security LDAP directly in your application context. You may
wish to customize the behaviour of some of the classes, for example. If you're happy
using namespace configuration then you can skip this section and the next one. </para>
<para> The main LDAP provider class, <classname>LdapAuthenticationProvider</classname>,
doesn't actually do much itself but delegates the work to two other beans, an
<interfacename>LdapAuthenticator</interfacename> and an
<interfacename>LdapAuthoritiesPopulator</interfacename> which are responsible for
authenticating the user and retrieving the user's set of
<interfacename>GrantedAuthority</interfacename>s respectively.</para>
<section xml:id="ldap-ldap-authenticators">
<info>
<title>LdapAuthenticator Implementations</title>
</info>
<para>The authenticator is also responsible for retrieving any required user attributes.
This is because the permissions on the attributes may depend on the type of
authentication being used. For example, if binding as the user, it may be necessary
to read them with the user's own permissions.</para>
<para>There are currently two authentication strategies supplied with Spring Security: <itemizedlist>
<listitem>
<para>Authentication directly to the LDAP server ("bind" authentication).</para>
</listitem>
<listitem>
<para>Password comparison, where the password supplied by the user is compared
with the one stored in the repository. This can either be done by retrieving
the value of the password attribute and checking it locally or by performing
an LDAP "compare" operation, where the supplied password is passed to the
server for comparison and the real password value is never retrieved.</para>
</listitem>
</itemizedlist></para>
<section xml:id="ldap-ldap-authenticators-common">
<info>
<title>Common Functionality</title>
</info>
<para>Before it is possible to authenticate a user (by either strategy), the
distinguished name (DN) has to be obtained from the login name supplied to the
application. This can be done either by simple pattern-matching (by setting the
<property>setUserDnPatterns</property> array property) or by setting the
<property>userSearch</property> property. For the DN pattern-matching approach,
a standard Java pattern format is used, and the login name will be substituted
for the parameter <parameter>{0}</parameter>. The pattern should be relative to
the DN that the configured
<interfacename>SpringSecurityContextSource</interfacename> will bind to (see the
section on <link linkend="ldap-context-source">connecting to the LDAP
server</link> for more information on this). For example, if you are using an
LDAP server with the URL
<literal>ldap://monkeymachine.co.uk/dc=springframework,dc=org</literal>, and
have a pattern <literal>uid={0},ou=greatapes</literal>, then a login name of
"gorilla" will map to a DN
<literal>uid=gorilla,ou=greatapes,dc=springframework,dc=org</literal>. Each
configured DN pattern will be tried in turn until a match is found. For
information on using a search, see the section on <link
linkend="ldap-searchobjects">search objects</link> below. A combination of the
two approaches can also be used - the patterns will be checked first and if no
matching DN is found, the search will be used.</para>
</section>
<section xml:id="ldap-ldap-authenticators-bind">
<info>
<title>BindAuthenticator</title>
</info>
<para>The class <classname>BindAuthenticator</classname> in the package
<filename>org.springframework.security.ldap.authentication</filename> implements
the bind authentication strategy. It simply attempts to bind as the user.</para>
</section>
<section xml:id="ldap-ldap-authenticators-password">
<info>
<title>PasswordComparisonAuthenticator</title>
</info>
<para>The class <classname>PasswordComparisonAuthenticator</classname> implements
the password comparison authentication strategy.</para>
</section>
</section>
<section xml:id="ldap-context-source">
<info>
<title>Connecting to the LDAP Server</title>
</info>
<para>The beans discussed above have to be able to connect to the server. They both have
to be supplied with a <interfacename>SpringSecurityContextSource</interfacename>
which is an extension of Spring LDAP's <interfacename>ContextSource</interfacename>.
Unless you have special requirements, you will usually configure a
<classname>DefaultSpringSecurityContextSource</classname> bean, which can be
configured with the URL of your LDAP server and optionally with the username and
password of a "manager" user which will be used by default when binding to the
server (instead of binding anonymously). For more information read the Javadoc for
this class and for Spring LDAP's <classname>AbstractContextSource</classname>.
</para>
</section>
<section xml:id="ldap-searchobjects">
<info>
<title>LDAP Search Objects</title>
</info>
<para>Often a more complicated strategy than simple DN-matching is required to locate a
user entry in the directory. This can be encapsulated in an
<interfacename>LdapUserSearch</interfacename> instance which can be supplied to the
authenticator implementations, for example, to allow them to locate a user. The
supplied implementation is <classname>FilterBasedLdapUserSearch</classname>.</para>
<section xml:id="ldap-searchobjects-filter">
<info>
<title xml:id="ldap-searchobjects-filter-based">
<classname>FilterBasedLdapUserSearch</classname> </title>
</info>
<para>This bean uses an LDAP filter to match the user object in the directory. The
process is explained in the Javadoc for the corresponding search method on the
<link xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="http://java.sun.com/j2se/1.4.2/docs/api/javax/naming/directory/DirContext.html#search(javax.naming.Name,%20java.lang.String,%20java.lang.Object[],%20javax.naming.directory.SearchControls)"
>JDK DirContext class</link>. As explained there, the search filter can be
supplied with parameters. For this class, the only valid parameter is
<parameter>{0}</parameter> which will be replaced with the user's login
name.</para>
</section>
</section>
<section xml:id="ldap-authorities">
<title>LdapAuthoritiesPopulator</title>
<para> After authenticating the user successfully, the
<classname>LdapAuthenticationProvider</classname> will attempt to load a set of
authorities for the user by calling the configured
<interfacename>LdapAuthoritiesPopulator</interfacename> bean. The
<classname>DefaultLdapAuthoritiesPopulator</classname> is an implementation which
will load the authorities by searching the directory for groups of which the user is
a member (typically these will be <literal>groupOfNames</literal> or
<literal>groupOfUniqueNames</literal> entries in the directory). Consult the Javadoc
for this class for more details on how it works. </para>
<para>If you want to use LDAP only for authentication, but load the authorities from a
difference source (such as a database) then you can provide your own implementation
of this interface and inject that instead.</para>
</section>
<section xml:id="ldap-bean-config">
<info>
<title>Spring Bean Configuration</title>
</info>
<para>A typical configuration, using some of the beans we've discussed here, might look
like this: <programlisting language="xml"><![CDATA[
<bean id="contextSource"
class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
<constructor-arg value="ldap://monkeymachine:389/dc=springframework,dc=org"/>
<property name="userDn" value="cn=manager,dc=springframework,dc=org"/>
<property name="password" value="password"/>
</bean>
<bean id="ldapAuthProvider"
class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userDnPatterns">
<list><value>uid={0},ou=people</value></list>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean
class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource"/>
<constructor-arg value="ou=groups"/>
<property name="groupRoleAttribute" value="ou"/>
</bean>
</constructor-arg>
</bean>]]>
</programlisting> This would set up the provider to access an LDAP server
with URL <literal>ldap://monkeymachine:389/dc=springframework,dc=org</literal>.
Authentication will be performed by attempting to bind with the DN
<literal>uid=&lt;user-login-name&gt;,ou=people,dc=springframework,dc=org</literal>.
After successful authentication, roles will be assigned to the user by searching
under the DN <literal>ou=groups,dc=springframework,dc=org</literal> with the default
filter <literal>(member=&lt;user's-DN&gt;)</literal>. The role name will be taken
from the <quote>ou</quote> attribute of each match.</para>
<para>To configure a user search object, which uses the filter
<literal>(uid=&lt;user-login-name&gt;)</literal> for use instead of the DN-pattern
(or in addition to it), you would configure the following bean <programlisting language="xml"><![CDATA[
<bean id="userSearch"
class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value=""/>
<constructor-arg index="1" value="(uid={0})"/>
<constructor-arg index="2" ref="contextSource" />
</bean> ]]>
</programlisting> and use it by setting the
<classname>BindAuthenticator</classname> bean's <property>userSearch</property>
property. The authenticator would then call the search object to obtain the correct
user's DN before attempting to bind as this user.</para>
</section>
<section xml:id="ldap-custom-user-details">
<title>LDAP Attributes and Customized UserDetails</title>
<para> The net result of an authentication using
<classname>LdapAuthenticationProvider</classname> is the same as a normal Spring
Security authentication using the standard
<interfacename>UserDetailsService</interfacename> interface. A
<interfacename>UserDetails</interfacename> object is created and stored in the
returned <interfacename>Authentication</interfacename> object. As with using a
<interfacename>UserDetailsService</interfacename>, a common requirement is to be
able to customize this implementation and add extra properties. When using LDAP,
these will normally be attributes from the user entry. The creation of the
<interfacename>UserDetails</interfacename> object is controlled by the provider's
<interfacename>UserDetailsContextMapper</interfacename> strategy, which is
responsible for mapping user objects to and from LDAP context data: <programlisting language="java"><![CDATA[
public interface UserDetailsContextMapper {
UserDetails mapUserFromContext(DirContextOperations ctx, String username,
Collection<GrantedAuthority> authorities);
void mapUserToContext(UserDetails user, DirContextAdapter ctx);
}]]>
</programlisting> Only the first method is relevant for authentication. If you
provide an implementation of this interface and inject it into the
<classname>LdapAuthenticationProvider</classname>, you have control over exactly how
the UserDetails object is created. The first parameter is an instance of Spring
LDAP's <interfacename>DirContextOperations</interfacename> which gives you access to
the LDAP attributes which were loaded during authentication. The
<literal>username</literal> parameter is the name used to authenticate and the final
parameter is the collection of authorities loaded for the user by the configured
<interfacename>LdapAuthoritiesPopulator</interfacename>. </para>
<para> The way the context data is loaded varies slightly depending on the type of
authentication you are using. With the <classname>BindAuthenticator</classname>, the
context returned from the bind operation will be used to read the attributes,
otherwise the data will be read using the standard context obtained from the
configured <interfacename>ContextSource</interfacename> (when a search is configured
to locate the user, this will be the data returned by the search object). </para>
</section>
</section>
<section xml:id="ldap-active-directory">
<title>Active Directory Authentication</title>
<para>Active Directory supports its own non-standard authentication options, and the normal usage pattern
doesn't fit too cleanly with the standard <classname>LdapAuthenticationProvider</classname>.
Typically authentication is performed using the domain username (in the form <literal>user@domain</literal>),
rather than using an LDAP distinguished name. To make this easier, Spring Security 3.1 has an
authentication provider which is customized for a typical Active Directory setup.
</para>
<section>
<title><classname>ActiveDirectoryLdapAuthenticationProvider</classname></title>
<para> Configuring <classname>ActiveDirectoryLdapAuthenticationProvider</classname> is
quite straightforward. You just need to supply the domain name and an LDAP URL
supplying the address of the server <footnote>
<para>It is also possible to obtain the server's IP address using a DNS lookup. This
is not currently supported, but hopefully will be in a future version.</para>
</footnote>. An example configuration would then look like this: <programlisting language="xml"><![CDATA[
<bean id="adAuthenticationProvider"
class="org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider">
<constructor-arg value="mydomain.com" />
<constructor-arg value="ldap://adserver.mydomain.com/" />
</bean>
}]]>
</programlisting> Note that there is no need to specify a separate
<literal>ContextSource</literal> in order to define the server location - the bean
is completely self-contained. A user named <quote>Sharon</quote>, for example, would
then be able to authenticate by entering either the username
<literal>sharon</literal> or the full Active Directory
<literal>userPrincipalName</literal>, namely <literal>sharon@mydomain.com</literal>.
The user's directory entry will then be located, and the attributes returned for
possible use in customizing the created <interfacename>UserDetails</interfacename>
object (a <interfacename>UserDetailsContextMapper</interfacename> can be injected
for this purpose, as described above). All interaction with the directory takes
place with the identity of the user themselves. There is no concept of a
<quote>manager</quote> user. </para>
<para>By default, the user authorities are obtained from the <literal>memberOf</literal>
attribute values of the user entry. The authorities allocated to the user can again
be customized using a <interfacename>UserDetailsContextMapper</interfacename>. You
can also inject a <interfacename>GrantedAuthoritiesMapper</interfacename> into the
provider instance to control the authorities which end up in the
<interfacename>Authentication</interfacename> object.</para>
<section>
<title>Active Directory Error Codes</title>
<para>By default, a failed result will cause a standard Spring Security
<classname>BadCredentialsException</classname>. If you set the property
<literal>convertSubErrorCodesToExceptions</literal> to <literal>true</literal>,
the exception messages will be parsed to attempt to extract the Active
Directory-specific error code and raise a more specific exception. Check the
class Javadoc for more information.</para>
</section>
</section>
</section>
</chapter>

File diff suppressed because it is too large Load Diff

View File

@ -1,67 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="new-3.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>What's new in Spring Security 3.1</title>
</info>
<para>This section contains summary of the updates found in Spring Security 3.1. A detailed list of changes can be found in the project's
<link xlink:href="https://jira.springsource.org/secure/IssueNavigator!executeAdvanced.jspa?jqlQuery=project+%3D+SEC+AND+fixVersion+in+%2812315%2C+11892%2C+11634%2C+11633%2C+11632%2C+11174%29+order+by+priority%2C+type&amp;runQuery=true&amp;clear=true">JIRA</link></para>
<section xml:id="new-3.1-highlevel">
<title>High level updates found Spring Security 3.1</title>
<para>Below you can find a high level summary of updates to Spring Security 3.1.</para>
<itemizedlist>
<listitem>Support for multiple http elements</listitem>
<listitem>Support for stateless authentication</listitem>
<listitem>DebugFilter provides additional debugging information</listitem>
<listitem>Improved Active Directory LDAP support (i.e. ActiveDirectoryLdapAuthenticationProvider)</listitem>
<listitem>Added Basic Crypto Module.</listitem>
<listitem>The namespace is fully documented in the reference appendix.</listitem>
<listitem>Added dependencies section to the reference appendix</listitem>
<listitem>Support HttpOnly Flag for Cookies in Servlet 3.0 environments</listitem>
<listitem>InMemoryUserDetailsManager provides in memory implementation of UserDetailsManager</listitem>
<listitem>Support for hasPermission expression on the authorize JSP tag</listitem>
<listitem>Support for disabling UI security (for testing purposes)</listitem>
<listitem>Support erasing credentials after successful authentication</listitem>
<listitem>Support clearing cookies on logout</listitem>
<listitem>Spring Security Google App Engine example application</listitem>
<listitem>Support for CAS proxy tickets</listitem>
<listitem>Support for arbitrary implementations of JAAS Configuration</listitem>
<listitem>Support nested switching of users for SwitchUserFilter</listitem>
</itemizedlist>
</section>
<section xml:id="new-3.1-ns">
<title>Spring Security 3.1 namespace updates</title>
<para>Below you can find a summary of updates to the Spring Security 3.1 namespace.</para>
<itemizedlist>
<listitem>Added support for multiple <link linkend="nsa-http">&lt;http&gt;</link> elements and support for determining which one to use with
<link linkend="nsa-http-pattern">http@pattern</link>, <link linkend="nsa-http-request-matcher">http@request-matcher</link>, and
<link linkend="nsa-http-security">http@security</link>.
Further information can be found in <link linkend="ns-config">Namespace Configuration</link> section of the reference.</listitem>
<listitem>Added stateless option for <link linkend="nsa-http-create-session">http@create-session</link></listitem>
<listitem>Added support for <link linkend="nsa-http-authentication-manager-ref">http@authentication-manager-ref</link>
and <link linkend="nsa-global-method-security-authentication-manager-ref">global-method-security@authentication-manager-ref</link>.</listitem>
<listitem>Added <link linkend="nsa-http-name">http@name</link></listitem>
<listitem>Added <link linkend="nsa-http-request-matcher-ref">http@request-matcher-ref</link> and
<link linkend="nsa-filter-chain-request-matcher-ref">filter-chain@request-matcher-ref</link></listitem>
<listitem>Added <link linkend="nsa-debug">&lt;debug&gt;</link></listitem>
<listitem>Added Support for setting the AuthenticationDetailsSource using the namespace. See
<link linkend="nsa-form-login-authentication-details-source-ref">form-login@authentication-details-source-ref</link>,
<link linkend="nsa-openid-login-authentication-details-source-ref">openid-login@authentication-details-source-ref</link>,
<link linkend="nsa-http-basic-authentication-details-source-ref">http-basic@authentication-details-source-ref</link>, and
<link linkend="nsa-x509-authentication-details-source-ref">x509@authentication-details-source-ref</link>.</listitem>
<listitem>Added support for http/expression-handler. This allows
<link linkend="nsa-expression-handler">&lt;expression-handler&gt;</link> to be used for web access expressions.</listitem>
<listitem>Added <link linkend="nsa-authentication-manager-erase-credentials">authentication-manager@erase-credentials</link></listitem>
<listitem>Added <link linkend="nsa-http-basic-entry-point-ref">http-basic@entry-point-ref</link></listitem>
<listitem>Added <link linkend="nsa-logout-delete-cookies">logout@delete-cookies</link></listitem>
<listitem>Added <link linkend="nsa-remember-me-authentication-success-handler-ref">remember-me@authentication-success-handler-ref</link></listitem>
<listitem>Added <link linkend="nsa-method-security-metadata-source">&lt;metadata-source-ref&gt;</link></listitem>
<listitem>Added <link linkend="nsa-global-method-security-metadata-source-ref">global-method-security@metadata-source-ref</link></listitem>
<listitem>Added <link linkend="nsa-global-method-security-mode">global-method-security@mode</link></listitem>
<listitem>Added <link linkend="nsa-attribute-exchange">&lt;attribute-exchange&gt;</link></listitem>
<listitem>Added <link linkend="nsa-remember-me-use-secure-cookie">remember-me@use-secure-cookie</link></listitem>
<listitem>Added <link linkend="nsa-http-jaas-api-provision">http@jaas-api-provision</link></listitem>
<listitem>Added <link linkend="nsa-form-login-username-parameter">form-login@username-parameter</link> and
<link linkend="nsa-form-login-password-parameter">form-login@password-parameter</link></listitem>
</itemizedlist>
</section>
</chapter>

View File

@ -1,3 +0,0 @@
CLASSPATH=`find ../../../.. -name *.jar | grep -v sources | xargs | sed "s/ /:/g"`
grep -o -e 'org.springframework.security\.[a-z]*\.[a-zA-Z0-9]*\.[A-Z][a-zA-z0-9]*' * | cut -d : -f 2 | xargs -n 1 javap -classpath "$CLASSPATH" | grep ERROR

View File

@ -1,101 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<!DOCTYPE t:templates [
<!ENTITY hsize0 "10pt">
<!ENTITY hsize1 "12pt">
<!ENTITY hsize2 "14.4pt">
<!ENTITY hsize3 "17.28pt">
<!ENTITY hsize4 "20.736pt">
<!ENTITY hsize5 "24.8832pt">
<!ENTITY hsize0space "7.5pt"> <!-- 0.75 * hsize0 -->
<!ENTITY hsize1space "9pt"> <!-- 0.75 * hsize1 -->
<!ENTITY hsize2space "10.8pt"> <!-- 0.75 * hsize2 -->
<!ENTITY hsize3space "12.96pt"> <!-- 0.75 * hsize3 -->
<!ENTITY hsize4space "15.552pt"> <!-- 0.75 * hsize4 -->
<!ENTITY hsize5space "18.6624pt"> <!-- 0.75 * hsize5 -->
]>
<t:templates xmlns:t="http://nwalsh.com/docbook/xsl/template/1.0"
xmlns:param="http://nwalsh.com/docbook/xsl/template/1.0/param"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<t:titlepage t:element="book" t:wrapper="fo:block">
<t:titlepage-content t:side="recto">
<title
t:named-template="division.title"
param:node="ancestor-or-self::book[1]"
text-align="center"
font-size="&hsize5;"
space-before="&hsize5space;"
font-weight="bold"
font-family="{$title.fontset}"
/>
<subtitle
text-align="center"
font-size="&hsize4;"
space-before="&hsize4space;"
font-family="{$title.fontset}"
/>
<corpauthor space-before="0.5em"
font-size="&hsize2;"
/>
<authorgroup space-before="0.5em"
font-size="&hsize2;"
/>
<author space-before="0.5em" font-size="&hsize2;"/>
<mediaobject space-before="2em" space-after="2em"/>
<releaseinfo space-before="5em" font-size="&hsize2;"/>
<othercredit space-before="2em" font-weight="normal" font-size="8"/>
<pubdate space-before="0.5em"/>
<revision space-before="0.5em"/>
<revhistory space-before="0.5em"/>
<abstract space-before="0.5em"
text-align="start"
margin-left="0.1in"
margin-right="0.1in"
font-family="{$body.fontset}"
/>
</t:titlepage-content>
<t:titlepage-content t:side="verso" text-align="start">
<copyright space-before="1.5em"/>
<legalnotice space-before="15em"/>
</t:titlepage-content>
<t:titlepage-separator>
</t:titlepage-separator>
<t:titlepage-before t:side="recto">
</t:titlepage-before>
<t:titlepage-before t:side="verso">
</t:titlepage-before>
</t:titlepage>
<!-- ==================================================================== -->
</t:templates>

View File

@ -1,192 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="preauth"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Pre-Authentication Scenarios</title>
</info>
<para> There are situations where you want to use Spring Security for authorization, but the
user has already been reliably authenticated by some external system prior to accessing the
application. We refer to these situations as <quote>pre-authenticated</quote> scenarios.
Examples include X.509, Siteminder and authentication by the J2EE container in which the
application is running. When using pre-authentication, Spring Security has to <orderedlist>
<listitem>
<para>Identify the user making the request. </para>
</listitem>
<listitem>
<para>Obtain the authorities for the user.</para>
</listitem>
</orderedlist>The details will depend on the external authentication mechanism. A user might
be identified by their certificate information in the case of X.509, or by an HTTP request
header in the case of Siteminder. If relying on container authentication, the user will be
identified by calling the <methodname>getUserPrincipal()</methodname> method on the incoming
HTTP request. In some cases, the external mechanism may supply role/authority information
for the user but in others the authorities must be obtained from a separate source, such as
a <interfacename>UserDetailsService</interfacename>. </para>
<section>
<title>Pre-Authentication Framework Classes</title>
<para> Because most pre-authentication mechanisms follow the same pattern, Spring Security
has a set of classes which provide an internal framework for implementing
pre-authenticated authentication providers. This removes duplication and allows new
implementations to be added in a structured fashion, without having to write everything
from scratch. You don't need to know about these classes if you want to use something
like <link linkend="x509">X.509 authentication</link>, as it already has a namespace
configuration option which is simpler to use and get started with. If you need to use
explicit bean configuration or are planning on writing your own implementation then an
understanding of how the provided implementations work will be useful. You will find
classes under the
<package>org.springframework.security.web.authentication.preauth</package>. We just
provide an outline here so you should consult the Javadoc and source where appropriate. </para>
<section>
<title>AbstractPreAuthenticatedProcessingFilter</title>
<para> This class will check the current contents of the security context and, if empty,
it will attempt to extract user information from the HTTP request and submit it to
the <interfacename>AuthenticationManager</interfacename>. Subclasses override the
following methods to obtain this information:
<programlisting language="java">
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
</programlisting>
After calling these, the filter will create a
<classname>PreAuthenticatedAuthenticationToken</classname> containing the returned
data and submit it for authentication. By <quote>authentication</quote> here, we
really just mean further processing to perhaps load the user's authorities, but the
standard Spring Security authentication architecture is followed. </para>
<para> Like other Spring Security authentication filters, the pre-authentication filter
has an <literal>authenticationDetailsSource</literal> property which by default will
create a <classname>WebAuthenticationDetails</classname> object to store additional
information such as the session-identifier and originating IP address in the
<literal>details</literal> property of the
<interfacename>Authentication</interfacename> object. In cases where user role
information can be obtained from the pre-authentication mechanism, the data is also
stored in this property, with the details implementing the
<interfacename>GrantedAuthoritiesContainer</interfacename> interface. This
enables the authentication provider to read the authorities which were externally
allocated to the user. We'll look at a concrete example next. </para>
<section xml:id="j2ee-preauth-details">
<title>J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource</title>
<para> If the filter is configured with an
<literal>authenticationDetailsSource</literal> which is an instance of this
class, the authority information is obtained by calling the
<methodname>isUserInRole(String role)</methodname> method for each of a
pre-determined set of <quote>mappable roles</quote>. The class gets these from a
configured <interfacename>MappableAttributesRetriever</interfacename>. Possible
implementations include hard-coding a list in the application context and
reading the role information from the <literal>&lt;security-role&gt;</literal>
information in a <filename>web.xml</filename> file. The pre-authentication
sample application uses the latter approach.</para>
<para>There is an additional stage where the roles (or attributes) are mapped to
Spring Security <interfacename>GrantedAuthority</interfacename> objects using a
configured <interfacename>Attributes2GrantedAuthoritiesMapper</interfacename>.
The default will just add the usual <literal>ROLE_</literal> prefix to the
names, but it gives you full control over the behaviour. </para>
</section>
</section>
<section>
<title>PreAuthenticatedAuthenticationProvider</title>
<para> The pre-authenticated provider has little more to do than load the
<interfacename>UserDetails</interfacename> object for the user. It does this by
delegating to a <interfacename>AuthenticationUserDetailsService</interfacename>. The
latter is similar to the standard <interfacename>UserDetailsService</interfacename>
but takes an <interfacename>Authentication</interfacename> object rather than just
user name:
<programlisting language="java">
public interface AuthenticationUserDetailsService {
UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}
</programlisting>
This interface may have also other uses but with pre-authentication it allows access
to the authorities which were packaged in the
<interfacename>Authentication</interfacename> object, as we saw in the previous
section. The
<classname>PreAuthenticatedGrantedAuthoritiesUserDetailsService</classname> class
does this. Alternatively, it may delegate to a standard
<interfacename>UserDetailsService</interfacename> via the
<classname>UserDetailsByNameServiceWrapper</classname> implementation. </para>
</section>
<section>
<title>Http403ForbiddenEntryPoint</title>
<para> The <interfacename>AuthenticationEntryPoint</interfacename> was discussed in the
<link linkend="tech-intro-auth-entry-point">technical overview</link> chapter.
Normally it is responsible for kick-starting the authentication process for an
unauthenticated user (when they try to access a protected resource), but in the
pre-authenticated case this doesn't apply. You would only configure the
<classname>ExceptionTranslationFilter</classname> with an instance of this class if
you aren't using pre-authentication in combination with other authentication
mechanisms. It will be called if the user is rejected by the
<classname>AbstractPreAuthenticatedProcessingFilter</classname> resulting in a null
authentication. It always returns a <literal>403</literal>-forbidden response code
if called. </para>
</section>
</section>
<section>
<title>Concrete Implementations</title>
<para> X.509 authentication is covered in its <link linkend="x509">own chapter</link>.
Here we'll look at some classes which provide support for other pre-authenticated
scenarios. </para>
<section>
<title>Request-Header Authentication (Siteminder)</title>
<para> An external authentication system may supply information to the application by
setting specific headers on the HTTP request. A well known example of this is
Siteminder, which passes the username in a header called <literal>SM_USER</literal>.
This mechanism is supported by the class
<classname>RequestHeaderAuthenticationFilter</classname> which simply extracts the
username from the header. It defaults to using the name <literal>SM_USER</literal>
as the header name. See the Javadoc for more details. </para>
<tip>
<para>Note that when using a system like this, the framework performs no
authentication checks at all and it is <emphasis>extremely</emphasis> important
that the external system is configured properly and protects all access to the
application. If an attacker is able to forge the headers in their original
request without this being detected then they could potentially choose any
username they wished. </para>
</tip>
<section>
<title>Siteminder Example Configuration</title>
<para> A typical configuration using this filter would look like this: <programlisting language="xml"><![CDATA[
<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class=
"org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
]]>
</programlisting> We've assumed here that the <link linkend="ns-config">security namespace</link>
is being used for configuration. It's also assumed that you have added a
<interfacename>UserDetailsService</interfacename> (called
<quote>userDetailsService</quote>) to your configuration to load the user's
roles. </para>
</section>
</section>
<section>
<title>J2EE Container Authentication</title>
<para> The class <classname>J2eePreAuthenticatedProcessingFilter</classname> will
extract the username from the <literal>userPrincipal</literal> property of the
<interfacename>HttpServletRequest</interfacename>. Use of this filter would usually
be combined with the use of J2EE roles as described above in <xref
linkend="j2ee-preauth-details"/>. </para>
<para> There is a sample application in the codebase which uses this approach, so get
hold of the code from subversion and have a look at the application context file if
you are interested. The code is in the <filename>samples/preauth</filename>
directory. </para>
</section>
</section>
</chapter>

View File

@ -1,180 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="remember-me"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Remember-Me Authentication</title>
</info>
<section xml:id="remember-me-overview">
<info>
<title>Overview</title>
</info>
<para>Remember-me or persistent-login authentication refers to web sites being able to
remember the identity of a principal between sessions. This is typically accomplished by
sending a cookie to the browser, with the cookie being detected during future sessions
and causing automated login to take place. Spring Security provides the necessary hooks
for these operations to take place, and has two concrete remember-me implementations.
One uses hashing to preserve the security of cookie-based tokens and the other uses a
database or other persistent storage mechanism to store the generated tokens. </para>
<para> Note that both implemementations require a
<interfacename>UserDetailsService</interfacename>. If you are using an authentication
provider which doesn't use a <interfacename>UserDetailsService</interfacename> (for
example, the LDAP provider) then it won't work unless you also have a
<interfacename>UserDetailsService</interfacename> bean in your application context.
</para>
</section>
<section xml:id="remember-me-hash-token">
<title>Simple Hash-Based Token Approach</title>
<para>This approach uses hashing to achieve a useful remember-me strategy. In essence a
cookie is sent to the browser upon successful interactive authentication, with the
cookie being composed as follows:
<programlisting language="txt">
base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" password + ":" + key))
username: As identifiable to the <interfacename>UserDetailsService</interfacename>
password: That matches the one in the retrieved UserDetails
expirationTime: The date and time when the remember-me token expires,
expressed in milliseconds
key: A private key to prevent modification of the remember-me token
</programlisting></para>
<para>As such the remember-me token is valid only for the period specified, and provided
that the username, password and key does not change. Notably, this has a potential
security issue in that a captured remember-me token will be usable from any user agent
until such time as the token expires. This is the same issue as with digest
authentication. If a principal is aware a token has been captured, they can easily
change their password and immediately invalidate all remember-me tokens on issue. If
more significant security is needed you should use the approach described in the next
section. Alternatively remember-me services should simply not be used at all.</para>
<para>If you are familiar with the topics discussed in the chapter on <link
linkend="ns-config">namespace configuration</link>, you can enable remember-me
authentication just by adding the <literal>&lt;remember-me&gt;</literal> element: <programlisting language="xml"><![CDATA[
<http>
...
<remember-me key="myAppKey"/>
</http>
]]>
</programlisting> The <interfacename>UserDetailsService</interfacename> will
normally be selected automatically. If you have more than one in your application
context, you need to specify which one should be used with the
<literal>user-service-ref</literal> attribute, where the value is the name of your
<interfacename>UserDetailsService</interfacename> bean. </para>
</section>
<section xml:id="remember-me-persistent-token">
<title>Persistent Token Approach</title>
<para>This approach is based on the article <link
xlink:href="http://jaspan.com/improved_persistent_login_cookie_best_practice"
>http://jaspan.com/improved_persistent_login_cookie_best_practice</link> with some minor
modifications <footnote>
<para>Essentially, the username is not included in the cookie, to prevent exposing a
valid login name unecessarily. There is a discussion on this in the comments section
of this article.</para>
</footnote>. To use the this approach with namespace configuration, you would supply a
datasource reference: <programlisting language="xml"><![CDATA[
<http>
...
<remember-me data-source-ref="someDataSource"/>
</http>
]]>
</programlisting> The database should contain a
<literal>persistent_logins</literal> table, created using the following SQL (or
equivalent):
<programlisting language="ddl">
create table persistent_logins (username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null)
</programlisting></para>
<!-- TODO: Add more info on the implementation and behaviour when tokens are stolen etc. Also some info for admins on invalidating tokens using key, or deleting info from db -->
</section>
<section xml:id="remember-me-impls">
<info>
<title>Remember-Me Interfaces and Implementations</title>
</info>
<para>Remember-me authentication is not used with basic authentication, given it is often
not used with <literal>HttpSession</literal>s. Remember-me is used with
<literal>UsernamePasswordAuthenticationFilter</literal>, and is implemented via hooks in
the <literal>AbstractAuthenticationProcessingFilter</literal> superclass. The hooks will
invoke a concrete <interfacename>RememberMeServices</interfacename> at the appropriate
times. The interface looks like this:
<programlisting language="java">
Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
void loginFail(HttpServletRequest request, HttpServletResponse response);
void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication);
</programlisting>
Please refer to the JavaDocs for a fuller discussion on what the methods do, although
note at this stage that <literal>AbstractAuthenticationProcessingFilter</literal> only
calls the <literal>loginFail()</literal> and <literal>loginSuccess()</literal> methods.
The <literal>autoLogin()</literal> method is called by
<classname>RememberMeAuthenticationFilter</classname> whenever the
<classname>SecurityContextHolder</classname> does not contain an
<interfacename>Authentication</interfacename>. This interface therefore provides the
underlying remember-me implementation with sufficient notification of
authentication-related events, and delegates to the implementation whenever a candidate
web request might contain a cookie and wish to be remembered. This design allows any
number of remember-me implementation strategies. We've seen above that Spring Security
provides two implementations. We'll look at these in turn.</para>
<section>
<title>TokenBasedRememberMeServices</title>
<para> This implementation supports the simpler approach described in <xref
linkend="remember-me-hash-token"/>.
<classname>TokenBasedRememberMeServices</classname> generates a
<literal>RememberMeAuthenticationToken</literal>, which is processed by
<literal>RememberMeAuthenticationProvider</literal>. A <literal>key</literal> is
shared between this authentication provider and the
<literal>TokenBasedRememberMeServices</literal>. In addition,
<literal>TokenBasedRememberMeServices</literal> requires A UserDetailsService from
which it can retrieve the username and password for signature comparison purposes,
and generate the <literal>RememberMeAuthenticationToken</literal> to contain the
correct <interfacename>GrantedAuthority</interfacename>s. Some sort of logout
command should be provided by the application that invalidates the cookie if the
user requests this. <classname>TokenBasedRememberMeServices</classname> also
implements Spring Security's <interfacename>LogoutHandler</interfacename> interface
so can be used with <classname>LogoutFilter</classname> to have the cookie cleared
automatically. </para>
<para>The beans required in an application context to enable remember-me services are as
follows: <programlisting language="xml"><![CDATA[
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="theAuthenticationManager" />
</bean>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.rememberme.RememberMeAuthenticationProvider">
<property name="key" value="springRocks"/>
</bean>
]]>
</programlisting>Don't forget to add your
<interfacename>RememberMeServices</interfacename> implementation to your
<literal>UsernamePasswordAuthenticationFilter.setRememberMeServices()</literal>
property, include the <literal>RememberMeAuthenticationProvider</literal> in your
<literal>AuthenticationManager.setProviders()</literal> list, and add
<classname>RememberMeAuthenticationFilter</classname> into your
<classname>FilterChainProxy</classname> (typically immediately after your
<literal>UsernamePasswordAuthenticationFilter</literal>).</para>
</section>
<section>
<title>PersistentTokenBasedRememberMeServices</title>
<para> This class can be used in the same way as
<classname>TokenBasedRememberMeServices</classname>, but it additionally needs to be
configured with a <interfacename>PersistentTokenRepository</interfacename> to store
the tokens. There are two standard implementations. <itemizedlist>
<listitem>
<para><classname>InMemoryTokenRepositoryImpl</classname> which is intended for
testing only.</para>
</listitem>
<listitem>
<para><classname>JdbcTokenRepositoryImpl</classname> which stores the tokens in
a database. </para>
</listitem>
</itemizedlist> The database schema is described above in <xref
linkend="remember-me-persistent-token"/>. </para>
</section>
</section>
</chapter>

View File

@ -1,97 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="runas">
<info>
<title>Run-As Authentication Replacement</title>
</info>
<section xml:id="runas-overview">
<info>
<title>Overview</title>
</info>
<para>The <classname>AbstractSecurityInterceptor</classname> is able to temporarily replace
the <interfacename>Authentication</interfacename> object in the
<interfacename>SecurityContext</interfacename> and
<classname>SecurityContextHolder</classname> during the secure object callback phase.
This only occurs if the original <interfacename>Authentication</interfacename> object
was successfully processed by the <interfacename>AuthenticationManager</interfacename>
and <interfacename>AccessDecisionManager</interfacename>. The
<literal>RunAsManager</literal> will indicate the replacement
<interfacename>Authentication</interfacename> object, if any, that should be used during
the <literal>SecurityInterceptorCallback</literal>.</para>
<para>By temporarily replacing the <interfacename>Authentication</interfacename> object
during the secure object callback phase, the secured invocation will be able to call
other objects which require different authentication and authorization credentials. It
will also be able to perform any internal security checks for specific
<interfacename>GrantedAuthority</interfacename> objects. Because Spring Security
provides a number of helper classes that automatically configure remoting protocols
based on the contents of the <classname>SecurityContextHolder</classname>, these run-as
replacements are particularly useful when calling remote web services</para>
</section>
<section xml:id="runas-config">
<info>
<title>Configuration</title>
</info>
<para>A <literal>RunAsManager</literal> interface is provided by Spring Security:
<programlisting language="java">
Authentication buildRunAs(Authentication authentication, Object object,
List&lt;ConfigAttribute&gt; config);
boolean supports(ConfigAttribute attribute);
boolean supports(Class clazz);
</programlisting> </para>
<para>The first method returns the <interfacename>Authentication</interfacename> object that
should replace the existing <interfacename>Authentication</interfacename> object for the
duration of the method invocation. If the method returns <literal>null</literal>, it
indicates no replacement should be made. The second method is used by the
<classname>AbstractSecurityInterceptor</classname> as part of its startup validation of
configuration attributes. The <literal>supports(Class)</literal> method is called by a
security interceptor implementation to ensure the configured
<literal>RunAsManager</literal> supports the type of secure object that the security
interceptor will present.</para>
<para>One concrete implementation of a <literal>RunAsManager</literal> is provided with
Spring Security. The <literal>RunAsManagerImpl</literal> class returns a replacement
<literal>RunAsUserToken</literal> if any <literal>ConfigAttribute</literal> starts with
<literal>RUN_AS_</literal>. If any such <literal>ConfigAttribute</literal> is found, the
replacement <literal>RunAsUserToken</literal> will contain the same principal,
credentials and granted authorities as the original
<interfacename>Authentication</interfacename> object, along with a new
<literal>GrantedAuthorityImpl</literal> for each <literal>RUN_AS_</literal>
<literal>ConfigAttribute</literal>. Each new <literal>GrantedAuthorityImpl</literal>
will be prefixed with <literal>ROLE_</literal>, followed by the
<literal>RUN_AS</literal> <literal>ConfigAttribute</literal>. For example, a
<literal>RUN_AS_SERVER</literal> will result in the replacement
<literal>RunAsUserToken</literal> containing a <literal>ROLE_RUN_AS_SERVER</literal>
granted authority.</para>
<para>The replacement <literal>RunAsUserToken</literal> is just like any other
<interfacename>Authentication</interfacename> object. It needs to be authenticated by
the <interfacename>AuthenticationManager</interfacename>, probably via delegation to a
suitable <classname>AuthenticationProvider</classname>. The
<literal>RunAsImplAuthenticationProvider</literal> performs such authentication. It
simply accepts as valid any <literal>RunAsUserToken</literal> presented.</para>
<para>To ensure malicious code does not create a <literal>RunAsUserToken</literal> and
present it for guaranteed acceptance by the
<literal>RunAsImplAuthenticationProvider</literal>, the hash of a key is stored in all
generated tokens. The <literal>RunAsManagerImpl</literal> and
<literal>RunAsImplAuthenticationProvider</literal> is created in the bean context with
the same key: <programlisting language="xml">
<![CDATA[
<bean id="runAsManager"
class="org.springframework.security.access.intercept.RunAsManagerImpl">
<property name="key" value="my_run_as_password"/>
</bean>
<bean id="runAsAuthenticationProvider"
class="org.springframework.security.access.intercept.RunAsImplAuthenticationProvider">
<property name="key" value="my_run_as_password"/>
</bean>]]></programlisting></para>
<para>By using the same key, each <literal>RunAsUserToken</literal> can be validated it was
created by an approved <literal>RunAsManagerImpl</literal>. The
<literal>RunAsUserToken</literal> is immutable after creation for security
reasons</para>
</section>
</chapter>

View File

@ -1,145 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink"
version="5.0" xml:id="sample-apps">
<info>
<title xml:id="samples">Sample Applications</title>
</info>
<para> There are several sample web applications that are available with the project. To avoid
an overly large download, only the "tutorial" and "contacts" samples are included in the
distribution zip file. The others can be built directly from the source which you can obtain
as described in <link linkend="get-source">the introduction</link>. It's easy to build
the project yourself and there's more information on the project web site at <link
xlink:href="http://www.springsource.org/security/">
http://www.springsource.org/security/ </link>. All paths referred to in this chapter are
relative to the project source directory. </para>
<section xml:id="tutorial-sample">
<title>Tutorial Sample</title>
<para> The tutorial sample is a nice basic example to get you started. It uses simple
namespace configuration throughout. The compiled application is included in the
distribution zip file, ready to be deployed into your web container
(<filename>spring-security-samples-tutorial-3.1.x.war</filename>). The <link
linkend="ns-form-and-basic">form-based</link> authentication mechanism is used in
combination with the commonly-used <link linkend="remember-me">remember-me</link>
authentication provider to automatically remember the login using cookies.</para>
<para>We recommend you start with the tutorial sample, as the XML is minimal and easy to
follow. Most importantly, you can easily add this one XML file (and its corresponding
<literal>web.xml</literal> entries) to your existing application. Only when this basic
integration is achieved do we suggest you attempt adding in method authorization or
domain object security.</para>
</section>
<section xml:id="contacts-sample">
<title>Contacts</title>
<para> The Contacts Sample is an advanced example in that it illustrates the more powerful
features of domain object access control lists (ACLs) in addition to basic application
security. The application provides an interface with which the users are able to
administer a simple database of contacts (the domain objects).</para>
<para>To deploy, simply copy the WAR file from Spring Security distribution into your
containers <literal>webapps</literal> directory. The war should be called
<filename>spring-security-samples-contacts-3.1.x.war</filename> (the appended version
number will vary depending on what release you are using). </para>
<para>After starting your container, check the application can load. Visit
<literal>http://localhost:8080/contacts</literal> (or whichever URL is appropriate for
your web container and the WAR you deployed). </para>
<para>Next, click "Debug". You will be prompted to authenticate, and a series of usernames
and passwords are suggested on that page. Simply authenticate with any of these and view
the resulting page. It should contain a success message similar to the following:
<literallayout>
Security Debug Information
Authentication object is of type:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
Authentication object as a String:
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@1f127853:
Principal: org.springframework.security.core.userdetails.User@b07ed00: Username: rod; \
Password: [PROTECTED]; Enabled: true; AccountNonExpired: true;
credentialsNonExpired: true; AccountNonLocked: true; \
Granted Authorities: ROLE_SUPERVISOR, ROLE_USER; \
Password: [PROTECTED]; Authenticated: true; \
Details: org.springframework.security.web.authentication.WebAuthenticationDetails@0: \
RemoteIpAddress: 127.0.0.1; SessionId: 8fkp8t83ohar; \
Granted Authorities: ROLE_SUPERVISOR, ROLE_USER
Authentication object holds the following granted authorities:
ROLE_SUPERVISOR (getAuthority(): ROLE_SUPERVISOR)
ROLE_USER (getAuthority(): ROLE_USER)
Success! Your web filters appear to be properly configured!
</literallayout></para>
<para>Once you successfully receive the above message, return to the sample application's
home page and click "Manage". You can then try out the application. Notice that only the
contacts available to the currently logged on user are displayed, and only users with
<literal>ROLE_SUPERVISOR</literal> are granted access to delete their contacts. Behind
the scenes, the <classname>MethodSecurityInterceptor</classname> is securing the
business objects. </para>
<para>The application allows you to modify the access control lists associated with
different contacts. Be sure to give this a try and understand how it works by reviewing
the application context XML files.</para>
<!--
TODO: Reintroduce standalone client example.
<para>The Contacts sample application also includes a
<literal>client</literal> directory. Inside you will find a small
application that queries the backend business objects using several
web services protocols. This demonstrates how to use Spring Security
for authentication with Spring remoting protocols. To try this client,
ensure your servlet container is still running the Contacts sample
application, and then execute <literal>client rod koala</literal>. The
command-line parameters respectively represent the username to use,
and the password to use. Note that you may need to edit
<literal>client.properties</literal> to use a different target
URL.</para>
-->
</section>
<section xml:id="ldap-sample">
<title>LDAP Sample</title>
<para> The LDAP sample application provides a basic configuration and sets up both a
namespace configuration and an equivalent configuration using traditional beans, both in
the same application context file. This means there are actually two identical
authentication providers configured in this application. </para>
</section>
<section xml:id="openid-sample">
<title>OpenID Sample</title>
<para>
The OpenID sample demonstrates how to use the namespace to configure OpenID and how to set up
<link xlink:href="http://openid.net/specs/openid-attribute-exchange-1_0.html">attribute exchange</link>
configurations for Google, Yahoo and MyOpenID identity providers (you can experiment with adding others
if you wish). It uses the JQuery-based <link xlink:href="http://code.google.com/p/openid-selector/">openid-selector</link>
project to provide a user-friendly login page which allows the user to easily select a provider, rather than
typing in the full OpenID identifier.
</para>
<para>
The application differs from normal authentication scenarios in that it allows any user to access the site (provided
their OpenID authentication is successful). The first time you login, you will get a <quote>Welcome [your name]"</quote>
message. If you logout and log back in (with the same OpenID identity) then this should change to <quote>Welcome Back</quote>.
This is achieved by using a custom <interfacename>UserDetailsService</interfacename> which assigns a standard role
to any user and stores the identities internally in a map. Obviously a real application would use a database instead.
Have a look at the source form more information. This class also takes into account the fact that different attributes may be returned
from different providers and builds the name with which it addresses the user accordingly.
</para>
</section>
<section xml:id="cas-sample">
<title>CAS Sample</title>
<para> The CAS sample requires that you run both a CAS server and CAS client. It isn't
included in the distribution so you should check out the project code as described in
<link linkend="get-source">the introduction</link>. You'll find the relevant files
under the <filename>sample/cas</filename> directory. There's also a
<filename>Readme.txt</filename> file in there which explains how to run both the server
and the client directly from the source tree, complete with SSL support.</para>
</section>
<section xml:id="jaas-sample">
<title>JAAS Sample</title>
<para>The JAAS sample is very simple example of how to use a JAAS LoginModule with Spring Security. The provided LoginModule will
successfully authenticate a user if the username equals the password otherwise a LoginException is thrown. The AuthorityGranter
used in this example always grants the role ROLE_USER. The sample application also demonstrates how to run as the JAAS Subject
returned by the LoginModule by setting <link linkend="nsa-http-jaas-api-provision">jaas-api-provision</link> equal to "true".</para>
</section>
<section xml:id="preauth-sample">
<title>Pre-Authentication Sample</title>
<para> This sample application demonstrates how to wire up beans from the <link
linkend="preauth">pre-authentication</link> framework to make use of login
information from a J2EE container. The user name and roles are those setup by the
container. </para>
<para> The code is in <filename>samples/preauth</filename>. </para>
</section>
</chapter>

View File

@ -1,154 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="secure-object-impls"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Secure Object Implementations</title>
</info>
<section xml:id="aop-alliance">
<info>
<title>AOP Alliance (MethodInvocation) Security Interceptor</title>
</info>
<para> Prior to Spring Security 2.0, securing <classname>MethodInvocation</classname>s
needed quite a lot of boiler plate configuration. Now the recommended approach for
method security is to use <link linkend="ns-method-security">namespace
configuration</link>. This way the method security infrastructure beans are configured
automatically for you so you don't really need to know about the implementation classes.
We'll just provide a quick overview of the classes that are involved here. </para>
<para> Method security in enforced using a <classname>MethodSecurityInterceptor</classname>,
which secures <classname>MethodInvocation</classname>s. Depending on the configuration
approach, an interceptor may be specific to a single bean or shared between multiple
beans. The interceptor uses a
<interfacename>MethodSecurityMetadataSource</interfacename> instance to obtain the
configuration attributes that apply to a particular method invocation.
<classname>MapBasedMethodSecurityMetadataSource</classname> is used to store
configuration attributes keyed by method names (which can be wildcarded) and will be
used internally when the attributes are defined in the application context using the
<literal>&lt;intercept-methods&gt;</literal> or <literal>&lt;protect-point&gt;</literal>
elements. Other implementations will be used to handle annotation-based configuration. </para>
<section>
<title>Explicit MethodSecurityInterceptor Configuration</title>
<para> You can of course configure a <classname>MethodSecurityIterceptor</classname>
directly in your application context for use with one of Spring AOP's proxying
mechanisms: <programlisting language="xml"><![CDATA[
<bean id="bankManagerSecurity" class=
"org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<sec:method-security-metadata-source>
<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
</sec:method-security-metadata-source>
</property>
</bean> ]]>
</programlisting></para>
</section>
</section>
<section xml:id="aspectj">
<info>
<title>AspectJ (JoinPoint) Security Interceptor</title>
</info>
<para>The AspectJ security interceptor is very similar to the AOP Alliance security
interceptor discussed in the previous section. Indeed we will only discuss the
differences in this section.</para>
<para>The AspectJ interceptor is named <literal>AspectJSecurityInterceptor</literal>. Unlike
the AOP Alliance security interceptor, which relies on the Spring application context to
weave in the security interceptor via proxying, the
<literal>AspectJSecurityInterceptor</literal> is weaved in via the AspectJ compiler. It
would not be uncommon to use both types of security interceptors in the same
application, with <literal>AspectJSecurityInterceptor</literal> being used for domain
object instance security and the AOP Alliance
<classname>MethodSecurityInterceptor</classname> being used for services layer
security.</para>
<para>Let's first consider how the <literal>AspectJSecurityInterceptor</literal> is
configured in the Spring application context:</para>
<programlisting language="xml"><![CDATA[
<bean id="bankManagerSecurity" class=
"org.springframework.security.access.intercept.aspectj.AspectJMethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="afterInvocationManager" ref="afterInvocationManager"/>
<property name="securityMetadataSource">
<sec:method-security-metadata-source>
<sec:protect method="com.mycompany.BankManager.delete*" access="ROLE_SUPERVISOR"/>
<sec:protect method="com.mycompany.BankManager.getBalance" access="ROLE_TELLER,ROLE_SUPERVISOR"/>
</sec:method-security-metadata-source>
</property>
</bean>]]> </programlisting>
<para>As you can see, aside from the class name, the
<literal>AspectJSecurityInterceptor</literal> is exactly the same as the AOP Alliance
security interceptor. Indeed the two interceptors can share the same
<literal>securityMetadataSource</literal>, as the
<interfacename>SecurityMetadataSource</interfacename> works with
<literal>java.lang.reflect.Method</literal>s rather than an AOP library-specific class.
Of course, your access decisions have access to the relevant AOP library-specific
invocation (ie <classname>MethodInvocation</classname> or <literal>JoinPoint</literal>)
and as such can consider a range of addition criteria when making access decisions (such
as method arguments).</para>
<para>Next you'll need to define an AspectJ <literal>aspect</literal>. For example:</para>
<programlisting language="java">
package org.springframework.security.samples.aspectj;
import org.springframework.security.access.intercept.aspectj.AspectJSecurityInterceptor;
import org.springframework.security.access.intercept.aspectj.AspectJCallback;
import org.springframework.beans.factory.InitializingBean;
public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
private AspectJSecurityInterceptor securityInterceptor;
pointcut domainObjectInstanceExecution(): target(PersistableEntity)
&amp;&amp; execution(public * *(..)) &amp;&amp; !within(DomainObjectInstanceSecurityAspect);
Object around(): domainObjectInstanceExecution() {
if (this.securityInterceptor == null) {
return proceed();
}
AspectJCallback callback = new AspectJCallback() {
public Object proceedWithObject() {
return proceed();
}
};
return this.securityInterceptor.invoke(thisJoinPoint, callback);
}
public AspectJSecurityInterceptor getSecurityInterceptor() {
return securityInterceptor;
}
public void setSecurityInterceptor(AspectJSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
public void afterPropertiesSet() throws Exception {
if (this.securityInterceptor == null)
throw new IllegalArgumentException("securityInterceptor required");
}
}
}
</programlisting>
<para>In the above example, the security interceptor will be applied to every instance of
<literal>PersistableEntity</literal>, which is an abstract class not shown (you can use
any other class or <literal>pointcut</literal> expression you like). For those curious,
<literal>AspectJCallback</literal> is needed because the <literal>proceed();</literal>
statement has special meaning only within an <literal>around()</literal> body. The
<literal>AspectJSecurityInterceptor</literal> calls this anonymous
<literal>AspectJCallback</literal> class when it wants the target object to
continue.</para>
<para>You will need to configure Spring to load the aspect and wire it with the
<literal>AspectJSecurityInterceptor</literal>. A bean declaration which achieves this is
shown below:</para>
<programlisting language="xml"><![CDATA[
<bean id="domainObjectInstanceSecurityAspect"
class="security.samples.aspectj.DomainObjectInstanceSecurityAspect"
factory-method="aspectOf">
<property name="securityInterceptor" ref="bankManagerSecurity"/>
</bean>]]>
</programlisting>
<para>That's it! Now you can create your beans from anywhere within your application, using
whatever means you think fit (eg <literal>new Person();</literal>) and they will have
the security interceptor applied.</para>
</section>
</chapter>

View File

@ -1,338 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="security-filter-chain"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>The Security Filter Chain</title>
</info>
<para>Spring Security's web infrastructure is based entirely on standard servlet filters. It
doesn't use servlets or any other servlet-based frameworks (such as Spring MVC) internally,
so it has no strong links to any particular web technology. It deals in
<classname>HttpServletRequest</classname>s and <classname>HttpServletResponse</classname>s
and doesn't care whether the requests come from a browser, a web service client, an
<classname>HttpInvoker</classname> or an AJAX application. </para>
<para> Spring Security maintains a filter chain internally where each of the filters has a
particular responsibility and filters are added or removed from the configuration depending
on which services are required. The ordering of the filters is important as there are
dependencies between them. If you have been using <link linkend="ns-config">namespace
configuration</link>, then the filters are automatically configured for you and you don't
have to define any Spring beans explicitly but here may be times when you want full control
over the security filter chain, either because you are using features which aren't supported
in the namespace, or you are using your own customized versions of classes.</para>
<section xml:id="delegating-filter-proxy">
<title><classname>DelegatingFilterProxy</classname></title>
<para> When using servlet filters, you obviously need to declare them in your
<filename>web.xml</filename>, or they will be ignored by the servlet container. In
Spring Security, the filter classes are also Spring beans defined in the application
context and thus able to take advantage of Spring's rich dependency-injection facilities
and lifecycle interfaces. Spring's <classname>DelegatingFilterProxy</classname> provides
the link between <filename>web.xml</filename> and the application context. </para>
<para>When using <classname>DelegatingFilterProxy</classname>, you will see something like
this in the <filename>web.xml</filename> file: <programlisting language="xml"><![CDATA[
<filter>
<filter-name>myFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>]]>
</programlisting> Notice that the filter is actually a
<literal>DelegatingFilterProxy</literal>, and not the class that will actually implement
the logic of the filter. What <classname>DelegatingFilterProxy</classname> does is
delegate the <interfacename>Filter</interfacename>'s methods through to a bean which is
obtained from the Spring application context. This enables the bean to benefit from the
Spring web application context lifecycle support and configuration flexibility. The bean
must implement <interfacename>javax.servlet.Filter</interfacename> and it must have the
same name as that in the <literal>filter-name</literal> element. Read the Javadoc for
<classname>DelegatingFilterProxy</classname> for more information</para>
</section>
<section xml:id="filter-chain-proxy">
<title><classname>FilterChainProxy</classname></title>
<para>Spring Security's web infrastructure should only be used by delegating to an
instance of <classname>FilterChainProxy</classname>. The security filters should not
be used by themselves. In theory you could declare each Spring Security filter bean
that you require in your application context file and add a corresponding
<classname>DelegatingFilterProxy</classname> entry to <filename>web.xml</filename>
for each filter, making sure that they are ordered correctly, but this would be
cumbersome and would clutter up the <filename>web.xml</filename> file quickly if you
have a lot of filters. <classname>FilterChainProxy</classname> lets us add a single
entry to <filename>web.xml</filename> and deal entirely with the application context
file for managing our web security beans. It is wired using a
<literal>DelegatingFilterProxy</literal>, just like in the example above, but with
the <literal>filter-name</literal> set to the bean name
<quote>filterChainProxy</quote>. The filter chain is then declared in the application
context with the same bean name. Here's an example: <programlisting language="xml"><![CDATA[
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
<constructor-arg>
<list>
<sec:filter-chain pattern="/restful/**" filters="
securityContextPersistenceFilterWithASCFalse,
basicAuthenticationFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
<sec:filter-chain pattern="/**" filters="
securityContextPersistenceFilterWithASCTrue,
formLoginFilter,
exceptionTranslationFilter,
filterSecurityInterceptor" />
</list>
</constructor-arg>
</bean>
]]>
</programlisting> The namespace element <literal>filter-chain</literal> is used for convenience
to set up the security filter chain(s) which are required within the application.
<footnote><para>Note that you'll need to include the security namespace in your application
context XML file in order to use this syntax. The older syntax which used a
<literal>filter-chain-map</literal> is still supported, but is deprecated in favour of
the constructor argument injection.</para>
</footnote>. It maps a particular URL pattern to a list of filters built up from the
bean names specified in the <literal>filters</literal> element, and combines them in
a bean of type <classname>SecurityFilterChain</classname>. The <literal>pattern</literal>
attribute takes an Ant Paths and the most specific URIs should appear first
<footnote><para>Instead of a path pattern, the <literal>request-matcher-ref</literal> attribute
can be used to specify a <interfacename>RequestMatcher</interfacename> instance for more powerful
matching</para></footnote>. At runtime the <classname>FilterChainProxy</classname> will
locate the first URI pattern that matches the current web request and the list of filter beans
specified by the <literal>filters</literal> attribute will be applied to that request.
The filters will be invoked in the order they are defined, so you have complete control
over the filter chain which is applied to a particular URL.</para>
<para>You may have noticed we have declared two
<classname>SecurityContextPersistenceFilter</classname>s in the filter chain
(<literal>ASC</literal> is short for <literal>allowSessionCreation</literal>, a property
of <classname>SecurityContextPersistenceFilter</classname>). As web services will never
present a <literal>jsessionid</literal> on future requests, creating
<literal>HttpSession</literal>s for such user agents would be wasteful. If you had a
high-volume application which required maximum scalability, we recommend you use the
approach shown above. For smaller applications, using a single
<classname>SecurityContextPersistenceFilter</classname> (with its default
<literal>allowSessionCreation</literal> as <literal>true</literal>) would likely be
sufficient.</para>
<para>Note that <classname>FilterChainProxy</classname> does not invoke standard filter
lifecycle methods on the filters it is configured with. We recommend you use
Spring's application context lifecycle interfaces as an alternative, just as you
would for any other Spring bean.</para>
<para> When we looked at how to set up web security using <link linkend="ns-web-xml"
>namespace configuration</link>, we used a <literal>DelegatingFilterProxy</literal> with
the name <quote>springSecurityFilterChain</quote>. You should now be able to see that
this is the name of the <classname>FilterChainProxy</classname> which is created by the
namespace. </para>
<section>
<title>Bypassing the Filter Chain</title>
<para> You can use the attribute <literal>filters =
"none"</literal> as an alternative to supplying a filter bean list. This will omit
the request pattern from the security filter chain entirely. Note that anything
matching this path will then have no authentication or authorization services
applied and will be freely accessible. If you want to make use of the contents of
the <classname>SecurityContext</classname> contents during a request, then it must
have passed through the security filter chain. Otherwise the
<classname>SecurityContextHolder</classname> will not have been populated and the
contents will be null.</para>
</section>
</section>
<section>
<title>Filter Ordering</title>
<para>The order that filters are defined in the chain is very important. Irrespective of
which filters you are actually using, the order should be as follows: <orderedlist>
<listitem>
<para><classname>ChannelProcessingFilter</classname>, because it might need to
redirect to a different protocol</para>
</listitem>
<listitem>
<para><classname>SecurityContextPersistenceFilter</classname>, so a
<interfacename>SecurityContext</interfacename> can be set up in the
<classname>SecurityContextHolder</classname> at the beginning of a web request,
and any changes to the <interfacename>SecurityContext</interfacename> can be
copied to the <literal>HttpSession</literal> when the web request ends (ready
for use with the next web request)</para>
</listitem>
<listitem>
<para><classname>ConcurrentSessionFilter</classname>, because it uses the
<classname>SecurityContextHolder</classname> functionality and needs to update
the <interfacename>SessionRegistry</interfacename> to reflect ongoing requests
from the principal</para>
</listitem>
<listitem>
<para>Authentication processing mechanisms -
<classname>UsernamePasswordAuthenticationFilter</classname>,
<classname>CasAuthenticationFilter</classname>,
<classname>BasicAuthenticationFilter</classname> etc - so that the
<classname>SecurityContextHolder</classname> can be modified to contain a valid
<interfacename>Authentication</interfacename> request token</para>
</listitem>
<listitem>
<para>The <literal>SecurityContextHolderAwareRequestFilter</literal>, if you are
using it to install a Spring Security aware
<literal>HttpServletRequestWrapper</literal> into your servlet container</para>
</listitem>
<listitem>
<para>The <classname>JaasApiIntegrationFilter</classname>, if a
<classname>JaasAuthenticationToken</classname> is in the
<classname>SecurityContextHolder</classname> this will process the
<classname>FilterChain</classname> as the <classname>Subject</classname> in the
<classname>JaasAuthenticationToken</classname></para>
</listitem>
<listitem>
<para><classname>RememberMeAuthenticationFilter</classname>, so that if no earlier
authentication processing mechanism updated the
<classname>SecurityContextHolder</classname>, and the request presents a cookie
that enables remember-me services to take place, a suitable remembered
<interfacename>Authentication</interfacename> object will be put there</para>
</listitem>
<listitem>
<para><classname>AnonymousAuthenticationFilter</classname>, so that if no earlier
authentication processing mechanism updated the
<classname>SecurityContextHolder</classname>, an anonymous
<interfacename>Authentication</interfacename> object will be put there</para>
</listitem>
<listitem>
<para><classname>ExceptionTranslationFilter</classname>, to catch any Spring
Security exceptions so that either an HTTP error response can be returned or an
appropriate <interfacename>AuthenticationEntryPoint</interfacename> can be
launched</para>
</listitem>
<listitem>
<para><classname>FilterSecurityInterceptor</classname>, to protect web URIs and
raise exceptions when access is denied</para>
</listitem>
</orderedlist></para>
</section>
<section xml:id="request-matching">
<title>Request Matching and <interfacename>HttpFirewall</interfacename></title>
<para>Spring Security has several areas where patterns you have defined are tested
against incoming requests in order to decide how the request should be handled. This
occurs when the <classname>FilterChainProxy</classname> decides which filter chain a
request should be passed through and also when the
<classname>FilterSecurityInterceptor</classname> decides which security constraints
apply to a request. It's important to understand what the mechanism is and what URL
value is used when testing against the patterns that you define.</para>
<para>The Servlet Specification defines several properties for the
<interfacename>HttpServletRequest</interfacename> which are accessible via getter
methods, and which we might want to match against. These are the
<literal>contextPath</literal>, <literal>servletPath</literal>,
<literal>pathInfo</literal> and <literal>queryString</literal>. Spring Security is
only interested in securing paths within the application, so the
<literal>contextPath</literal> is ignored. Unfortunately, the servlet spec does not
define exactly what the values of <literal>servletPath</literal> and
<literal>pathInfo</literal> will contain for a particular request URI. For example,
each path segment of a URL may contain parameters, as defined in <link
xlink:href="http://www.ietf.org/rfc/rfc2396.txt">RFC 2396</link><footnote>
<para>You have probably seen this when a browser doesn't support cookies and the
<literal>jsessionid</literal> parameter is appended to the URL after a
semi-colon. However the RFC allows the presence of these parameters in any path
segment of the URL</para>
</footnote>. The Specification does not clearly state whether these should be
included in the <literal>servletPath</literal> and <literal>pathInfo</literal>
values and the behaviour varies between different servlet containers. There is a
danger that when an application is deployed in a container which does not strip path
parameters from these values, an attacker could add them to the requested URL in
order to cause a pattern match to succeed or fail unexpectedly.<footnote>
<para>The original values will be returned once the request leaves the
<classname>FilterChainProxy</classname>, so will still be available to the
application.</para>
</footnote>. Other variations in the incoming URL are also possible. For example, it
could contain path-traversal sequences (like <literal>/../</literal>) or multiple
forward slashes (<literal>//</literal>) which could also cause pattern-matches to
fail. Some containers normalize these out before performing the servlet mapping, but
others don't. To protect against issues like these,
<classname>FilterChainProxy</classname> uses an
<interfacename>HttpFirewall</interfacename> strategy to check and wrap the request.
Un-normalized requests are automatically rejected by default, and path parameters
and duplicate slashes are removed for matching purposes.<footnote>
<para>So, for example, an original request path
<literal>/secure;hack=1/somefile.html;hack=2</literal> will be returned as
<literal>/secure/somefile.html</literal>.</para>
</footnote>. It is therefore essential that a
<classname>FilterChainProxy</classname> is used to manage the security filter chain.
Note that the <literal>servletPath</literal> and <literal>pathInfo</literal> values
are decoded by the container, so your application should not have any valid paths
which contain semi-colons, as these parts will be removed for matching purposes. </para>
<para>As mentioned above, the default strategy is to use Ant-style paths for matching
and this is likely to be the best choice for most users. The strategy is implemented
in the class <classname>AntPathRequestMatcher</classname> which uses Spring's
<classname>AntPathMatcher</classname> to perform a case-insensitive match of the
pattern against the concatenated <literal>servletPath</literal> and
<literal>pathInfo</literal>, ignoring the <literal>queryString</literal>.</para>
<para>If for some reason, you need a more powerful matching strategy, you can use
regular expressions. The strategy implementation is then
<classname>RegexRequestMatcher</classname>. See the Javadoc for this class for more
information.</para>
<para>In practice we recommend that you use method security at your service layer, to
control access to your application, and do not rely entirely on the use of security
constraints defined at the web-application level. URLs change and it is difficult to
take account of all the possible URLs that an application might support and how
requests might be manipulated. You should try and restrict yourself to using a few
simple ant paths which are simple to understand. Always try to use a
<quote>deny-by-default</quote> approach where you have a catch-all wildcard
(<literal>/**</literal> or <literal>**</literal>) defined last and denying access.</para>
<para>Security defined at the service layer is much more robust and harder to bypass, so
you should always take advantage of Spring Security's method security
options.</para>
</section>
<section>
<title>Use with other Filter-Based Frameworks</title>
<para>If you're using some other framework that is also filter-based, then you need to make
sure that the Spring Security filters come first. This enables the
<classname>SecurityContextHolder</classname> to be populated in time for use by the
other filters. Examples are the use of SiteMesh to decorate your web pages or a web
framework like Wicket which uses a filter to handle its requests. </para>
</section>
<section xml:id="filter-chains-with-ns">
<title>Advanced Namespace Configuration</title>
<para>As we saw earlier in the namespace chapter, it's possible to use multiple <literal>http</literal>
elements to define different security configurations for different URL patterns.
Each element creates a filter chain within the internal <classname>FilterChainProxy</classname> and the
URL pattern that should be mapped to it. The elements will be added in the order they are declared, so the
most specific patterns must again be declared first. Here's another example, for a similar situation to
that above, where the application supports both a stateless RESTful API and also a normal web application
which users log into using a form.
<programlisting language="xml">
<![CDATA[
<!-- Stateless RESTful service using Basic authentication -->
<http pattern="/restful/**" create-session="stateless">
<intercept-url pattern='/**' access='ROLE_REMOTE' />
<http-basic />
</http>
<!-- Empty filter chain for the login page -->
<http pattern="/login.htm*" security="none"/>
<!-- Additional filter chain for normal users, matching all other requests -->
<http>
<intercept-url pattern='/**' access='ROLE_USER' />
<form-login login-page='/login.htm' default-target-url="/home.htm"/>
<logout />
</http>
]]>
</programlisting>
</para>
</section>
<!--
<section xml:id="taglib">
<info>
<title>Tag Libraries</title>
</info>
<para>Spring Security comes bundled with several JSP tag libraries which provide a range of
different services.</para>
<section xml:id="taglib-config">
<info>
<title>Configuration</title>
</info>
<para>All taglib classes are included in the core
<literal>spring-security-taglibs-&lt;version>.jar</literal> file, with the
<literal>security.tld</literal> located in the JAR's <literal>META-INF</literal>
directory. This means for JSP 1.2+ web containers you can simply include the JAR in the
WAR's <literal>WEB-INF/lib</literal> directory and it will be available.</para>
</section>
<section xml:id="taglib-usage">
<info>
<title>Usage</title>
</info>
<para>Now that you've configured the tag libraries, refer to the individual reference guide
sections for details on how to use them. Note that when using the tags, you should include
the taglib reference in your JSP:
<programlisting language="xml">
&lt;%@ taglib prefix='security' uri='http://www.springframework.org/security/tags' %&gt;
</programlisting></para>
</section>
</section>-->
</chapter>

View File

@ -1,167 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="servletapi"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Servlet API integration</title>
</info>
<para>This section describes how Spring Security is integrated with the Servlet API. The
<link xlink:href="https://github.com/SpringSource/spring-security/blob/master/samples/servletapi-xml">servletapi-xml</link> sample application demonstrates
the usage of each of these methods.</para>
<section xml:id="servletapi-25">
<title>Servlet 2.5+ Integration</title>
<section xml:id="servletapi-remote-user">
<title>HttpServletRequest.getRemoteUser()</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getRemoteUser()">HttpServletRequest.getRemoteUser()</link>
will return the result of <literal>SecurityContextHolder.getContext().getAuthentication().getName()</literal> which is typically the current
username. This can be useful if you want to display the current username in your application. Additionally, checking if this
is null can be used to indicate if a user has authenticated or is anonymous. Knowing if the user is authenticated or not can
be useful for determining if certain UI elements should be shown or not (i.e. a log out link should only be displayed if the
user is authenticated).</para>
</section>
<section xml:id="servletapi-user-principal">
<title>HttpServletRequest.getUserPrincipal()</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#getUserPrincipal()">HttpServletRequest.getUserPrincipal()</link>
will return the result of <literal>SecurityContextHolder.getContext().getAuthentication()</literal>. This means it is an <interfacename>Authentication</interfacename>
which is typically an instance of <classname>UsernamePasswordAuthenticationToken</classname> when using username and password based authentication. This can be useful if
you need additional information about your user. For example, you might have created a custom <interfacename>UserDetailsService</interfacename>
that returns a custom <interfacename>UserDetails</interfacename> containing a first and last name for your user. You could obtain this information with the
following:</para>
<programlisting language="java"><![CDATA[Authentication auth = httpServletRequest.getUserPrincipal();
// assume integrated custom UserDetails called MyCustomUserDetails
// by default, typically instance of UserDetails
MyCustomUserDetails userDetails = (MyCustomUserDetails) auth.getPrincipal();
String firstName = userDetails.getFirstName();
String lastName = userDetails.getLastName();
]]></programlisting>
<note>
<para>It should be noted that it is typically bad practice to perform so much logic throughout your application. Instead, one should centralize it to reduce
any coupling of Spring Security and the Servlet API's.
</para>
</note>
</section>
<section xml:id="servletapi-user-in-role">
<title>HttpServletRequest.isUserInRole(String)</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#isUserInRole(java.lang.String)">HttpServletRequest.isUserInRole(String)</link>
will determine if <literal>SecurityContextHolder.getContext().getAuthentication().getAuthorities()</literal> contains a
<interfacename>GrantedAuthority</interfacename> with the role passed into <literal>isUserInRole(String)</literal>. Typically users should not pass in the "ROLE_" prefix
into this method since it is added automatically. For example, if you want to determine if the current user has the authority "ROLE_ADMIN", you could use the
the following:</para>
<programlisting language="java"><![CDATA[boolean isAdmin = httpServletRequest.isUserInRole("ADMIN");]]></programlisting>
<para>This might be useful to determine if certain UI components should be displayed. For example, you might display admin links only if the current
user is an admin.</para>
</section>
</section>
<section xml:id="servletapi-3">
<title>Servlet 3+ Integration</title>
<para>The following section describes the Servlet 3 methods that Spring Security integrates with.</para>
<section xml:id="servletapi-authenticate">
<title>HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#authenticate%28javax.servlet.http.HttpServletResponse%29">HttpServletRequest.authenticate(HttpServletRequest,HttpServletResponse)</link>
method can be used to ensure that a user is authenticated. If they are not authenticated, the configured AuthenticationEntryPoint will be used to request the user to authenticate
(i.e. redirect to the login page).</para>
</section>
<section xml:id="servletapi-login">
<title>HttpServletRequest.login(String,String)</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#login%28java.lang.String,%20java.lang.String%29">HttpServletRequest.login(String,String)</link>
method can be used to authenticate the user with the current <interfacename>AuthenticationManager</interfacename>. For example, the following would attempt to
authenticate with the username "user" and password "password":</para>
<programlisting language="java"><![CDATA[try {
httpServletRequest.login("user","password");
} catch(ServletException e) {
// fail to authenticate
}]]></programlisting>
<note>
<para>It is not necessary to catch the ServletException if you want Spring Security to process the failed authentication attempt.</para>
</note>
</section>
<section xml:id="servletapi-logout">
<title>HttpServletRequest.logout()</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequest.html#logout%28%29">HttpServletRequest.logout()</link>
method can be used to log the current user out.</para>
<para>Typically this means that the SecurityContextHolder will be cleared out, the HttpSession will be invalidated, any "Remember Me" authentication will be
cleaned up, etc. However, the configured LogoutHandler implementations will vary depending on your Spring Security configuration. It is important to note
that after HttpServletRequest.logout() has been invoked, you are still in charge of writing a response out. Typically this would involve a redirect to the
welcome page.</para>
</section>
<section xml:id="servletapi-start-runnable">
<title>AsyncContext.start(Runnable)</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html#start%28java.lang.Runnable%29">AsynchContext.start(Runnable)</link>
method that ensures your credentials will be propagated to the new Thread. Using Spring Security's concurrency support, Spring Security overrides
the AsyncContext.start(Runnable) to ensure that the current SecurityContext is used when processing the Runnable. For example, the following
would output the current user's Authentication:</para>
<programlisting language="java"><![CDATA[final AsyncContext async = httpServletRequest.startAsync();
async.start(new Runnable() {
public void run() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
try {
final HttpServletResponse asyncResponse = (HttpServletResponse) async.getResponse();
asyncResponse.setStatus(HttpServletResponse.SC_OK);
asyncResponse.getWriter().write(String.valueOf(authentication));
async.complete();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
});]]></programlisting>
</section>
<section xml:id="servletapi-async">
<title>Async Servlet Support</title>
<para>If you are using Java Based configuration, you are ready to go. If you are using XML configuration, there are
a few updates that are necessary. The first step is to ensure you have updated your web.xml to use at least the 3.0 schema
as shown below:</para>
<programlisting language="xml"><![CDATA[<web-app 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_3_0.xsd"
version="3.0">
</web-app>]]></programlisting>
<para>Next you need to ensure that your springSecurityFilterChain is setup for processing asynchronous requests.</para>
<programlisting language="xml"><![CDATA[<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ASYNC</dispatcher>
</filter-mapping>]]></programlisting>
<para>That's it! Now Spring Security will ensure that your SecurityContext is propagated on asynchronous requests too.</para>
<para>So how does it work? If you are not really interested, feel free to skip the remainder of this section, otherwise read on. Most of this
is built into the Servlet specification, but there is a little bit of tweaking that Spring Security does to ensure things work with
asynchronous requests properly. Prior to Spring Security 3.2, the SecurityContext from the SecurityContextHolder was automatically saved as soon
as the HttpServletResponse was committed. This can cause issues in a Async environment. For example, consider the following:</para>
<programlisting language="java"><![CDATA[httpServletRequest.startAsync();
new Thread("AsyncThread") {
@Override
public void run() {
try {
// Do work
TimeUnit.SECONDS.sleep(1);
// Write to and commit the httpServletResponse
httpServletResponse.getOutputStream().flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();]]></programlisting>
<para>The issue is that this Thread is not known to Spring Security, so the SecurityContext is not propagated to it. This means when we commit the
HttpServletResponse there is no SecuriytContext. When Spring Security automatically saved the SecurityContext on committing the HttpServletResponse it
would lose our logged in user.</para>
<para>Since version 3.2, Spring Security is smart enough to no longer automatically save the SecurityContext on commiting the HttpServletResponse
as soon as HttpServletRequest.startAsync() is invoked.</para>
</section>
</section>
<section xml:id="servletapi-31">
<title>Servlet 3.1+ Integration</title>
<para>The following section describes the Servlet 3.1 methods that Spring Security integrates with.</para>
<section xml:id="servletapi-change-session-id">
<title>HttpServletRequest#changeSessionId()</title>
<para>The <link xlink:href="http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#changeSessionId()">HttpServletRequest.changeSessionId()</link>
is the default method for protecting against <link linkend="ns-session-fixation">Session Fixation</link> attacks in Servlet 3.1 and higher.</para>
</section>
</section>
</chapter>

View File

@ -1,181 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="session-mgmt"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Session Management</title>
</info>
<para>HTTP session related functonality is handled by a combination of the
<classname>SessionManagementFilter</classname> and the
<interfacename>SessionAuthenticationStrategy</interfacename> interface, which the filter
delegates to. Typical usage includes session-fixation protection attack prevention,
detection of session timeouts and restrictions on how many sessions an authenticated user
may have open concurrently.</para>
<section>
<title>SessionManagementFilter</title>
<para>The <classname>SessionManagementFilter</classname> checks the contents of the
<interfacename>SecurityContextRepository</interfacename> against the current contents of
the <classname>SecurityContextHolder</classname> to determine whether a user has been
authenticated during the current request, typically by a non-interactive authentication
mechanism, such as pre-authentication or remember-me <footnote>
<para>Authentication by mechanisms which perform a redirect after authenticating (such
as form-login) will not be detected by
<classname>SessionManagementFilter</classname>, as the filter will not be invoked
during the authenticating request. Session-management functionality has to be
handled separately in these cases. </para>
</footnote>. If the repository contains a security context, the filter does nothing. If
it doesn't, and the thread-local <interfacename>SecurityContext</interfacename> contains
a (non-anonymous) <interfacename>Authentication</interfacename> object, the filter
assumes they have been authenticated by a previous filter in the stack. It will then
invoke the configured
<interfacename>SessionAuthenticationStrategy</interfacename>.</para>
<para>If the user is not currently authenticated, the filter will check whether an invalid
session ID has been requested (because of a timeout, for example) and will invoke the configured
<interfacename>InvalidSessionStrategy</interfacename>, if one is set. The most common behaviour
is just to redirect to a fixed URL and this is encapsulated in the standard implementation
<classname>SimpleRedirectInvalidSessionStrategy</classname>. The latter is also used
when configuring an invalid session URL through the namespace,
<link linkend="ns-session-mgmt">as described earlier</link>.</para>
</section>
<section>
<title><interfacename>SessionAuthenticationStrategy</interfacename></title>
<para> <interfacename>SessionAuthenticationStrategy</interfacename> is used by both
<classname>SessionManagementFilter</classname> and
<classname>AbstractAuthenticationProcessingFilter</classname>, so if you are using a
customized form-login class, for example, you will need to inject it into both of these.
In this case, a typical configuration, combining the namespace and custom beans might
look like this:<programlisting language="xml"><![CDATA[
<http>
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>
<beans:bean id="myAuthFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
...
</beans:bean>
<beans:bean id="sas" class=
"org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
]]></programlisting>
Note that the use of the default, <classname>SessionFixationProtectionStrategy</classname>
may cause issues if you are storing beans in the session which implement
<interfacename>HttpSessionBindingListener</interfacename>, including Spring session-scoped
beans. See the Javadoc for this class for more information.
</para>
</section>
<section xml:id="concurrent-sessions">
<title>Concurrency Control</title>
<para>Spring Security is able to prevent a principal from concurrently authenticating to the
same application more than a specified number of times. Many ISVs take advantage of this
to enforce licensing, whilst network administrators like this feature because it helps
prevent people from sharing login names. You can, for example, stop user
<quote>Batman</quote> from logging onto the web application from two different sessions.
You can either expire their previous login or you can report an error when they try to
log in again, preventing the second login. Note that if you are using the second
approach, a user who has not explicitly logged out (but who has just closed their
browser, for example) will not be able to log in again until their original session
expires.</para>
<para>Concurrency control is supported by the namespace, so please check the earlier
namespace chapter for the simplest configuration. Sometimes you need to customize things
though. </para>
<para>The implementation uses a specialized version of
<interfacename>SessionAuthenticationStrategy</interfacename>, called
<classname>ConcurrentSessionControlAuthenticationStrategy</classname>. <note>
<para>Previously the concurrent authentication check was made by the
<classname>ProviderManager</classname>, which could be injected with a
<literal>ConcurrentSessionController</literal>. The latter would check if the user
was attempting to exceed the number of permitted sessions. However, this approach
required that an HTTP session be created in advance, which is undesirable. In Spring
Security 3, the user is first authenticated by the
<interfacename>AuthenticationManager</interfacename> and once they are successfully
authenticated, a session is created and the check is made whether they are allowed
to have another session open.</para>
</note></para>
<para>To use concurrent session support, you'll need to add the following to
<literal>web.xml</literal>: <programlisting language="xml"><![CDATA[
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener> ]]>
</programlisting></para>
<para>In addition, you will need to add the <literal>ConcurrentSessionFilter</literal> to
your <classname>FilterChainProxy</classname>. The
<classname>ConcurrentSessionFilter</classname> requires two properties,
<literal>sessionRegistry</literal>, which generally points to an instance of
<classname>SessionRegistryImpl</classname>, and <literal>expiredUrl</literal>, which
points to the page to display when a session has expired. A configuration using the
namespace to create the <classname>FilterChainProxy</classname> and other default beans
might look like this: <programlisting language="xml"><![CDATA[
<http>
<custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />
<custom-filter position="FORM_LOGIN_FILTER" ref="myAuthFilter" />
<session-management session-authentication-strategy-ref="sas"/>
</http>
<beans:bean id="concurrencyFilter"
class="org.springframework.security.web.session.ConcurrentSessionFilter">
<beans:property name="sessionRegistry" ref="sessionRegistry" />
<beans:property name="expiredUrl" value="/session-expired.htm" />
</beans:bean>
<beans:bean id="myAuthFilter" class=
"org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="sessionAuthenticationStrategy" ref="sas" />
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="sas" class="org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy">
<beans:constructor-arg>
<beans:list>
<beans:bean class="org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy">
<beans:constructor-arg ref="sessionRegistry"/>
<beans:property name="maximumSessions" value="1" />
<beans:property name="exceptionIfMaximumExceeded" value="true" />
</beans:bean>
<beans:bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy">
</beans:bean>
<beans:bean class="org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy">
<beans:constructor-arg ref="sessionRegistry"/>
</beans:bean>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="sessionRegistry"
class="org.springframework.security.core.session.SessionRegistryImpl" />
]]>
</programlisting></para>
<para>Adding the listener to <filename>web.xml</filename> causes an
<literal>ApplicationEvent</literal> to be published to the Spring
<literal>ApplicationContext</literal> every time a <literal>HttpSession</literal>
commences or terminates. This is critical, as it allows the
<classname>SessionRegistryImpl</classname> to be notified when a session ends. Without
it, a user will never be able to log back in again once they have exceeded their session
allowance, even if they log out of another session or it times out.</para>
<section xml:id="list-authenticated-principals">
<title>Querying the <interfacename>SessionRegistry</interfacename> for currently authenticated
users and their sessions</title>
<para>
Setting up concurrency-control, either through the namespace or using plain beans has the
useful side effect of providing you with a reference to the <interfacename>SessionRegistry</interfacename>
which you can use directly within your application, so even if you don't want to restrict the
number of sessions a user may have, it may be worth setting up the infrastructure anyway. You can
set the <literal>maximumSession</literal> property to -1 to allow unlimited sessions. If
you're using the namespace, you can set an alias for the internally-created
<interfacename>SessionRegistry</interfacename> using the <literal>session-registry-alias</literal>
attribute, providing a reference which you can inject into your own beans.</para>
<para>
The <methodname>getAllPrincipals()</methodname>
method supplies you with a list of the currently authenticated users. You can list a user's
sessions by calling the <methodname>getAllSessions(Object principal, boolean includeExpiredSessions)</methodname> method,
which returns a list of <classname>SessionInformation</classname> objects. You can also
expire a user's session by calling <methodname>expireNow()</methodname> on a
<methodname>SessionInformation</methodname> instance. When the user returns to the application, they
will be prevented from proceeding. You may find these methods useful in an administration
application, for example. Have a look at the Javadoc for more information.
</para>
</section>
</section>
</chapter>

View File

@ -1,117 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="taglibs"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>JSP Tag Libraries</title>
<para> Spring Security has its own taglib which provides basic support for accessing security
information and applying security constraints in JSPs. </para>
<section>
<title>Declaring the Taglib</title>
<para>To use any of the tags, you must have the security taglib declared in your JSP:
<programlisting language="xml">
<![CDATA[<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>]]>
</programlisting></para>
</section>
<section>
<title>The <literal>authorize</literal> Tag</title>
<para> This tag is used to determine whether its contents should be evaluated or not. In
Spring Security 3.0, it can be used in two ways <footnote>
<para>The legacy options from Spring Security 2.0 are also supported, but
discouraged.</para>
</footnote>. The first approach uses a <link linkend="el-access-web">web-security
expression</link>, specified in the <literal>access</literal> attribute of the tag. The
expression evaluation will be delegated to the
<interfacename>SecurityExpressionHandler&lt;FilterInvocation&gt;</interfacename> defined in the application
context (you should have web expressions enabled in your <literal>&lt;http></literal>
namespace configuration to make sure this service is available). So, for example, you
might
have<programlisting language="xml">&lt;sec:authorize access="hasRole('supervisor')">
This content will only be visible to users who have
the "supervisor" authority in their list of &lt;tt>GrantedAuthority&lt;/tt>s.
&lt;/sec:authorize></programlisting></para>
<para>A common requirement is to only show a particular link, if the user is actually
allowed to click it. How can we determine in advance whether something will be allowed?
This tag can also operate in an alternative mode which allows you to define a particular
URL as an attribute. If the user is allowed to invoke that URL, then the tag body will
be evaluated, otherwise it will be skipped. So you might have something
like<programlisting language="xml">&lt;sec:authorize url="/admin">
This content will only be visible to users who are authorized to send requests to the "/admin" URL.
&lt;/sec:authorize></programlisting>To
use this tag there must also be an instance of
<interfacename>WebInvocationPrivilegeEvaluator</interfacename> in your application
context. If you are using the namespace, one will automatically be registered. This is
an instance of <classname>DefaultWebInvocationPrivilegeEvaluator</classname>, which
creates a dummy web request for the supplied URL and invokes the security interceptor to
see whether the request would succeed or fail. This allows you to delegate to the
access-control setup you defined using <literal>intercept-url</literal> declarations
within the <literal>&lt;http></literal> namespace configuration and saves having to
duplicate the information (such as the required roles) within your JSPs. This approach
can also be combined with a <literal>method</literal> attribute, supplying the HTTP
method, for a more specific match.</para>
<para>The boolean result of evaluating the tag (whether it grants or denies access) can be
stored in a page context scope variable by setting the <literal>var</literal> attribute
to the variable name, avoiding the need for duplicating and re-evaluating the condition
at other points in the page.</para>
<section>
<title>Disabling Tag Authorization for Testing</title>
<para>Hiding a link in a page for unauthorized users doesn't prevent them from accessing
the URL. They could just type it into their browser directly, for example. As part
of your testing process, you may want to reveal the hidden areas in order to check
that links really are secured at the back end. If you set the system property
<literal>spring.security.disableUISecurity</literal> to <literal>true</literal>,
the <literal>authorize</literal> tag will still run but will not hide its contents.
By default it will also surround the content with <literal>&lt;span
class="securityHiddenUI">...&lt;/span></literal> tags. This allows you to
display <quote>hidden</quote> content with a particular CSS style such as a
different background colour. Try running the <quote>tutorial</quote> sample
application with this property enabled, for example.</para>
<para>You can also set the properties <literal>spring.security.securedUIPrefix</literal>
and <literal>spring.security.securedUISuffix</literal> if you want to change
surrounding text from the default <literal>span</literal> tags (or use empty strings
to remove it completely).</para>
</section>
</section>
<section>
<title>The <literal>authentication</literal>Tag</title>
<para>This tag allows access to the current <interfacename>Authentication</interfacename>
object stored in the security context. It renders a property of the object directly in
the JSP. So, for example, if the <literal>principal</literal> property of the
<interfacename>Authentication</interfacename> is an instance of Spring Security's
<interfacename>UserDetails</interfacename> object, then using
<literal>&lt;sec:authentication property="principal.username" /></literal> will render
the name of the current user.</para>
<para>Of course, it isn't necessary to use JSP tags for this kind of thing and some people
prefer to keep as little logic as possible in the view. You can access the
<interfacename>Authentication</interfacename> object in your MVC controller (by calling
<code>SecurityContextHolder.getContext().getAuthentication()</code>) and add the data
directly to your model for rendering by the view.</para>
</section>
<section>
<title>The <literal>accesscontrollist</literal> Tag</title>
<para>This tag is only valid when used with Spring Security's ACL module. It checks a
comma-separated list of required permissions for a specified domain object. If the
current user has any of those permissions, then the tag body will be evaluated. If they
don't, it will be skipped. An example might
be<programlisting language="xml">&lt;sec:accesscontrollist hasPermission="1,2" domainObject="${someObject}">
This will be shown if the user has either of the permissions
represented by the values "1" or "2" on the given object.
&lt;/sec:accesscontrollist></programlisting></para>
<para>The permissions are passed to the <interfacename>PermissionFactory</interfacename>
defined in the application context, converting them to ACL
<interfacename>Permission</interfacename> instances, so they may be any format which is
supported by the factory - they don't have to be integers, they could be strings like
<literal>READ</literal> or <literal>WRITE</literal>. If no
<interfacename>PermissionFactory</interfacename> is found, an instance of
<classname>DefaultPermissionFactory</classname> will be used. The
<interfacename>AclService</interfacename>from the application context will be used to
load the <interfacename>Acl</interfacename> instance for the supplied object. The
<interfacename>Acl</interfacename> will be invoked with the required permissions to
check if any of them are granted.</para>
<para>This tag also supports the <literal>var</literal> attribute, in the same way as the
<literal>authorize</literal> tag.</para>
</section>
</chapter>

View File

@ -1,719 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="technical-overview"
xmlns:xlink="http://www.w3.org/1999/xlink">
<info>
<title>Technical Overview</title>
</info>
<section xml:id="runtime-environment">
<info>
<title>Runtime Environment</title>
</info>
<para>Spring Security 3.0 requires a Java 5.0 Runtime Environment or higher. As Spring
Security aims to operate in a self-contained manner, there is no need to place any
special configuration files into your Java Runtime Environment. In particular, there is
no need to configure a special Java Authentication and Authorization Service (JAAS)
policy file or place Spring Security into common classpath locations.</para>
<para>Similarly, if you are using an EJB Container or Servlet Container there is no need to
put any special configuration files anywhere, nor include Spring Security in a server
classloader. All the required files will be contained within your application.</para>
<para>This design offers maximum deployment time flexibility, as you can simply copy your
target artifact (be it a JAR, WAR or EAR) from one system to another and it will
immediately work.</para>
</section>
<section xml:id="core-components">
<info>
<title>Core Components</title>
</info>
<para>In Spring Security 3.0, the contents of the <filename>spring-security-core</filename>
jar were stripped down to the bare minimum. It no longer contains any code related to
web-application security, LDAP or namespace configuration. We'll take a look here at
some of the Java types that you'll find in the core module. They represent the building
blocks of the the framework, so if you ever need to go beyond a simple namespace
configuration then it's important that you understand what they are, even if you don't
actually need to interact with them directly.</para>
<section>
<title> SecurityContextHolder, SecurityContext and Authentication Objects </title>
<para>The most fundamental object is <classname>SecurityContextHolder</classname>. This
is where we store details of the present security context of the application, which
includes details of the principal currently using the application. By default the
<classname>SecurityContextHolder</classname> uses a <literal>ThreadLocal</literal>
to store these details, which means that the security context is always available to
methods in the same thread of execution, even if the security context is not
explicitly passed around as an argument to those methods. Using a
<literal>ThreadLocal</literal> in this way is quite safe if care is taken to clear
the thread after the present principal's request is processed. Of course, Spring
Security takes care of this for you automatically so there is no need to worry about
it.</para>
<para>Some applications aren't entirely suitable for using a
<literal>ThreadLocal</literal>, because of the specific way they work with threads.
For example, a Swing client might want all threads in a Java Virtual Machine to use
the same security context. <classname>SecurityContextHolder</classname> can be
configured with a strategy on startup to specify how you would like the context to
be stored. For a standalone application you would use the
<literal>SecurityContextHolder.MODE_GLOBAL</literal> strategy. Other applications
might want to have threads spawned by the secure thread also assume the same
security identity. This is achieved by using
<literal>SecurityContextHolder.MODE_INHERITABLETHREADLOCAL</literal>. You can change
the mode from the default <literal>SecurityContextHolder.MODE_THREADLOCAL</literal>
in two ways. The first is to set a system property, the second is to call a static
method on <classname>SecurityContextHolder</classname>. Most applications won't need
to change from the default, but if you do, take a look at the JavaDocs for
<classname>SecurityContextHolder</classname> to learn more.</para>
<section>
<title>Obtaining information about the current user</title>
<para>Inside the <classname>SecurityContextHolder</classname> we store details of
the principal currently interacting with the application. Spring Security uses
an <interfacename>Authentication</interfacename> object to represent this
information. You won't normally need to create an
<interfacename>Authentication</interfacename> object yourself, but it is fairly
common for users to query the <interfacename>Authentication</interfacename>
object. You can use the following code block - from anywhere in your application
- to obtain the name of the currently authenticated user, for example:</para>
<programlisting language="java">
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}</programlisting>
<para>The object returned by the call to <methodname>getContext()</methodname> is an
instance of the <interfacename>SecurityContext</interfacename> interface. This
is the object that is kept in thread-local storage. As we'll see below, most
authentication mechanisms withing Spring Security return an instance of
<interfacename>UserDetails</interfacename> as the principal. </para>
</section>
</section>
<section xml:id="tech-userdetailsservice">
<title>The UserDetailsService</title>
<para>Another item to note from the above code fragment is that you can obtain a
principal from the <interfacename>Authentication</interfacename> object. The
principal is just an <literal>Object</literal>. Most of the time this can be cast
into a <interfacename>UserDetails</interfacename> object.
<interfacename>UserDetails</interfacename> is a core interface in Spring
Security. It represents a principal, but in an extensible and application-specific
way. Think of <interfacename>UserDetails</interfacename> as the adapter between your
own user database and what Spring Security needs inside the
<classname>SecurityContextHolder</classname>. Being a representation of something
from your own user database, quite often you will cast the
<interfacename>UserDetails</interfacename> to the original object that your
application provided, so you can call business-specific methods (like
<literal>getEmail()</literal>, <literal>getEmployeeNumber()</literal> and so
on).</para>
<para>By now you're probably wondering, so when do I provide a
<interfacename>UserDetails</interfacename> object? How do I do that? I thought you
said this thing was declarative and I didn't need to write any Java code - what
gives? The short answer is that there is a special interface called
<interfacename>UserDetailsService</interfacename>. The only method on this interface
accepts a <literal>String</literal>-based username argument and returns a
<interfacename>UserDetails</interfacename>:
<programlisting language="java">
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
</programlisting>
This is the most common approach to loading information for a user within Spring
Security and you will see it used throughout the framework whenever information on a
user is required.</para>
<para> On successful authentication, <interfacename>UserDetails</interfacename> is used
to build the <interfacename>Authentication</interfacename> object that is stored in
the <classname>SecurityContextHolder</classname> (more on this <link
linkend="tech-intro-authentication">below</link>). The good news is that we
provide a number of <interfacename>UserDetailsService</interfacename>
implementations, including one that uses an in-memory map
(<classname>InMemoryDaoImpl</classname>) and another that uses JDBC
(<classname>JdbcDaoImpl</classname>). Most users tend to write their own, though,
with their implementations often simply sitting on top of an existing Data Access
Object (DAO) that represents their employees, customers, or other users of the
application. Remember the advantage that whatever your
<interfacename>UserDetailsService</interfacename> returns can always be obtained
from the <classname>SecurityContextHolder</classname> using the above code fragment.
</para>
<note>
<para>There is often some confusion about <interfacename>UserDetailsService</interfacename>.
It is purely a DAO for user data and performs no other function other than to supply that data
to other components within the framework. In particular, it <emphasis>does not</emphasis>
authenticate the user, which is done by the <interfacename>AuthenticationManager</interfacename>.
In many cases it makes more sense to
<link linkend="core-services-authentication-manager">implement <interfacename>AuthenticationProvider</interfacename></link>
directly if you require a custom authentication process.
</para>
</note>
</section>
<section xml:id="tech-granted-authority">
<title>GrantedAuthority</title>
<para>Besides the principal, another important method provided by
<interfacename>Authentication</interfacename> is
<literal>getAuthorities(</literal>). This method provides an array of
<interfacename>GrantedAuthority</interfacename> objects. A
<interfacename>GrantedAuthority</interfacename> is, not surprisingly, an authority
that is granted to the principal. Such authorities are usually <quote>roles</quote>,
such as <literal>ROLE_ADMINISTRATOR</literal> or
<literal>ROLE_HR_SUPERVISOR</literal>. These roles are later on configured for web
authorization, method authorization and domain object authorization. Other parts of
Spring Security are capable of interpreting these authorities, and expect them to be
present. <interfacename>GrantedAuthority</interfacename> objects are usually loaded
by the <interfacename>UserDetailsService</interfacename>.</para>
<para>Usually the <interfacename>GrantedAuthority</interfacename> objects are
application-wide permissions. They are not specific to a given domain object. Thus,
you wouldn't likely have a <interfacename>GrantedAuthority</interfacename> to
represent a permission to <literal>Employee</literal> object number 54, because if
there are thousands of such authorities you would quickly run out of memory (or, at
the very least, cause the application to take a long time to authenticate a user).
Of course, Spring Security is expressly designed to handle this common requirement,
but you'd instead use the project's domain object security capabilities for this
purpose.</para>
</section>
<section>
<title>Summary</title>
<para>Just to recap, the major building blocks of Spring Security that we've seen so far
are:</para>
<itemizedlist spacing="compact">
<listitem>
<para><classname>SecurityContextHolder</classname>, to provide access to the
<interfacename>SecurityContext</interfacename>.</para>
</listitem>
<listitem>
<para><interfacename>SecurityContext</interfacename>, to hold the
<interfacename>Authentication</interfacename> and possibly request-specific
security information.</para>
</listitem>
<listitem>
<para><interfacename>Authentication</interfacename>, to represent the principal
in a Spring Security-specific manner.</para>
</listitem>
<listitem>
<para><interfacename>GrantedAuthority</interfacename>, to reflect the
application-wide permissions granted to a principal.</para>
</listitem>
<listitem>
<para><interfacename>UserDetails</interfacename>, to provide the necessary
information to build an Authentication object from your application's DAOs
or other source of security data.</para>
</listitem>
<listitem>
<para><interfacename>UserDetailsService</interfacename>, to create a
<interfacename>UserDetails</interfacename> when passed in a
<literal>String</literal>-based username (or certificate ID or the
like).</para>
</listitem>
</itemizedlist>
<para>Now that you've gained an understanding of these repeatedly-used components, let's
take a closer look at the process of authentication.</para>
</section>
</section>
<section xml:id="tech-intro-authentication">
<info>
<title>Authentication</title>
</info>
<para>Spring Security can participate in many different authentication environments. While
we recommend people use Spring Security for authentication and not integrate with
existing Container Managed Authentication, it is nevertheless supported - as is
integrating with your own proprietary authentication system. </para>
<section>
<title>What is authentication in Spring Security?</title>
<para> Let's consider a standard authentication scenario that everyone is familiar with. <orderedlist>
<listitem>
<para>A user is prompted to log in with a username and password.</para>
</listitem>
<listitem>
<para>The system (successfully) verifies that the password is correct for the
username.</para>
</listitem>
<listitem>
<para>The context information for that user is obtained (their list of roles and
so on).</para>
</listitem>
<listitem>
<para>A security context is established for the user</para>
</listitem>
<listitem>
<para>The user proceeds, potentially to perform some operation which is
potentially protected by an access control mechanism which checks the
required permissions for the operation against the current security context
information. </para>
</listitem>
</orderedlist> The first three items constitute the authentication process so we'll
take a look at how these take place within Spring Security.<orderedlist>
<listitem>
<para>The username and password are obtained and combined into an instance of
<classname>UsernamePasswordAuthenticationToken</classname> (an instance of
the <interfacename>Authentication</interfacename> interface, which we saw
earlier).</para>
</listitem>
<listitem>
<para>The token is passed to an instance of
<interfacename>AuthenticationManager</interfacename> for validation.</para>
</listitem>
<listitem>
<para>The <interfacename>AuthenticationManager</interfacename> returns a fully
populated <interfacename>Authentication</interfacename> instance on
successful authentication.</para>
</listitem>
<listitem>
<para>The security context is established by calling
<code>SecurityContextHolder.getContext().setAuthentication(...)</code>,
passing in the returned authentication object.</para>
</listitem>
</orderedlist>From that point on, the user is considered to be authenticated. Let's
look at some code as an example.
<programlisting language="java">import org.springframework.security.authentication.*;
import org.springframework.security.core.*;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
public class AuthenticationExample {
private static AuthenticationManager am = new SampleAuthenticationManager();
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true) {
System.out.println("Please enter your username:");
String name = in.readLine();
System.out.println("Please enter your password:");
String password = in.readLine();
try {
Authentication request = new UsernamePasswordAuthenticationToken(name, password);
Authentication result = am.authenticate(request);
SecurityContextHolder.getContext().setAuthentication(result);
break;
} catch(AuthenticationException e) {
System.out.println("Authentication failed: " + e.getMessage());
}
}
System.out.println("Successfully authenticated. Security context contains: " +
SecurityContextHolder.getContext().getAuthentication());
}
}
class SampleAuthenticationManager implements AuthenticationManager {
static final List&lt;GrantedAuthority> AUTHORITIES = new ArrayList&lt;GrantedAuthority>();
static {
AUTHORITIES.add(new SimpleGrantedAuthority("ROLE_USER"));
}
public Authentication authenticate(Authentication auth) throws AuthenticationException {
if (auth.getName().equals(auth.getCredentials())) {
return new UsernamePasswordAuthenticationToken(auth.getName(),
auth.getCredentials(), AUTHORITIES);
}
throw new BadCredentialsException("Bad Credentials");
}
}</programlisting>Here
we have written a little program that asks the user to enter a username and password
and performs the above sequence. The
<interfacename>AuthenticationManager</interfacename> which we've implemented here
will authenticate any user whose username and password are the same. It assigns a
single role to every user. The output from the above will be something
like:<programlisting language="txt">
Please enter your username:
bob
Please enter your password:
password
Authentication failed: Bad Credentials
Please enter your username:
bob
Please enter your password:
bob
Successfully authenticated. Security context contains: \
org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d0230: \
Principal: bob; Password: [PROTECTED]; \
Authenticated: true; Details: null; \
Granted Authorities: ROLE_USER
</programlisting></para>
<para>Note that you don't normally need to write any code like this. The process will
normally occur internally, in a web authentication filter for example. We've just
included the code here to show that the question of what actually constitutes
authentication in Spring Security has quite a simple answer. A user is authenticated
when the <classname>SecurityContextHolder</classname> contains a fully populated
<interfacename>Authentication</interfacename> object.</para>
</section>
<section>
<title>Setting the SecurityContextHolder Contents Directly</title>
<para>In fact, Spring Security doesn't mind how you put the
<interfacename>Authentication</interfacename> object inside the
<classname>SecurityContextHolder</classname>. The only critical requirement is that
the <classname>SecurityContextHolder</classname> contains an
<interfacename>Authentication</interfacename> which represents a principal before
the <classname>AbstractSecurityInterceptor</classname> (which we'll see more about
later) needs to authorize a user operation.</para>
<para>You can (and many users do) write their own filters or MVC controllers to provide
interoperability with authentication systems that are not based on Spring Security.
For example, you might be using Container-Managed Authentication which makes the
current user available from a ThreadLocal or JNDI location. Or you might work for a
company that has a legacy proprietary authentication system, which is a corporate
"standard" over which you have little control. In situations like this it's quite
easy to get Spring Security to work, and still provide authorization capabilities.
All you need to do is write a filter (or equivalent) that reads the third-party user
information from a location, build a Spring Security-specific
<interfacename>Authentication</interfacename> object, and put it into the
<classname>SecurityContextHolder</classname>. In this case you also need to think
about things which are normally taken care of automatically by the built-in authentication
infrastructure. For example, you might need to pre-emptively create an HTTP session to
<link xlink:href="tech-intro-sec-context-persistence">cache the context between requests</link>,
before you write the response to the client<footnote><para>It isn't possible to create a session once the
response has been committed.</para></footnote>.
</para>
<para> If you're wondering how the <interfacename>AuthenticationManager</interfacename>
is implemented in a real world example, we'll look at that in the <link
linkend="core-services-authentication-manager">core services
chapter</link>.</para>
</section>
</section>
<section xml:id="tech-intro-web-authentication">
<title>Authentication in a Web Application</title>
<para> Now let's explore the situation where you are using Spring Security in a web
application (without <filename>web.xml</filename> security enabled). How is a user
authenticated and the security context established?</para>
<para>Consider a typical web application's authentication process:</para>
<orderedlist inheritnum="ignore" continuation="restarts">
<listitem>
<para>You visit the home page, and click on a link.</para>
</listitem>
<listitem>
<para>A request goes to the server, and the server decides that you've asked for a
protected resource.</para>
</listitem>
<listitem>
<para>As you're not presently authenticated, the server sends back a response
indicating that you must authenticate. The response will either be an HTTP
response code, or a redirect to a particular web page.</para>
</listitem>
<listitem>
<para>Depending on the authentication mechanism, your browser will either redirect
to the specific web page so that you can fill out the form, or the browser will
somehow retrieve your identity (via a BASIC authentication dialogue box, a
cookie, a X.509 certificate etc.).</para>
</listitem>
<listitem>
<para>The browser will send back a response to the server. This will either be an
HTTP POST containing the contents of the form that you filled out, or an HTTP
header containing your authentication details.</para>
</listitem>
<listitem>
<para>Next the server will decide whether or not the presented credentials are
valid. If they're valid, the next step will happen. If they're invalid, usually
your browser will be asked to try again (so you return to step two
above).</para>
</listitem>
<listitem>
<para>The original request that you made to cause the authentication process will be
retried. Hopefully you've authenticated with sufficient granted authorities to
access the protected resource. If you have sufficient access, the request will
be successful. Otherwise, you'll receive back an HTTP error code 403, which
means "forbidden".</para>
</listitem>
</orderedlist>
<para>Spring Security has distinct classes responsible for most of the steps described
above. The main participants (in the order that they are used) are the
<classname>ExceptionTranslationFilter</classname>, an
<interfacename>AuthenticationEntryPoint</interfacename> and an <quote>authentication
mechanism</quote>, which is responsible for calling the
<classname>AuthenticationManager</classname> which we saw in the previous
section.</para>
<section>
<title>ExceptionTranslationFilter</title>
<para><classname>ExceptionTranslationFilter</classname> is a Spring Security filter that
has responsibility for detecting any Spring Security exceptions that are thrown.
Such exceptions will generally be thrown by an
<classname>AbstractSecurityInterceptor</classname>, which is the main provider of
authorization services. We will discuss
<classname>AbstractSecurityInterceptor</classname> in the next section, but for now
we just need to know that it produces Java exceptions and knows nothing about HTTP
or how to go about authenticating a principal. Instead the
<classname>ExceptionTranslationFilter</classname> offers this service, with specific
responsibility for either returning error code 403 (if the principal has been
authenticated and therefore simply lacks sufficient access - as per step seven
above), or launching an <interfacename>AuthenticationEntryPoint</interfacename> (if
the principal has not been authenticated and therefore we need to go commence step
three).</para>
</section>
<section xml:id="tech-intro-auth-entry-point">
<title>AuthenticationEntryPoint</title>
<para>The <interfacename>AuthenticationEntryPoint</interfacename> is responsible for
step three in the above list. As you can imagine, each web application will have a
default authentication strategy (well, this can be configured like nearly everything
else in Spring Security, but let's keep it simple for now). Each major
authentication system will have its own
<interfacename>AuthenticationEntryPoint</interfacename> implementation, which
typically performs one of the actions described in step 3.</para>
</section>
<section>
<title>Authentication Mechanism</title>
<para>Once your browser submits your authentication credentials (either as an HTTP form
post or HTTP header) there needs to be something on the server that
<quote>collects</quote> these authentication details. By now we're at step six in
the above list. In Spring Security we have a special name for the function of
collecting authentication details from a user agent (usually a web browser),
referring to it as the <quote>authentication mechanism</quote>. Examples are
form-base login and Basic authentication. Once the authentication details have been
collected from the user agent, an <interfacename>Authentication</interfacename>
<quote>request</quote> object is built and then presented to the
<interfacename>AuthenticationManager</interfacename>.</para>
<para>After the authentication mechanism receives back the fully-populated
<interfacename>Authentication</interfacename> object, it will deem the request
valid, put the <interfacename>Authentication</interfacename> into the
<classname>SecurityContextHolder</classname>, and cause the original request to be
retried (step seven above). If, on the other hand, the
<classname>AuthenticationManager</classname> rejected the request, the
authentication mechanism will ask the user agent to retry (step two above).</para>
</section>
<section xml:id="tech-intro-sec-context-persistence">
<title>Storing the <interfacename>SecurityContext</interfacename> between
requests</title>
<para>Depending on the type of application, there may need to be a strategy in place to
store the security context between user operations. In a typical web application, a
user logs in once and is subsequently identified by their session Id. The server
caches the principal information for the duration session. In Spring Security, the
responsibility for storing the <interfacename>SecurityContext</interfacename>
between requests falls to the
<classname>SecurityContextPersistenceFilter</classname>, which by default stores the
context as an <literal>HttpSession</literal> attribute between HTTP requests. It
restores the context to the <classname>SecurityContextHolder</classname> for each
request and, crucially, clears the <classname>SecurityContextHolder</classname> when
the request completes. You shouldn't interact directly with the
<literal>HttpSession</literal> for security purposes. There is simply no
justification for doing so - always use the
<classname>SecurityContextHolder</classname> instead. </para>
<para> Many other types of application (for example, a stateless RESTful web service) do
not use HTTP sessions and will re-authenticate on every request. However, it is
still important that the <classname>SecurityContextPersistenceFilter</classname> is
included in the chain to make sure that the
<classname>SecurityContextHolder</classname> is cleared after each request.</para>
<note>
<para>In an application which receives concurrent requests in a single session, the
same <interfacename>SecurityContext</interfacename> instance will be shared
between threads. Even though a <classname>ThreadLocal</classname> is being used,
it is the same instance that is retrieved from the
<interfacename>HttpSession</interfacename> for each thread. This has
implications if you wish to temporarily change the context under which a thread
is running. If you just use <code>SecurityContextHolder.getContext()</code>, and
call <code>setAuthentication(anAuthentication)</code> on the returned context
object, then the <interfacename>Authentication</interfacename> object will
change in <emphasis>all</emphasis> concurrent threads which share the same
<interfacename>SecurityContext</interfacename> instance. You can customize the
behaviour of <classname>SecurityContextPersistenceFilter</classname> to create a
completely new <interfacename>SecurityContext</interfacename> for each request,
preventing changes in one thread from affecting another. Alternatively you can
create a new instance just at the point where you temporarily change the
context. The method <code>SecurityContextHolder.createEmptyContext()</code>
always returns a new context instance.</para>
</note>
</section>
</section>
<section xml:id="tech-intro-access-control">
<title>Access-Control (Authorization) in Spring Security</title>
<para> The main interface responsible for making access-control decisions in Spring Security
is the <interfacename>AccessDecisionManager</interfacename>. It has a
<methodname>decide</methodname> method which takes an
<interfacename>Authentication</interfacename> object representing the principal
requesting access, a <quote>secure object</quote> (see below) and a list of security
metadata attributes which apply for the object (such as a list of roles which are
required for access to be granted). </para>
<section>
<title>Security and AOP Advice</title>
<para>If you're familiar with AOP, you'd be aware there are different types of advice
available: before, after, throws and around. An around advice is very useful,
because an advisor can elect whether or not to proceed with a method invocation,
whether or not to modify the response, and whether or not to throw an exception.
Spring Security provides an around advice for method invocations as well as web
requests. We achieve an around advice for method invocations using Spring's standard
AOP support and we achieve an around advice for web requests using a standard
Filter.</para>
<para>For those not familiar with AOP, the key point to understand is that Spring
Security can help you protect method invocations as well as web requests. Most
people are interested in securing method invocations on their services layer. This
is because the services layer is where most business logic resides in
current-generation J2EE applications. If you just need to secure method invocations
in the services layer, Spring's standard AOP will be adequate. If you need to secure
domain objects directly, you will likely find that AspectJ is worth
considering.</para>
<para>You can elect to perform method authorization using AspectJ or Spring AOP, or you
can elect to perform web request authorization using filters. You can use zero, one,
two or three of these approaches together. The mainstream usage pattern is to
perform some web request authorization, coupled with some Spring AOP method
invocation authorization on the services layer.</para>
</section>
<section xml:id="secure-objects">
<title>Secure Objects and the <classname>AbstractSecurityInterceptor</classname></title>
<para>So what <emphasis>is</emphasis> a <quote>secure object</quote> anyway? Spring
Security uses the term to refer to any object that can have security (such as an
authorization decision) applied to it. The most common examples are method
invocations and web requests.</para>
<para>Each supported secure object type has its own interceptor class, which is a
subclass of <classname>AbstractSecurityInterceptor</classname>. Importantly, by the
time the <classname>AbstractSecurityInterceptor</classname> is called, the
<classname>SecurityContextHolder</classname> will contain a valid
<interfacename>Authentication</interfacename> if the principal has been
authenticated.</para>
<para><classname>AbstractSecurityInterceptor</classname> provides a consistent workflow
for handling secure object requests, typically: <orderedlist>
<listitem>
<para>Look up the <quote>configuration attributes</quote> associated with the
present request</para>
</listitem>
<listitem>
<para>Submitting the secure object, current
<interfacename>Authentication</interfacename> and configuration attributes
to the <interfacename>AccessDecisionManager</interfacename> for an
authorization decision</para>
</listitem>
<listitem>
<para>Optionally change the <interfacename>Authentication</interfacename> under
which the invocation takes place</para>
</listitem>
<listitem>
<para>Allow the secure object invocation to proceed (assuming access was
granted)</para>
</listitem>
<listitem>
<para>Call the <interfacename>AfterInvocationManager</interfacename> if
configured, once the invocation has returned. If the invocation raised an
exception, the <interfacename>AfterInvocationManager</interfacename>
will not be invoked.</para>
</listitem>
</orderedlist></para>
<section xml:id="tech-intro-config-attributes">
<title>What are Configuration Attributes?</title>
<para> A <quote>configuration attribute</quote> can be thought of as a String that
has special meaning to the classes used by
<classname>AbstractSecurityInterceptor</classname>. They are represented by the
interface <interfacename>ConfigAttribute</interfacename> within the framework.
They may be simple role names or have more complex meaning, depending on the how
sophisticated the <interfacename>AccessDecisionManager</interfacename>
implementation is. The <classname>AbstractSecurityInterceptor</classname> is
configured with a <interfacename>SecurityMetadataSource</interfacename> which it
uses to look up the attributes for a secure object. Usually this configuration
will be hidden from the user. Configuration attributes will be entered as
annotations on secured methods or as access attributes on secured URLs. For
example, when we saw something like <literal>&lt;intercept-url
pattern='/secure/**' access='ROLE_A,ROLE_B'/></literal> in the namespace
introduction, this is saying that the configuration attributes
<literal>ROLE_A</literal> and <literal>ROLE_B</literal> apply to web requests
matching the given pattern. In practice, with the default
<interfacename>AccessDecisionManager</interfacename> configuration, this means
that anyone who has a <interfacename>GrantedAuthority</interfacename> matching
either of these two attributes will be allowed access. Strictly speaking though,
they are just attributes and the interpretation is dependent on the
<interfacename>AccessDecisionManager</interfacename> implementation. The use of
the prefix <literal>ROLE_</literal> is a marker to indicate that these
attributes are roles and should be consumed by Spring Security's
<classname>RoleVoter</classname>. This is only relevant when a voter-based
<interfacename>AccessDecisionManager</interfacename> is in use. We'll see how
the <interfacename>AccessDecisionManager</interfacename> is implemented in the
<link linkend="authz-arch">authorization chapter</link>.</para>
</section>
<section>
<title>RunAsManager</title>
<para>Assuming <interfacename>AccessDecisionManager</interfacename> decides to allow
the request, the <classname>AbstractSecurityInterceptor</classname> will
normally just proceed with the request. Having said that, on rare occasions
users may want to replace the <interfacename>Authentication</interfacename>
inside the <interfacename>SecurityContext</interfacename> with a different
<interfacename>Authentication</interfacename>, which is handled by the
<interfacename>AccessDecisionManager</interfacename> calling a
<literal>RunAsManager</literal>. This might be useful in reasonably unusual
situations, such as if a services layer method needs to call a remote system and
present a different identity. Because Spring Security automatically propagates
security identity from one server to another (assuming you're using a
properly-configured RMI or HttpInvoker remoting protocol client), this may be
useful.</para>
</section>
<section>
<title>AfterInvocationManager</title>
<para>Following the secure object invocation proceeding and then returning - which may mean a
method invocation completing or a filter chain proceeding - the
<classname>AbstractSecurityInterceptor</classname> gets one final chance to
handle the invocation. At this stage the
<classname>AbstractSecurityInterceptor</classname> is interested in possibly
modifying the return object. We might want this to happen because an
authorization decision couldn't be made <quote>on the way in</quote> to a secure
object invocation. Being highly pluggable,
<classname>AbstractSecurityInterceptor</classname> will pass control to an
<literal>AfterInvocationManager</literal> to actually modify the object if
needed. This class can even entirely replace the object, or throw an exception,
or not change it in any way as it chooses. The after-invocation checks will only
be executed if the invocation is successful. If an exception occurs, the additional
checks will be skipped.
</para>
<para><classname>AbstractSecurityInterceptor</classname> and its related objects are
shown in <xref linkend="abstract-security-interceptor"/>. <figure
xml:id="abstract-security-interceptor">
<title>Security interceptors and the <quote>secure object</quote> model</title>
<mediaobject>
<imageobject>
<imagedata align="center" fileref="images/security-interception.png"
format="PNG" scale="75"/>
</imageobject>
</mediaobject>
</figure></para>
</section>
<section>
<title>Extending the Secure Object Model</title>
<para>Only developers contemplating an entirely new way of intercepting and
authorizing requests would need to use secure objects directly. For example, it
would be possible to build a new secure object to secure calls to a messaging
system. Anything that requires security and also provides a way of intercepting
a call (like the AOP around advice semantics) is capable of being made into a
secure object. Having said that, most Spring applications will simply use the
three currently supported secure object types (AOP Alliance
<classname>MethodInvocation</classname>, AspectJ
<classname>JoinPoint</classname> and web request
<classname>FilterInvocation</classname>) with complete transparency.</para>
</section>
</section>
</section>
<section xml:id="localization">
<title>Localization</title>
<para>Spring Security supports localization of exception messages that end users are likely
to see. If your application is designed for English-speaking users, you don't need to do
anything as by default all Security Security messages are in English. If you need to
support other locales, everything you need to know is contained in this section.</para>
<para>All exception messages can be localized, including messages related to authentication
failures and access being denied (authorization failures). Exceptions and logging
messages that are focused on developers or system deployers (including incorrect
attributes, interface contract violations, using incorrect constructors, startup time
validation, debug-level logging) are not localized and instead are hard-coded in English
within Spring Security's code.</para>
<para>Shipping in the <literal>spring-security-core-xx.jar</literal> you will find an
<literal>org.springframework.security</literal> package that in turn contains a
<literal>messages.properties</literal> file, as well as localized versions for some
common languages. This should be referred to by your
<literal>ApplicationContext</literal>, as Spring Security classes implement Spring's
<literal>MessageSourceAware</literal> interface and expect the message resolver to be
dependency injected at application context startup time. Usually all you need to do is
register a bean inside your application context to refer to the messages. An example is
shown below:</para>
<para>
<programlisting language="xml"><![CDATA[
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="classpath:org/springframework/security/messages"/>
</bean>
]]></programlisting>
</para>
<para>The <literal>messages.properties</literal> is named in accordance with standard
resource bundles and represents the default language supported by Spring Security
messages. This default file is in English. </para>
<para>If you wish to customize the <literal>messages.properties</literal> file, or support
other languages, you should copy the file, rename it accordingly, and register it inside
the above bean definition. There are not a large number of message keys inside this
file, so localization should not be considered a major initiative. If you do perform
localization of this file, please consider sharing your work with the community by
logging a JIRA task and attaching your appropriately-named localized version of
<literal>messages.properties</literal>.</para>
<para>Spring Security relies on Spring's localization support in order to actually lookup
the appropriate message. In order for this to work, you have to make sure that the
locale from the incoming request is stored in Spring's
<classname>org.springframework.context.i18n.LocaleContextHolder</classname>. Spring
MVC's <classname>DispatcherServlet</classname> does this for your application
automatically, but since Spring Security's filters are invoked before this, the
<classname>LocaleContextHolder</classname> needs to be set up to contain the correct
<literal>Locale</literal> before the filters are called. You can either do this in a
filter yourself (which must come before the Spring Security filters in
<filename>web.xml</filename>) or you can use Spring's
<classname>RequestContextFilter</classname>. Please refer to the Spring Framework
documentation for further details on using localization with Spring. </para>
<para>The <quote>contacts</quote> sample application is set up to use localized messages.
</para>
</section>
</chapter>

View File

@ -1,100 +0,0 @@
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="x509">
<info>
<title>X.509 Authentication</title>
</info>
<section xml:id="x509-overview">
<info>
<title>Overview</title>
</info>
<para>The most common use of X.509 certificate authentication is in verifying the identity
of a server when using SSL, most commonly when using HTTPS from a browser. The browser
will automatically check that the certificate presented by a server has been issued (ie
digitally signed) by one of a list of trusted certificate authorities which it
maintains.</para>
<para>You can also use SSL with <quote>mutual authentication</quote>; the server will then
request a valid certificate from the client as part of the SSL handshake. The server
will authenticate the client by checking that its certificate is signed by an acceptable
authority. If a valid certificate has been provided, it can be obtained through the
servlet API in an application. Spring Security X.509 module extracts the certificate
using a filter. It maps the certificate to an application user and loads that user's set
of granted authorities for use with the standard Spring Security infrastructure.</para>
<para>You should be familiar with using certificates and setting up client authentication
for your servlet container before attempting to use it with Spring Security. Most of the
work is in creating and installing suitable certificates and keys. For example, if
you're using Tomcat then read the instructions here <uri
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html"
>http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html</uri>. It's important that you
get this working before trying it out with Spring Security</para>
</section>
<section>
<info>
<title>Adding X.509 Authentication to Your Web Application</title>
</info>
<para> Enabling X.509 client authentication is very straightforward. Just add the
<literal>&lt;x509/&gt;</literal> element to your http security namespace configuration.
<programlisting language="xml"><![CDATA[
<http>
...
<x509 subject-principal-regex="CN=(.*?)," user-service-ref="userService"/>;
...
</http>
]]></programlisting>
The element has two optional attributes: <itemizedlist>
<listitem>
<para><literal>subject-principal-regex</literal>. The regular expression used to
extract a username from the certificate's subject name. The default value is
shown above. This is the username which will be passed to the
<interfacename>UserDetailsService</interfacename> to load the authorities for
the user.</para>
</listitem>
<listitem>
<para><literal>user-service-ref</literal>. This is the bean Id of the
<interfacename>UserDetailsService</interfacename> to be used with X.509. It
isn't needed if there is only one defined in your application context.</para>
</listitem>
</itemizedlist> The <literal>subject-principal-regex</literal> should contain a single
group. For example the default expression "CN=(.*?)," matches the common name field. So
if the subject name in the certificate is "CN=Jimi Hendrix, OU=...", this will give a
user name of "Jimi Hendrix". The matches are case insensitive. So "emailAddress=(.?),"
will match "EMAILADDRESS=jimi@hendrix.org,CN=..." giving a user name "jimi@hendrix.org".
If the client presents a certificate and a valid username is successfully extracted,
then there should be a valid <classname>Authentication</classname> object in the
security context. If no certificate is found, or no corresponding user could be found
then the security context will remain empty. This means that you can easily use X.509
authentication with other options such as a form-based login. </para>
</section>
<section xml:id="x509-ssl-config">
<info>
<title>Setting up SSL in Tomcat</title>
</info>
<para>There are some pre-generated certificates in the
<filename>samples/certificate</filename> directory in the Spring Security project. You
can use these to enable SSL for testing if you don't want to generate your own. The file
<filename>server.jks</filename> contains the server certificate, private key and the
issuing certificate authority certificate. There are also some client certificate files
for the users from the sample applications. You can install these in your browser to
enable SSL client authentication. </para>
<para> To run tomcat with SSL support, drop the <filename>server.jks</filename> file into
the tomcat <filename>conf</filename> directory and add the following connector to the
<filename>server.xml</filename> file
<programlisting language="xml"><![CDATA[
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" scheme="https" secure="true"
clientAuth="true" sslProtocol="TLS"
keystoreFile="${catalina.home}/conf/server.jks"
keystoreType="JKS" keystorePass="password"
truststoreFile="${catalina.home}/conf/server.jks"
truststoreType="JKS" truststorePass="password"
/>
]]></programlisting>
<parameter>clientAuth</parameter> can also be set to <parameter>want</parameter> if you
still want SSL connections to succeed even if the client doesn't provide a certificate.
Clients which don't present a certificate won't be able to access any objects secured by
Spring Security unless you use a non-X.509 authentication mechanism, such as form
authentication. </para>
</section>
</chapter>

View File

@ -1,35 +0,0 @@
/*
code highlight CSS resemblign the Eclipse IDE default color schema
@author Costin Leau
*/
.hl-keyword {
color: #7F0055;
font-weight: bold;
}
.hl-comment {
color: #3F5F5F;
font-style: italic;
}
.hl-multiline-comment {
color: #3F5FBF;
font-style: italic;
}
.hl-tag {
color: #3F7F7F;
}
.hl-attribute {
color: #7F007F;
}
.hl-value {
color: #2A00FF;
}
.hl-string {
color: #2A00FF;
}

View File

@ -1,69 +0,0 @@
@IMPORT url("highlight.css");
html {
padding: 0pt;
margin: 0pt;
}
body {
margin-left: 10%;
margin-right: 10%;
font-family: Arial, Sans-serif;
}
div {
margin: 0pt;
}
p {
text-align: justify;
}
hr {
border: 1px solid gray;
background: gray;
}
h1,h2,h3,h4 {
color: #234623;
font-family: Arial, Sans-serif;
}
pre {
line-height: 1.0;
color: black;
}
pre.programlisting {
font-size: 10pt;
padding: 7pt 3pt;
border: 1pt solid black;
background: #eeeeee;
clear: both;
}
div.table {
margin: 1em;
padding: 0.5em;
text-align: center;
}
div.table table {
display: table;
width: 100%;
}
div.table td {
padding-left: 7px;
padding-right: 7px;
}
.sidebar {
float: right;
margin: 10px 0 10px 30px;
padding: 10px 20px 20px 20px;
width: 33%;
border: 1px solid black;
background-color: #F4F4F4;
font-size: 14px;
}

View File

@ -1,145 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xslthl="http://xslthl.sf.net"
exclude-result-prefixes="xslthl"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/html/chunk.xsl"/>
<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/html/highlight.xsl"/>
<xsl:param name="chunk.section.depth">'5'</xsl:param>
<xsl:param name="use.id.as.filename">'1'</xsl:param>
<!-- Only use scaling in FO -->
<xsl:param name="ignore.image.scaling">1</xsl:param>
<!-- Use code syntax highlighting -->
<xsl:param name="highlight.source">1</xsl:param>
<!-- Extensions -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<!-- Activate Graphics -->
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">images/</xsl:param>
<xsl:param name="admon.graphics.extension">.png</xsl:param>
<xsl:param name="callout.graphics" select="1" />
<xsl:param name="callout.defaultcolumn">120</xsl:param>
<xsl:param name="callout.graphics.path">images/callouts/</xsl:param>
<xsl:param name="callout.graphics.extension">.png</xsl:param>
<xsl:param name="table.borders.with.css" select="1"/>
<xsl:param name="html.stylesheet">css/manual.css</xsl:param>
<xsl:param name="html.stylesheet.type">text/css</xsl:param>
<xsl:param name="generate.toc">book toc,title</xsl:param>
<xsl:param name="admonition.title.properties">text-align: left</xsl:param>
<!-- Leave image paths as relative when navigating XInclude -->
<xsl:param name="keep.relative.image.uris" select="1"/>
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel" select="1"/>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.autolabel.max.depth" select="2"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<xsl:param name="table.footnote.number.format" select="'1'"/>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!-- Remove "Chapter" from the Chapter titles... -->
<xsl:param name="local.l10n.xml" select="document('')"/>
<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
<l:l10n language="en">
<l:context name="title-numbered">
<l:template name="chapter" text="%n.&#160;%t"/>
<l:template name="section" text="%n&#160;%t"/>
</l:context>
</l:l10n>
</l:i18n>
<xsl:template match='xslthl:keyword' mode="xslthl">
<span class="hl-keyword"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:comment' mode="xslthl">
<span class="hl-comment"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:oneline-comment' mode="xslthl">
<span class="hl-comment"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:multiline-comment' mode="xslthl">
<span class="hl-multiline-comment"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:tag' mode="xslthl">
<span class="hl-tag"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:attribute' mode="xslthl">
<span class="hl-attribute"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:value' mode="xslthl">
<span class="hl-value"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:string' mode="xslthl">
<span class="hl-string"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<!-- Google Analytics -->
<xsl:template name="user.head.content">
<xsl:comment>Begin Google Analytics code</xsl:comment>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-2728886-3");
pageTracker._setDomainName("none");
pageTracker._setAllowLinker(true);
pageTracker._trackPageview();
</script>
<xsl:comment>End Google Analytics code</xsl:comment>
</xsl:template>
<!-- Loopfuse -->
<xsl:template name="user.footer.content">
<xsl:comment>Begin LoopFuse code</xsl:comment>
<script src="http://loopfuse.net/webrecorder/js/listen.js" type="text/javascript">
</script>
<script type="text/javascript">
_lf_cid = "LF_48be82fa";
_lf_remora();
</script>
<xsl:comment>End LoopFuse code</xsl:comment>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,142 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xslthl="http://xslthl.sf.net"
exclude-result-prefixes="xslthl"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/html/docbook.xsl"/>
<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/html/highlight.xsl"/>
<!-- Only use scaling in FO -->
<xsl:param name="ignore.image.scaling">1</xsl:param>
<!-- Use code syntax highlighting -->
<xsl:param name="highlight.source">1</xsl:param>
<!-- Extensions -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<!-- Activate Graphics -->
<xsl:param name="admon.graphics" select="1"/>
<xsl:param name="admon.graphics.path">images/</xsl:param>
<xsl:param name="admon.graphics.extension">.png</xsl:param>
<xsl:param name="callout.graphics" select="1" />
<xsl:param name="callout.defaultcolumn">120</xsl:param>
<xsl:param name="callout.graphics.path">images/callouts/</xsl:param>
<xsl:param name="callout.graphics.extension">.png</xsl:param>
<xsl:param name="table.borders.with.css" select="1"/>
<xsl:param name="html.stylesheet">css/manual.css</xsl:param>
<xsl:param name="html.stylesheet.type">text/css</xsl:param>
<xsl:param name="generate.toc">book toc,title</xsl:param>
<xsl:param name="admonition.title.properties">text-align: left</xsl:param>
<!-- Leave image paths as relative when navigating XInclude -->
<xsl:param name="keep.relative.image.uris" select="1"/>
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel" select="1"/>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.autolabel.max.depth" select="2"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<xsl:param name="table.footnote.number.format" select="'1'"/>
<!-- Show only Sections up to level 2 in the TOCs -->
<xsl:param name="toc.section.depth">2</xsl:param>
<!-- Remove "Chapter" from the Chapter titles... -->
<xsl:param name="local.l10n.xml" select="document('')"/>
<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
<l:l10n language="en">
<l:context name="title-numbered">
<l:template name="chapter" text="%n.&#160;%t"/>
<l:template name="section" text="%n&#160;%t"/>
</l:context>
</l:l10n>
</l:i18n>
<xsl:template match='xslthl:keyword' mode="xslthl">
<span class="hl-keyword"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:comment' mode="xslthl">
<span class="hl-comment"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:oneline-comment' mode="xslthl">
<span class="hl-comment"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:multiline-comment' mode="xslthl">
<span class="hl-multiline-comment"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:tag' mode="xslthl">
<span class="hl-tag"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:attribute' mode="xslthl">
<span class="hl-attribute"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:value' mode="xslthl">
<span class="hl-value"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<xsl:template match='xslthl:string' mode="xslthl">
<span class="hl-string"><xsl:apply-templates mode="xslthl"/></span>
</xsl:template>
<!-- Google Analytics -->
<xsl:template name="user.head.content">
<xsl:comment>Begin Google Analytics code</xsl:comment>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-2728886-3");
pageTracker._setDomainName("none");
pageTracker._setAllowLinker(true);
pageTracker._trackPageview();
</script>
<xsl:comment>End Google Analytics code</xsl:comment>
</xsl:template>
<!-- Loopfuse -->
<xsl:template name="user.footer.content">
<xsl:comment>Begin LoopFuse code</xsl:comment>
<script src="http://loopfuse.net/webrecorder/js/listen.js" type="text/javascript">
</script>
<script type="text/javascript">
_lf_cid = "LF_48be82fa";
_lf_remora();
</script>
<xsl:comment>End LoopFuse code</xsl:comment>
</xsl:template>
</xsl:stylesheet>

View File

@ -1,503 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you 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.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xslthl="http://xslthl.sf.net"
xmlns:d="http://docbook.org/ns/docbook"
exclude-result-prefixes="d xslthl"
version='1.0'>
<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/fo/docbook.xsl"/>
<xsl:import href="http://docbook.sourceforge.net/release/xsl-ns/current/fo/highlight.xsl"/>
<!-- xsl:param name="draft.watermark.image" select="'images/draft.png'"/ -->
<xsl:param name="paper.type" select="'A4'"/>
<xsl:param name="page.margin.top" select="'1cm'"/>
<xsl:param name="region.before.extent" select="'1cm'"/>
<xsl:param name="body.margin.top" select="'1.5cm'"/>
<xsl:param name="body.margin.bottom" select="'1.5cm'"/>
<xsl:param name="region.after.extent" select="'1cm'"/>
<xsl:param name="page.margin.bottom" select="'1cm'"/>
<xsl:param name="title.margin.left" select="'0cm'"/>
<xsl:param name="page.margin.inner" select="'2cm'"/>
<xsl:param name="page.margin.outer" select="'2cm'"/>
<!--###################################################
Header
################################################### -->
<!-- More space in the center header for long text -->
<xsl:attribute-set name="header.content.properties">
<xsl:attribute name="font-family">
<xsl:value-of select="$body.font.family"/>
</xsl:attribute>
<xsl:attribute name="margin-left">-5em</xsl:attribute>
<xsl:attribute name="margin-right">-5em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Table of Contents
################################################### -->
<xsl:param name="generate.toc">
book toc,title
</xsl:param>
<!--###################################################
Custom Header
################################################### -->
<xsl:template name="header.content">
<xsl:param name="pageclass" select="''"/>
<xsl:param name="sequence" select="''"/>
<xsl:param name="position" select="''"/>
<xsl:param name="gentext-key" select="''"/>
<xsl:variable name="Version">
<xsl:choose>
<xsl:when test="//d:productname">
<xsl:value-of select="//d:productname"/><xsl:text> </xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>please define productname in your docbook file!</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:choose>
<xsl:when test="$sequence='blank'">
<xsl:choose>
<xsl:when test="$position='center'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$pageclass='titlepage'">
</xsl:when>
<xsl:when test="$position='center'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!--###################################################
Custom Footer
################################################### -->
<xsl:template name="footer.content">
<xsl:param name="pageclass" select="''"/>
<xsl:param name="sequence" select="''"/>
<xsl:param name="position" select="''"/>
<xsl:param name="gentext-key" select="''"/>
<xsl:variable name="Version">
<xsl:choose>
<xsl:when test="//d:releaseinfo">
<xsl:value-of select="//d:releaseinfo"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="Title">
<xsl:value-of select="//title"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$sequence='blank'">
<xsl:choose>
<xsl:when test="$double.sided != 0 and $position = 'left'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$double.sided = 0 and $position = 'center'">
</xsl:when>
<xsl:otherwise>
<fo:page-number/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$pageclass='titlepage'">
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='left'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='right'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$double.sided = 0 and $position='right'">
<fo:page-number/>
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'odd' and $position='left'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$double.sided != 0 and $sequence = 'even' and $position='right'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$double.sided = 0 and $position='left'">
<xsl:value-of select="$Version"/>
</xsl:when>
<xsl:when test="$position='center'">
<xsl:value-of select="$Title"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="processing-instruction('hard-pagebreak')">
<fo:block break-before='page'/>
</xsl:template>
<!--###################################################
Extensions
################################################### -->
<!-- These extensions are required for table printing and other stuff -->
<xsl:param name="use.extensions">1</xsl:param>
<xsl:param name="tablecolumns.extension">0</xsl:param>
<xsl:param name="callout.extensions">1</xsl:param>
<xsl:param name="fop1.extensions">1</xsl:param>
<!--###################################################
Paper & Page Size
################################################### -->
<!-- Paper type, no headers on blank pages, no double sided printing -->
<xsl:param name="double.sided">0</xsl:param>
<xsl:param name="headers.on.blank.pages">0</xsl:param>
<xsl:param name="footers.on.blank.pages">0</xsl:param>
<!--###################################################
Fonts & Styles
################################################### -->
<xsl:param name="hyphenate">false</xsl:param>
<!-- Default Font size -->
<xsl:param name="body.font.master">11</xsl:param>
<xsl:param name="body.font.small">8</xsl:param>
<!-- Line height in body text -->
<xsl:param name="line-height">1.4</xsl:param>
<!-- Chapter title size -->
<xsl:attribute-set name="chapter.titlepage.recto.style">
<xsl:attribute name="text-align">left</xsl:attribute>
<xsl:attribute name="font-weight">bold</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.8"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
</xsl:attribute-set>
<!-- Why is the font-size for chapters hardcoded in the XSL FO templates?
Let's remove it, so this sucker can use our attribute-set only... -->
<xsl:template match="d:title" mode="chapter.titlepage.recto.auto.mode">
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format"
xsl:use-attribute-sets="chapter.titlepage.recto.style">
<xsl:call-template name="component.title">
<xsl:with-param name="node" select="ancestor-or-self::chapter[1]"/>
</xsl:call-template>
</fo:block>
</xsl:template>
<!-- Sections 1, 2 and 3 titles have a small bump factor and padding -->
<xsl:attribute-set name="section.title.level1.properties">
<xsl:attribute name="space-before.optimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.8em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.8em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.5"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level2.properties">
<xsl:attribute name="space-before.optimum">0.6em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.6em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.6em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.25"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="section.title.level3.properties">
<xsl:attribute name="space-before.optimum">0.4em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.4em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.4em</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master * 1.0"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<!-- Use code syntax highlighting -->
<xsl:param name="highlight.source" select="1"/>
<xsl:param name="highlight.default.language" select="xml" />
<xsl:template match='xslthl:keyword'>
<fo:inline font-weight="bold" color="#7F0055"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:comment'>
<fo:inline font-style="italic" color="#3F5F5F"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:oneline-comment'>
<fo:inline font-style="italic" color="#3F5F5F"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:multiline-comment'>
<fo:inline font-style="italic" color="#3F5FBF"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:tag'>
<fo:inline color="#3F7F7F"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:attribute'>
<fo:inline color="#7F007F"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:value'>
<fo:inline color="#2A00FF"><xsl:apply-templates/></fo:inline>
</xsl:template>
<xsl:template match='xslthl:string'>
<fo:inline color="#2A00FF"><xsl:apply-templates/></fo:inline>
</xsl:template>
<!--###################################################
Tables
################################################### -->
<!-- Some padding inside tables -->
<xsl:attribute-set name="table.cell.padding">
<xsl:attribute name="padding-left">4pt</xsl:attribute>
<xsl:attribute name="padding-right">4pt</xsl:attribute>
<xsl:attribute name="padding-top">4pt</xsl:attribute>
<xsl:attribute name="padding-bottom">4pt</xsl:attribute>
</xsl:attribute-set>
<!-- Only hairlines as frame and cell borders in tables -->
<xsl:param name="table.frame.border.thickness">0.1pt</xsl:param>
<xsl:param name="table.cell.border.thickness">0.1pt</xsl:param>
<!--###################################################
Labels
################################################### -->
<!-- Label Chapters and Sections (numbering) -->
<xsl:param name="chapter.autolabel" select="1"/>
<xsl:param name="section.autolabel" select="1"/>
<xsl:param name="section.autolabel.max.depth" select="1"/>
<xsl:param name="section.label.includes.component.label" select="1"/>
<xsl:param name="table.footnote.number.format" select="'1'"/>
<!--###################################################
Programlistings
################################################### -->
<!-- Verbatim text formatting (programlistings) -->
<xsl:attribute-set name="monospace.verbatim.properties">
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.small * 1.0"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="verbatim.properties">
<xsl:attribute name="space-before.minimum">1em</xsl:attribute>
<xsl:attribute name="space-before.optimum">1em</xsl:attribute>
<xsl:attribute name="space-before.maximum">1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
<xsl:attribute name="border-color">#444444</xsl:attribute>
<xsl:attribute name="border-style">solid</xsl:attribute>
<xsl:attribute name="border-width">0.1pt</xsl:attribute>
<xsl:attribute name="padding-top">0.5em</xsl:attribute>
<xsl:attribute name="padding-left">0.5em</xsl:attribute>
<xsl:attribute name="padding-right">0.5em</xsl:attribute>
<xsl:attribute name="padding-bottom">0.5em</xsl:attribute>
<xsl:attribute name="margin-left">0.5em</xsl:attribute>
<xsl:attribute name="margin-right">0.5em</xsl:attribute>
</xsl:attribute-set>
<!-- Shade (background) programlistings -->
<xsl:param name="shade.verbatim">1</xsl:param>
<xsl:attribute-set name="shade.verbatim.style">
<xsl:attribute name="background-color">#F0F0F0</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="list.block.spacing">
<xsl:attribute name="space-before.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<xsl:attribute-set name="example.properties">
<xsl:attribute name="space-before.minimum">0.5em</xsl:attribute>
<xsl:attribute name="space-before.optimum">0.5em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.5em</xsl:attribute>
<xsl:attribute name="space-after.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-after.maximum">0.1em</xsl:attribute>
<xsl:attribute name="keep-together.within-column">always</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Title information for Figures, Examples etc.
################################################### -->
<xsl:attribute-set name="formal.title.properties" use-attribute-sets="normal.para.spacing">
<xsl:attribute name="font-weight">normal</xsl:attribute>
<xsl:attribute name="font-style">italic</xsl:attribute>
<xsl:attribute name="font-size">
<xsl:value-of select="$body.font.master"/>
<xsl:text>pt</xsl:text>
</xsl:attribute>
<xsl:attribute name="hyphenate">false</xsl:attribute>
<xsl:attribute name="space-before.minimum">0.1em</xsl:attribute>
<xsl:attribute name="space-before.optimum">0.1em</xsl:attribute>
<xsl:attribute name="space-before.maximum">0.1em</xsl:attribute>
</xsl:attribute-set>
<!--###################################################
Callouts
################################################### -->
<!-- don't use images for callouts
<xsl:param name="callout.graphics">0</xsl:param>
<xsl:param name="callout.unicode">1</xsl:param>
-->
<!-- Place callout marks at this column in annotated areas
<xsl:param name="callout.defaultcolumn">90</xsl:param>
-->
<!--###################################################
Misc
################################################### -->
<!-- Placement of titles -->
<xsl:param name="formal.title.placement">
figure after
example after
equation before
table before
procedure before
</xsl:param>
<!-- Format Variable Lists as Blocks (prevents horizontal overflow) -->
<xsl:param name="variablelist.as.blocks">1</xsl:param>
<xsl:param name="body.start.indent">0pt</xsl:param>
<!-- Show only Sections up to level 3 in the TOCs -->
<xsl:param name="toc.section.depth">3</xsl:param>
<!-- Remove "Chapter" from the Chapter titles... -->
<xsl:param name="local.l10n.xml" select="document('')"/>
<l:i18n xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0">
<l:l10n language="en">
<l:context name="title-numbered">
<l:template name="chapter" text="%n.&#160;%t"/>
<l:template name="section" text="%n&#160;%t"/>
</l:context>
<l:context name="title">
<l:template name="example" text="Example&#160;%n&#160;%t"/>
</l:context>
</l:l10n>
</l:i18n>
<!--###################################################
colored and hyphenated links
################################################### -->
<!--
<xsl:template match="ulink">
<fo:basic-link external-destination="{@url}"
xsl:use-attribute-sets="xref.properties"
text-decoration="underline"
color="blue">
<xsl:choose>
<xsl:when test="count(child::node())=0">
<xsl:value-of select="@url"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</fo:basic-link>
</xsl:template>
<xsl:template match="link">
<fo:basic-link internal-destination="{@linkend}"
xsl:use-attribute-sets="xref.properties"
text-decoration="underline"
color="blue">
<xsl:choose>
<xsl:when test="count(child::node())=0">
<xsl:value-of select="@linkend"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</fo:basic-link>
</xsl:template>
-->
</xsl:stylesheet>