SEC-2349: Convert Reference to Asciidoctor
@ -3,7 +3,6 @@ import groovy.text.SimpleTemplateEngine
|
|||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "http://repo.springsource.org/plugins-release" }
|
maven { url "http://repo.springsource.org/plugins-release" }
|
||||||
maven { url "http://dl.bintray.com/content/aalmiray/asciidoctor" }
|
|
||||||
maven { url "http://jcenter.bintray.com"}
|
maven { url "http://jcenter.bintray.com"}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -11,7 +10,6 @@ buildscript {
|
|||||||
classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.7")
|
classpath("org.springframework.build.gradle:docbook-reference-plugin:0.2.7")
|
||||||
classpath("org.springframework.build.gradle:bundlor-plugin:0.1.2")
|
classpath("org.springframework.build.gradle:bundlor-plugin:0.1.2")
|
||||||
classpath("org.gradle.api.plugins:gradle-tomcat-plugin:0.9.8")
|
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')
|
classpath('me.champeau.gradle:gradle-javadoc-hotfix-plugin:0.1')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,10 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile gradleApi()
|
||||||
|
}
|
||||||
|
|
||||||
// GAE
|
// GAE
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'com.google.appengine:appengine-tools-sdk:1.4.2'
|
compile 'com.google.appengine:appengine-tools-sdk:1.4.2'
|
||||||
@ -27,6 +31,21 @@ dependencies {
|
|||||||
'net.sourceforge.saxon:saxon:9.1.0.8'
|
'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) {
|
task ide(type: Copy) {
|
||||||
from configurations.runtime
|
from configurations.runtime
|
||||||
into 'ide'
|
into 'ide'
|
||||||
|
45
buildSrc/src/main/groovy/org/asciidoctor/gradle/AsciidoctorBackend.groovy
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.asciidoctor.gradle
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported backends.
|
||||||
|
*
|
||||||
|
* @author Benjamin Muschko
|
||||||
|
*/
|
||||||
|
enum AsciidoctorBackend {
|
||||||
|
HTML5('html5'), DOCBOOK('docbook'), PDF('pdf')
|
||||||
|
|
||||||
|
private final static Map<String, AsciidoctorBackend> ALL_BACKENDS
|
||||||
|
private final String id
|
||||||
|
|
||||||
|
static {
|
||||||
|
ALL_BACKENDS = values().collectEntries{ [it.id, it] }.asImmutable()
|
||||||
|
}
|
||||||
|
|
||||||
|
private AsciidoctorBackend(String id) {
|
||||||
|
this.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
String getId() {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isSupported(String name) {
|
||||||
|
ALL_BACKENDS.containsKey(name)
|
||||||
|
}
|
||||||
|
}
|
32
buildSrc/src/main/groovy/org/asciidoctor/gradle/AsciidoctorPlugin.groovy
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.asciidoctor.gradle
|
||||||
|
|
||||||
|
import groovy.lang.Closure;
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Noam Tenne
|
||||||
|
* @author Andres Almiray
|
||||||
|
*/
|
||||||
|
class AsciidoctorPlugin implements Plugin<Project> {
|
||||||
|
void apply(Project project) {
|
||||||
|
project.task('asciidoctor', type: AsciidoctorTask, group: 'Documentation')
|
||||||
|
}
|
||||||
|
}
|
201
buildSrc/src/main/groovy/org/asciidoctor/gradle/AsciidoctorTask.groovy
Executable file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2013 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.asciidoctor.gradle
|
||||||
|
|
||||||
|
import org.asciidoctor.gradle.*
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils
|
||||||
|
import org.asciidoctor.Asciidoctor
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.InvalidUserDataException
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
|
||||||
|
import javax.xml.transform.*
|
||||||
|
import javax.xml.transform.stream.*
|
||||||
|
import javax.xml.transform.sax.*
|
||||||
|
import org.apache.fop.apps.FopFactory
|
||||||
|
import org.apache.fop.apps.Fop
|
||||||
|
import org.apache.fop.apps.MimeConstants
|
||||||
|
|
||||||
|
|
||||||
|
class AsciidoctorTask extends DefaultTask {
|
||||||
|
@InputFile File sourceDocument
|
||||||
|
@Input Map options = [:]
|
||||||
|
|
||||||
|
@Optional @OutputDirectory File outputDir
|
||||||
|
@Optional @Input List<String> backends
|
||||||
|
|
||||||
|
AsciidoctorTask() {
|
||||||
|
sourceDocument = project.file("src/asciidoctor/index.adoc")
|
||||||
|
outputDir = project.file("${project.buildDir}/asciidoctor")
|
||||||
|
backends = [AsciidoctorBackend.HTML5.id]
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
void render() {
|
||||||
|
|
||||||
|
Asciidoctor asciidoctor = Asciidoctor.Factory.create()
|
||||||
|
|
||||||
|
for(backend in backends) {
|
||||||
|
boolean isPdf = backend == AsciidoctorBackend.PDF.id
|
||||||
|
String asciidoctorBackend = isPdf ? AsciidoctorBackend.DOCBOOK.id : backend
|
||||||
|
|
||||||
|
File distDir = new File("${outputDir}/dist/$backend")
|
||||||
|
File workingDir = new File("${outputDir}/work/$backend")
|
||||||
|
|
||||||
|
[workingDir,distDir]*.mkdirs()
|
||||||
|
|
||||||
|
try {
|
||||||
|
asciidoctor.renderFile(sourceDocument, mergedOptions(options, isPdf ? workingDir : distDir, asciidoctorBackend))
|
||||||
|
|
||||||
|
if(isPdf) {
|
||||||
|
generatePdf(workingDir,distDir)
|
||||||
|
} else {
|
||||||
|
project.copy {
|
||||||
|
from "${sourceDocument.parent}/images"
|
||||||
|
into "${distDir}/images/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new GradleException('Error running Asciidoctor on single source '+asciidoctorBackend, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generatePdf(File workingDir, File distDir) {
|
||||||
|
String docbookXmlUrl = 'http://maven-us.nuxeo.org/nexus/content/repositories/public/docbook/docbook-xml/4.5/docbook-xml-4.5.jar'
|
||||||
|
String docbookXslUrl = 'http://downloads.sourceforge.net/project/docbook/docbook-xsl-ns/1.78.1/docbook-xsl-ns-1.78.1.zip'
|
||||||
|
|
||||||
|
File docbookXmlFile = downloadFile(docbookXmlUrl)
|
||||||
|
File docbookXslFile = downloadFile(docbookXslUrl)
|
||||||
|
|
||||||
|
project.copy {
|
||||||
|
from "src/asciidoctor/images"
|
||||||
|
into "${workingDir}/images/"
|
||||||
|
}
|
||||||
|
|
||||||
|
project.copy {
|
||||||
|
from project.zipTree(docbookXmlFile)
|
||||||
|
into "$workingDir/docbook"
|
||||||
|
}
|
||||||
|
|
||||||
|
project.copy {
|
||||||
|
from(project.zipTree(docbookXslFile)) {
|
||||||
|
eachFile { details ->
|
||||||
|
details.path = details.path.substring(details.relativePath.segments[0].length())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
into "$workingDir/docbook/"
|
||||||
|
}
|
||||||
|
|
||||||
|
unzipDockbookXsl(workingDir)
|
||||||
|
|
||||||
|
def outputUri = workingDir.toURI().toASCIIString()
|
||||||
|
|
||||||
|
Vector params = new Vector()
|
||||||
|
params.add("highlight.xslthl.config")
|
||||||
|
params.add(outputUri + "docbook-xsl/xslthl-config.xml")
|
||||||
|
params.add("admon.graphics.path")
|
||||||
|
params.add(outputUri + "docbook/images/")
|
||||||
|
params.add("callout.graphics.path")
|
||||||
|
params.add(outputUri + "docbook/images/callouts/")
|
||||||
|
params.add("img.src.path")
|
||||||
|
params.add(outputUri)
|
||||||
|
params.add("fop-output-format")
|
||||||
|
params.add("application/pdf")
|
||||||
|
params.add("fop-version")
|
||||||
|
params.add("1.1")
|
||||||
|
|
||||||
|
File outputFile = new File("${distDir}/", sourceDocument.name.replaceAll("\\..*", ".pdf"))
|
||||||
|
File docbookFile = new File("$workingDir/",sourceDocument.name.replaceAll("\\..*", ".xml"))
|
||||||
|
File xsltFile = new File("${workingDir}/docbook-xsl/fo-pdf.xsl")
|
||||||
|
|
||||||
|
InputHandler handler = new InputHandler(docbookFile, xsltFile, params)
|
||||||
|
|
||||||
|
FopFactory fopFactory = FopFactory.newInstance(); // Reuse the FopFactory if possible!
|
||||||
|
fopFactory.setUserConfig(new File("${workingDir}/docbook-xsl/fop-config.xml"))
|
||||||
|
// do the following for each new rendering run
|
||||||
|
def foUserAgent = fopFactory.newFOUserAgent();
|
||||||
|
|
||||||
|
handler.createCatalogResolver(foUserAgent)
|
||||||
|
|
||||||
|
def out = new java.io.BufferedOutputStream(
|
||||||
|
new java.io.FileOutputStream(outputFile));
|
||||||
|
|
||||||
|
foUserAgent.setOutputFile(outputFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
handler.renderTo(foUserAgent, MimeConstants.MIME_PDF, out)
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unzipDockbookXsl(def installDir) {
|
||||||
|
def docbookXslResourceName = 'docbook-xsl.zip'
|
||||||
|
def docbookXslInputStream = this.class.classLoader.getResourceAsStream(docbookXslResourceName)
|
||||||
|
if (docbookXslInputStream == null) {
|
||||||
|
throw new GradleException("could not find ${docbookXslResourceName} on the classpath");
|
||||||
|
}
|
||||||
|
// the file is a jar:file - write it to disk first
|
||||||
|
File docbookXslOutputFile = new File("${installDir}/downloads/${docbookXslResourceName}")
|
||||||
|
docbookXslOutputFile.parentFile.mkdirs()
|
||||||
|
IOUtils.copy(docbookXslInputStream, new FileOutputStream(docbookXslOutputFile))
|
||||||
|
project.copy {
|
||||||
|
from project.zipTree(docbookXslOutputFile)
|
||||||
|
into "${installDir}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File downloadFile(String url) {
|
||||||
|
def home = System.getProperty("user.home")
|
||||||
|
File destinationFile = new File("${home}/.fopdf/downloads", url.split("/")[-1])
|
||||||
|
destinationFile.parentFile.mkdirs()
|
||||||
|
|
||||||
|
if(!destinationFile.exists()) {
|
||||||
|
logger.info("Downloading " + url + " to "+ destinationFile + "...")
|
||||||
|
destinationFile.bytes = new URL(url).bytes
|
||||||
|
}
|
||||||
|
destinationFile
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, Object> mergedOptions(Map options, File outputDir, String backend) {
|
||||||
|
Map<String, Object> mergedOptions = [:]
|
||||||
|
mergedOptions.putAll(options)
|
||||||
|
mergedOptions.in_place = false
|
||||||
|
mergedOptions.safe = 0i
|
||||||
|
mergedOptions.to_dir = outputDir.absolutePath
|
||||||
|
Map attributes = mergedOptions.get('attributes', [:])
|
||||||
|
attributes.backend = backend
|
||||||
|
|
||||||
|
// Issue #14 force GString -> String as jruby will fail
|
||||||
|
// to find an exact match when invoking Asciidoctor
|
||||||
|
for (entry in mergedOptions) {
|
||||||
|
if (entry.value instanceof CharSequence) {
|
||||||
|
mergedOptions[entry.key] = entry.value.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (entry in attributes) {
|
||||||
|
if (entry.value instanceof CharSequence) {
|
||||||
|
attributes[entry.key] = entry.value.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mergedOptions
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
BIN
buildSrc/src/main/resources/docbook-xsl.zip
Normal file
@ -2,7 +2,7 @@
|
|||||||
apply plugin: 'base'
|
apply plugin: 'base'
|
||||||
|
|
||||||
task docs {
|
task docs {
|
||||||
dependsOn 'manual:reference', 'faq:referenceHtmlSingle', 'apidocs', 'guides:asciidoctor'
|
dependsOn 'manual:asciidoctor', 'faq:referenceHtmlSingle', 'apidocs', 'guides:asciidoctor'
|
||||||
}
|
}
|
||||||
|
|
||||||
project('faq') {
|
project('faq') {
|
||||||
@ -25,26 +25,37 @@ project('faq') {
|
|||||||
|
|
||||||
project('manual') {
|
project('manual') {
|
||||||
apply plugin: 'base'
|
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 {
|
ext.spec = copySpec {
|
||||||
into ('reference') {
|
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) {
|
task apidocs(type: Javadoc) {
|
||||||
destinationDir = new File(buildDir, 'apidocs')
|
destinationDir = new File(buildDir, 'apidocs')
|
||||||
title = "Spring Security $version API"
|
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) {
|
task docsZip(type: Zip) {
|
||||||
dependsOn docs
|
dependsOn docs
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
|
|
||||||
import org.asciidoctor.gradle.*
|
import org.asciidoctor.gradle.*
|
||||||
|
|
||||||
file("src/asciidoc").eachFileMatch(~/.*\.asc/) { file->
|
file("src/asciidoc").eachFileMatch(~/.*\.asc/) { file->
|
||||||
task "asciidoctor-${file.name}"(type: AsciidoctorTask) {
|
task "asciidoctor-${file.name}"(type: AsciidoctorTask) {
|
||||||
outputDir = project.file("$buildDir/docs")
|
sourceDocument = file
|
||||||
sourceDocumentName = file
|
|
||||||
options = [
|
options = [
|
||||||
|
eruby: 'erubis',
|
||||||
eruby: 'erubis',
|
eruby: 'erubis',
|
||||||
attributes: [
|
attributes: [
|
||||||
copycss : '',
|
copycss : '',
|
||||||
icons : 'font',
|
icons : 'font',
|
||||||
'source-highlighter': 'prettify',
|
'source-highlighter': 'prettify',
|
||||||
sectanchors : '',
|
sectanchors : '',
|
||||||
'toc-placement' : 'preamble',
|
toc2: '',
|
||||||
toc: '',
|
|
||||||
idprefix: '',
|
idprefix: '',
|
||||||
idseparator: '-',
|
idseparator: '-',
|
||||||
|
doctype: 'book',
|
||||||
'spring-security-version' : project.version,
|
'spring-security-version' : project.version,
|
||||||
revnumber : project.version
|
revnumber : project.version
|
||||||
]
|
]
|
||||||
@ -30,6 +31,6 @@ task asciidoctor {
|
|||||||
|
|
||||||
ext.spec = copySpec {
|
ext.spec = copySpec {
|
||||||
into ('guides') {
|
into ('guides') {
|
||||||
from("$buildDir/docs/")
|
from("$buildDir/asciidoctor/dist/html5/")
|
||||||
}
|
}
|
||||||
}
|
}
|
12
docs/manual/src/asciidoctor/Guardfile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
require 'asciidoctor'
|
||||||
|
require 'erb'
|
||||||
|
|
||||||
|
guard 'shell' do
|
||||||
|
watch(/^.*\.adoc$/) {|m|
|
||||||
|
Asciidoctor.render_file(m[0], :to_dir => "build/", :safe => Asciidoctor::SafeMode::UNSAFE, :attributes=> {'idprefix' => '', 'idseparator' => '-', 'copycss' => '', 'icons' => 'font', 'source-highlighter' => 'prettify', 'sectanchors' => '', 'doctype' => 'book','toc2' => '', 'spring-security-version' => '3.2.0.CI-SNAPSHOT', 'revnumber' => '3.2.0.CI-SNAPSHOT' })
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
guard 'livereload' do
|
||||||
|
watch(%r{build/.+\.(css|js|html)$})
|
||||||
|
end
|
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 |
7579
docs/manual/src/asciidoctor/index.adoc
Normal file
@ -1,138 +0,0 @@
|
|||||||
<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="anonymous"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<info>
|
|
||||||
<title>Anonymous Authentication</title>
|
|
||||||
</info>
|
|
||||||
<section xml:id="anonymous-overview">
|
|
||||||
<info>
|
|
||||||
<title>Overview</title>
|
|
||||||
</info>
|
|
||||||
<para>It's generally considered good security practice to adopt a
|
|
||||||
<quote>deny-by-default</quote> where you explicitly specify what is allowed and disallow
|
|
||||||
everything else. Defining what is accessible to unauthenticated users is a similar
|
|
||||||
situation, particularly for web applications. Many sites require that users must be
|
|
||||||
authenticated for anything other than a few URLs (for example the home and login pages).
|
|
||||||
In this case it is easiest to define access configuration attributes for these specific
|
|
||||||
URLs rather than have for every secured resource. Put differently, sometimes it is nice
|
|
||||||
to say <literal>ROLE_SOMETHING</literal> is required by default and only allow certain
|
|
||||||
exceptions to this rule, such as for login, logout and home pages of an application. You
|
|
||||||
could also omit these pages from the filter chain entirely, thus bypassing the access
|
|
||||||
control checks, but this may be undesirable for other reasons, particularly if the pages
|
|
||||||
behave differently for authenticated users.</para>
|
|
||||||
<para>This is what we mean by anonymous authentication. Note that there is no real
|
|
||||||
conceptual difference between a user who is <quote>anonymously authenticated</quote> and
|
|
||||||
an unauthenticated user. Spring Security's anonymous authentication just gives you a
|
|
||||||
more convenient way to configure your access-control attributes. Calls to servlet API
|
|
||||||
calls such as <methodname>getCallerPrincipal</methodname>, for example, will still
|
|
||||||
return null even though there is actually an anonymous authentication object in the
|
|
||||||
<classname>SecurityContextHolder</classname>.</para>
|
|
||||||
<para>There are other situations where anonymous authentication is useful, such as when an
|
|
||||||
auditing interceptor queries the <classname>SecurityContextHolder</classname> to
|
|
||||||
identify which principal was responsible for a given operation. Classes can be authored
|
|
||||||
more robustly if they know the <classname>SecurityContextHolder</classname> always
|
|
||||||
contains an <interfacename>Authentication</interfacename> object, and never
|
|
||||||
<literal>null</literal>.</para>
|
|
||||||
</section>
|
|
||||||
<section xml:id="anonymous-config">
|
|
||||||
<info>
|
|
||||||
<title>Configuration</title>
|
|
||||||
</info>
|
|
||||||
<para>Anonymous authentication support is provided automatically when using the HTTP
|
|
||||||
configuration Spring Security 3.0 and can be customized (or disabled) using the
|
|
||||||
<literal><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>
|
|