SEC-2349: Convert Reference to Asciidoctor
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
implementation-class=org.asciidoctor.gradle.AsciidoctorPlugin
|
|
@ -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
|
||||
|
|
|
@ -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/")
|
||||
}
|
||||
}
|
|
@ -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
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -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><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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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<ConfigAttribute> 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<ConfigAttribute> 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>></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>
|
|
@ -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>
|
|
@ -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&ticket=ST-0-ER94xMJmn6pha35CQRoZ&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&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>
|
|
@ -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><welcome-file></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><intercept-url></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><welcome-file></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>
|
|
@ -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>
|
|
@ -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/</</;
|
||||
$title =~ s/>/>/;
|
||||
$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");
|
|
@ -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>
|
|
@ -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><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><filter-security-metadata-source></literal> namespace element. This is
|
||||
similar to the <literal><http></literal> element from the namespace chapter
|
||||
but the <literal><intercept-url></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<ConfigAttribute></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><http></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><form-login></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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"><csrf></link> element. Refer to the
|
||||
<link linkend="nsa-csrf"><csrf></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 <form:form> 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/">cujoJS’s</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 <filter-mapping> 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"><csrf></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>
|
|
@ -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>
|
|
@ -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><http></literal> element
|
||||
to <literal>true</literal>. Spring Security will then expect the
|
||||
<literal>access</literal> attributes of the <literal><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 built–in 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 built–in 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<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>
|
|
@ -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 <form-login> 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>
|
|
@ -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"><headers></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"><headers></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"><cache-control></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 <headers> 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"><content-type-options></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 <headers> 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"><hsts></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 <headers> 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 <headers> 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"><header></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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 Security’s
|
||||
<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 user’s 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>Let’s 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>
|
|
@ -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><ldap-server></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><ldap-server></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=<user-login-name>,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=<user's-DN>)</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=<user-login-name>)</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>
|
|
@ -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&runQuery=true&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"><http></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"><debug></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"><expression-handler></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"><metadata-source-ref></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"><attribute-exchange></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>
|
|
@ -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
|
|
@ -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>
|
|
@ -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><security-role></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>
|
|
@ -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><remember-me></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>
|
|
@ -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<ConfigAttribute> 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>
|
|
@ -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
|
||||
container’s <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>
|
|
@ -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><intercept-methods></literal> or <literal><protect-point></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)
|
||||
&& execution(public * *(..)) && !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>
|
|
@ -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-<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">
|
||||
<%@ taglib prefix='security' uri='http://www.springframework.org/security/tags' %>
|
||||
</programlisting></para>
|
||||
</section>
|
||||
</section>-->
|
||||
</chapter>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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<FilterInvocation></interfacename> defined in the application
|
||||
context (you should have web expressions enabled in your <literal><http></literal>
|
||||
namespace configuration to make sure this service is available). So, for example, you
|
||||
might
|
||||
have<programlisting language="xml"><sec:authorize access="hasRole('supervisor')">
|
||||
|
||||
This content will only be visible to users who have
|
||||
the "supervisor" authority in their list of <tt>GrantedAuthority</tt>s.
|
||||
|
||||
</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"><sec:authorize url="/admin">
|
||||
|
||||
This content will only be visible to users who are authorized to send requests to the "/admin" URL.
|
||||
|
||||
</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><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><span
|
||||
class="securityHiddenUI">...</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><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"><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.
|
||||
|
||||
</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>
|
|
@ -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<GrantedAuthority> AUTHORITIES = new ArrayList<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><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>
|
|
@ -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><x509/></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>
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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. %t"/>
|
||||
<l:template name="section" text="%n %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>
|
|
@ -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. %t"/>
|
||||
<l:template name="section" text="%n %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>
|
|
@ -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. %t"/>
|
||||
<l:template name="section" text="%n %t"/>
|
||||
</l:context>
|
||||
<l:context name="title">
|
||||
<l:template name="example" text="Example %n %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>
|