SEC-576: Tidied up code, added preauth sample demo app.

This commit is contained in:
Luke Taylor 2008-01-23 20:02:11 +00:00
parent a9ff309b02
commit 837ecd85ec
27 changed files with 984 additions and 578 deletions

View File

@ -16,74 +16,77 @@ import org.springframework.util.Assert;
* Processes a pre-authenticated authentication request. The request will * Processes a pre-authenticated authentication request. The request will
* typically originate from a {@link org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter} * typically originate from a {@link org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter}
* subclass. * subclass.
* </p> *
*
* <p> * <p>
* This authentication provider will not perform any checks on authentication * This authentication provider will not perform any checks on authentication
* requests, as they should already be pre- authenticated. However, the * requests, as they should already be pre- authenticated. However, the
* PreAuthenticatedUserDetailsService implementation may still throw for exampe * PreAuthenticatedUserDetailsService implementation may still throw for exampe
* a UsernameNotFoundException. * a UsernameNotFoundException.
* </p> *
* @author Ruud Senden
* @since 2.0
*/ */
public class PreAuthenticatedAuthenticationProvider implements AuthenticationProvider, InitializingBean, Ordered { public class PreAuthenticatedAuthenticationProvider implements AuthenticationProvider, InitializingBean, Ordered {
private static final Log LOG = LogFactory.getLog(PreAuthenticatedAuthenticationProvider.class); private static final Log logger = LogFactory.getLog(PreAuthenticatedAuthenticationProvider.class);
private PreAuthenticatedUserDetailsService preAuthenticatedUserDetailsService = null; private PreAuthenticatedUserDetailsService preAuthenticatedUserDetailsService = null;
private int order = -1; // default: same as non-ordered private int order = -1; // default: same as non-ordered
/** /**
* Check whether all required properties have been set. * Check whether all required properties have been set.
*/ */
public void afterPropertiesSet() { public void afterPropertiesSet() {
Assert.notNull(preAuthenticatedUserDetailsService, "A PreAuthenticatedUserDetailsService must be set"); Assert.notNull(preAuthenticatedUserDetailsService, "A PreAuthenticatedUserDetailsService must be set");
} }
/** /**
* Authenticate the given PreAuthenticatedAuthenticationToken. * Authenticate the given PreAuthenticatedAuthenticationToken.
*/ */
public Authentication authenticate(Authentication authentication) throws AuthenticationException { public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) { if (!supports(authentication.getClass())) {
return null; return null;
} }
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("PreAuthenticated authentication request: " + authentication); logger.debug("PreAuthenticated authentication request: " + authentication);
} }
UserDetails ud = preAuthenticatedUserDetailsService.getUserDetails((PreAuthenticatedAuthenticationToken) authentication); UserDetails ud = preAuthenticatedUserDetailsService.getUserDetails((PreAuthenticatedAuthenticationToken) authentication);
if (ud == null) {
return null; if (ud == null) {
} return null;
PreAuthenticatedAuthenticationToken result = }
PreAuthenticatedAuthenticationToken result =
new PreAuthenticatedAuthenticationToken(ud, authentication.getCredentials(), ud.getAuthorities()); new PreAuthenticatedAuthenticationToken(ud, authentication.getCredentials(), ud.getAuthorities());
result.setDetails(authentication.getDetails()); result.setDetails(authentication.getDetails());
return result; return result;
} }
/** /**
* Indicate that this provider only supports PreAuthenticatedAuthenticationToken (sub)classes. * Indicate that this provider only supports PreAuthenticatedAuthenticationToken (sub)classes.
*/ */
public boolean supports(Class authentication) { public boolean supports(Class authentication) {
return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication); return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);
} }
/** /**
* Set the PreAuthenticatedUserDetailsServices to be used. * Set the PreAuthenticatedUserDetailsServices to be used.
* *
* @param aPreAuthenticatedUserDetailsService * @param aPreAuthenticatedUserDetailsService
*/ */
public void setPreAuthenticatedUserDetailsService(PreAuthenticatedUserDetailsService aPreAuthenticatedUserDetailsService) { public void setPreAuthenticatedUserDetailsService(PreAuthenticatedUserDetailsService aPreAuthenticatedUserDetailsService) {
this.preAuthenticatedUserDetailsService = aPreAuthenticatedUserDetailsService; this.preAuthenticatedUserDetailsService = aPreAuthenticatedUserDetailsService;
} }
public int getOrder() { public int getOrder() {
return order; return order;
} }
public void setOrder(int i) { public void setOrder(int i) {
order = i; order = i;
} }
} }

View File

@ -7,64 +7,65 @@ import org.springframework.security.GrantedAuthority;
/** /**
* {@link org.springframework.security.Authentication} implementation for pre-authenticated * {@link org.springframework.security.Authentication} implementation for pre-authenticated
* authentication. * authentication.
*
* @author Ruud Senden
* @since 2.0
*/ */
public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationToken { public class PreAuthenticatedAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 1L; private Object principal;
private Object principal; private Object credentials;
private Object credentials; /**
* Constructor used for an authentication request. The {@link
* org.springframework.security.Authentication#isAuthenticated()} will return
* <code>false</code>.
*
* @TODO Should we have only a single credentials parameter here? For
* example for X509 the certificate is used as credentials, while
* currently a J2EE username is specified as a principal but could as
* well be set as credentials.
*
* @param aPrincipal
* The pre-authenticated principal
* @param aCredentials
* The pre-authenticated credentials
*/
public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials) {
super(null);
this.principal = aPrincipal;
this.credentials = aCredentials;
}
/** /**
* Constructor used for an authentication request. The {@link * Constructor used for an authentication response. The {@link
* org.springframework.security.Authentication#isAuthenticated()} will return * org.springframework.security.Authentication#isAuthenticated()} will return
* <code>false</code>. * <code>true</code>.
* *
* @TODO Should we have only a single credentials parameter here? For * @param aPrincipal
* example for X509 the certificate is used as credentials, while * The authenticated principal
* currently a J2EE username is specified as a principal but could as * @param anAuthorities
* well be set as credentials. * The granted authorities
* */
* @param aPrincipal public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials, GrantedAuthority[] anAuthorities) {
* The pre-authenticated principal super(anAuthorities);
* @param aCredentials this.principal = aPrincipal;
* The pre-authenticated credentials this.credentials = aCredentials;
*/ setAuthenticated(true);
public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials) { }
super(null);
this.principal = aPrincipal;
this.credentials = aCredentials;
}
/** /**
* Constructor used for an authentication response. The {@link * Get the credentials
* org.springframework.security.Authentication#isAuthenticated()} will return */
* <code>true</code>. public Object getCredentials() {
* return this.credentials;
* @param aPrincipal }
* The authenticated principal
* @param anAuthorities
* The granted authorities
*/
public PreAuthenticatedAuthenticationToken(Object aPrincipal, Object aCredentials, GrantedAuthority[] anAuthorities) {
super(anAuthorities);
this.principal = aPrincipal;
this.credentials = aCredentials;
setAuthenticated(true);
}
/** /**
* Get the credentials * Get the principal
*/ */
public Object getCredentials() { public Object getPrincipal() {
return this.credentials; return this.principal;
} }
/**
* Get the principal
*/
public Object getPrincipal() {
return this.principal;
}
} }

View File

@ -6,10 +6,13 @@ import org.springframework.security.GrantedAuthority;
/** /**
* Interface that allows for retrieval of a list of pre-authenticated Granted * Interface that allows for retrieval of a list of pre-authenticated Granted
* Authorities. * Authorities.
*
* @author Ruud Senden
* @since 2.0
*/ */
public interface PreAuthenticatedGrantedAuthoritiesRetriever { public interface PreAuthenticatedGrantedAuthoritiesRetriever {
/** /**
* @return GrantedAuthority[] List of pre-authenticated GrantedAuthorities * @return GrantedAuthority[] List of pre-authenticated GrantedAuthorities
*/ */
public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities(); GrantedAuthority[] getPreAuthenticatedGrantedAuthorities();
} }

View File

@ -8,11 +8,14 @@ import org.springframework.security.GrantedAuthority;
* actually being used by the PreAuthenticatedAuthenticationProvider or one of * actually being used by the PreAuthenticatedAuthenticationProvider or one of
* its related classes, but may be useful for classes that also implement * its related classes, but may be useful for classes that also implement
* PreAuthenticatedGrantedAuthoritiesRetriever. * PreAuthenticatedGrantedAuthoritiesRetriever.
*
* @author Ruud Senden
* @since 2.0
*/ */
public interface PreAuthenticatedGrantedAuthoritiesSetter { public interface PreAuthenticatedGrantedAuthoritiesSetter {
/** /**
* @param aPreAuthenticatedGrantedAuthorities * @param aPreAuthenticatedGrantedAuthorities
* The pre-authenticated GrantedAuthority[] to set * The pre-authenticated GrantedAuthority[] to set
*/ */
public void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aPreAuthenticatedGrantedAuthorities); void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aPreAuthenticatedGrantedAuthorities);
} }

View File

@ -17,14 +17,15 @@ import org.springframework.util.Assert;
* PreAuthenticatedAuthenticationProvider anyway), and the Granted Authorities * PreAuthenticatedAuthenticationProvider anyway), and the Granted Authorities
* are retrieved from the details object as returned by * are retrieved from the details object as returned by
* PreAuthenticatedAuthenticationToken.getDetails(). * PreAuthenticatedAuthenticationToken.getDetails().
* </p>
* *
* <p> * <p>
* The details object as returned by * The details object as returned by
* PreAuthenticatedAuthenticationToken.getDetails() must implement the * PreAuthenticatedAuthenticationToken.getDetails() must implement the
* PreAuthenticatedGrantedAuthoritiesRetriever interface for this implementation * PreAuthenticatedGrantedAuthoritiesRetriever interface for this implementation
* to work. * to work.
* </p> *
* @author Ruud Senden
* @since 2.0
*/ */
public class PreAuthenticatedGrantedAuthoritiesUserDetailsService implements PreAuthenticatedUserDetailsService { public class PreAuthenticatedGrantedAuthoritiesUserDetailsService implements PreAuthenticatedUserDetailsService {
/** /**

View File

@ -7,11 +7,14 @@ import org.springframework.security.userdetails.UserDetails;
/** /**
* Interface that allows for retrieving a UserDetails object based on a * Interface that allows for retrieving a UserDetails object based on a
* PreAuthenticatedAuthenticationToken object. * PreAuthenticatedAuthenticationToken object.
*
* @author Ruud Senden
* @since 2.0
*/ */
public interface PreAuthenticatedUserDetailsService { public interface PreAuthenticatedUserDetailsService {
/** /**
* *
* @param aPreAuthenticatedAuthenticationToken * @param aPreAuthenticatedAuthenticationToken
* The pre-authenticated authentication token * The pre-authenticated authentication token
* @return UserDetails for the given authentication token. * @return UserDetails for the given authentication token.
@ -19,6 +22,6 @@ public interface PreAuthenticatedUserDetailsService {
* if no user details can be found for the given authentication * if no user details can be found for the given authentication
* token * token
*/ */
public UserDetails getUserDetails(PreAuthenticatedAuthenticationToken aPreAuthenticatedAuthenticationToken) UserDetails getUserDetails(PreAuthenticatedAuthenticationToken aPreAuthenticatedAuthenticationToken)
throws UsernameNotFoundException; throws UsernameNotFoundException;
} }

View File

@ -9,15 +9,18 @@ import org.springframework.util.Assert;
/** /**
* This implementation for PreAuthenticatedUserDetailsService wraps a regular * This implementation for PreAuthenticatedUserDetailsService wraps a regular
* Acegi UserDetailsService implementation, to retrieve a UserDetails object * Spring Security UserDetailsService implementation, to retrieve a UserDetails object
* based on the user name contained in a PreAuthenticatedAuthenticationToken. * based on the user name contained in a PreAuthenticatedAuthenticationToken.
*
* @author Ruud Senden
* @since 2.0
*/ */
public class UserDetailsByNameServiceWrapper implements PreAuthenticatedUserDetailsService, InitializingBean { public class UserDetailsByNameServiceWrapper implements PreAuthenticatedUserDetailsService, InitializingBean {
private UserDetailsService userDetailsService = null; private UserDetailsService userDetailsService = null;
/** /**
* Check whether all required properties have been set. * Check whether all required properties have been set.
* *
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/ */
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
@ -35,7 +38,7 @@ public class UserDetailsByNameServiceWrapper implements PreAuthenticatedUserDeta
/** /**
* Set the wrapped UserDetailsService implementation * Set the wrapped UserDetailsService implementation
* *
* @param aUserDetailsService * @param aUserDetailsService
* The wrapped UserDetailsService to set * The wrapped UserDetailsService to set
*/ */

View File

@ -4,13 +4,16 @@ package org.springframework.security.rolemapping;
* Interface to be implemented by classes that can retrieve a list of mappable * Interface to be implemented by classes that can retrieve a list of mappable
* roles (for example the list of all available J2EE roles in a web or EJB * roles (for example the list of all available J2EE roles in a web or EJB
* application). * application).
*
* @author Ruud Senden
* @since 2.0
*/ */
public interface MappableRolesRetriever { public interface MappableRolesRetriever {
/** /**
* Implementations of this method should return a list of all mappable * Implementations of this method should return a list of all mappable
* roles. * roles.
* *
* @return String[] containg list of all mappable roles * @return list of all mappable roles
*/ */
public String[] getMappableRoles(); String[] getMappableRoles();
} }

View File

@ -5,18 +5,20 @@ import org.springframework.security.GrantedAuthority;
/** /**
* Interface to be implemented by classes that can map a list of roles to a list * Interface to be implemented by classes that can map a list of roles to a list
* of Acegi GrantedAuthorities. * of Acegi GrantedAuthorities.
*
* @author Ruud Senden
* @since 2.0
*/ */
public interface Roles2GrantedAuthoritiesMapper { public interface Roles2GrantedAuthoritiesMapper {
/** /**
* Implementations of this method should map the given list of roles to a * Implementations of this method should map the given list of roles to a
* list of Acegi GrantedAuthorities. There are no restrictions for the * list of Acegi GrantedAuthorities. There are no restrictions for the
* mapping process; a single role can be mapped to multiple Acegi * mapping process; a single role can be mapped to multiple Acegi
* GrantedAuthorities, all roles can be mapped to a single Acegi * GrantedAuthorities, all roles can be mapped to a single Acegi
* GrantedAuthority, some roles may not be mapped, etc. * GrantedAuthority, some roles may not be mapped, etc.
* *
* @param String[] * @param roles the roles to be mapped
* containing list of roles * @return the list of mapped GrantedAuthorities
* @return GrantedAuthority[] containing list of mapped GrantedAuthorities */
*/ public GrantedAuthority[] getGrantedAuthorities(String[] roles);
public GrantedAuthority[] getGrantedAuthorities(String[] roles);
} }

View File

@ -6,25 +6,28 @@ import org.springframework.util.Assert;
* This class implements the MappableRolesRetriever interface by just returning * This class implements the MappableRolesRetriever interface by just returning
* a list of mappable roles as previously set using the corresponding setter * a list of mappable roles as previously set using the corresponding setter
* method. * method.
*
* @author Ruud Senden
* @since 2.0
*/ */
public class SimpleMappableRolesRetriever implements MappableRolesRetriever { public class SimpleMappableRolesRetriever implements MappableRolesRetriever {
private String[] mappableRoles = null; private String[] mappableRoles = null;
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.springframework.security.rolemapping.MappableRolesRetriever#getMappableRoles() * @see org.springframework.security.rolemapping.MappableRolesRetriever#getMappableRoles()
*/ */
public String[] getMappableRoles() { public String[] getMappableRoles() {
Assert.notNull(mappableRoles, "No mappable roles have been set"); Assert.notNull(mappableRoles, "No mappable roles have been set");
String[] copy = new String[mappableRoles.length]; String[] copy = new String[mappableRoles.length];
System.arraycopy(mappableRoles, 0, copy, 0, copy.length); System.arraycopy(mappableRoles, 0, copy, 0, copy.length);
return copy; return copy;
} }
public void setMappableRoles(String[] aMappableRoles) { public void setMappableRoles(String[] aMappableRoles) {
this.mappableRoles = new String[aMappableRoles.length]; this.mappableRoles = new String[aMappableRoles.length];
System.arraycopy(aMappableRoles, 0, mappableRoles, 0, mappableRoles.length); System.arraycopy(aMappableRoles, 0, mappableRoles, 0, mappableRoles.length);
} }
} }

View File

@ -14,92 +14,92 @@ import org.springframework.util.Assert;
* one-on-one mapping from roles to Acegi GrantedAuthorities. Optionally a * one-on-one mapping from roles to Acegi GrantedAuthorities. Optionally a
* prefix can be added, and the role name can be converted to upper or lower * prefix can be added, and the role name can be converted to upper or lower
* case. * case.
* </p>
*
* <p> * <p>
* By default, the role is prefixed with "ROLE_" unless it already starts with * By default, the role is prefixed with "ROLE_" unless it already starts with
* "ROLE_", and no case conversion is done. * "ROLE_", and no case conversion is done.
* </p> *
* @author Ruud Senden
* @since 2.0
*/ */
public class SimpleRoles2GrantedAuthoritiesMapper implements Roles2GrantedAuthoritiesMapper, InitializingBean { public class SimpleRoles2GrantedAuthoritiesMapper implements Roles2GrantedAuthoritiesMapper, InitializingBean {
private String rolePrefix = "ROLE_"; private String rolePrefix = "ROLE_";
private boolean convertRoleToUpperCase = false; private boolean convertRoleToUpperCase = false;
private boolean convertRoleToLowerCase = false; private boolean convertRoleToLowerCase = false;
private boolean addPrefixIfAlreadyExisting = false; private boolean addPrefixIfAlreadyExisting = false;
/** /**
* Check whether all properties have been set to correct values. * Check whether all properties have been set to correct values.
*/ */
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.isTrue(!(isConvertRoleToUpperCase() && isConvertRoleToLowerCase()), Assert.isTrue(!(isConvertRoleToUpperCase() && isConvertRoleToLowerCase()),
"Either convertRoleToUpperCase or convertRoleToLowerCase can be set to true, but not both"); "Either convertRoleToUpperCase or convertRoleToLowerCase can be set to true, but not both");
} }
/** /**
* Map the given list of roles one-on-one to Acegi GrantedAuthorities. * Map the given list of roles one-on-one to Acegi GrantedAuthorities.
*/ */
public GrantedAuthority[] getGrantedAuthorities(String[] roles) { public GrantedAuthority[] getGrantedAuthorities(String[] roles) {
GrantedAuthority[] result = new GrantedAuthority[roles.length]; GrantedAuthority[] result = new GrantedAuthority[roles.length];
for (int i = 0; i < roles.length; i++) { for (int i = 0; i < roles.length; i++) {
result[i] = getGrantedAuthority(roles[i]); result[i] = getGrantedAuthority(roles[i]);
} }
return result; return result;
} }
/** /**
* Map the given role ono-on-one to an Acegi GrantedAuthority, optionally * Map the given role ono-on-one to an Acegi GrantedAuthority, optionally
* doing case conversion and/or adding a prefix. * doing case conversion and/or adding a prefix.
* *
* @param role * @param role
* The role for which to get a GrantedAuthority * The role for which to get a GrantedAuthority
* @return GrantedAuthority representing the given role. * @return GrantedAuthority representing the given role.
*/ */
private GrantedAuthority getGrantedAuthority(String role) { private GrantedAuthority getGrantedAuthority(String role) {
if (isConvertRoleToLowerCase()) { if (isConvertRoleToLowerCase()) {
role = role.toLowerCase(Locale.getDefault()); role = role.toLowerCase(Locale.getDefault());
} else if (isConvertRoleToUpperCase()) { } else if (isConvertRoleToUpperCase()) {
role = role.toUpperCase(Locale.getDefault()); role = role.toUpperCase(Locale.getDefault());
} }
if (isAddPrefixIfAlreadyExisting() || !role.startsWith(getRolePrefix())) { if (isAddPrefixIfAlreadyExisting() || !role.startsWith(getRolePrefix())) {
return new GrantedAuthorityImpl(getRolePrefix() + role); return new GrantedAuthorityImpl(getRolePrefix() + role);
} else { } else {
return new GrantedAuthorityImpl(role); return new GrantedAuthorityImpl(role);
} }
} }
private boolean isConvertRoleToLowerCase() { private boolean isConvertRoleToLowerCase() {
return convertRoleToLowerCase; return convertRoleToLowerCase;
} }
public void setConvertRoleToLowerCase(boolean b) { public void setConvertRoleToLowerCase(boolean b) {
convertRoleToLowerCase = b; convertRoleToLowerCase = b;
} }
private boolean isConvertRoleToUpperCase() { private boolean isConvertRoleToUpperCase() {
return convertRoleToUpperCase; return convertRoleToUpperCase;
} }
public void setConvertRoleToUpperCase(boolean b) { public void setConvertRoleToUpperCase(boolean b) {
convertRoleToUpperCase = b; convertRoleToUpperCase = b;
} }
private String getRolePrefix() { private String getRolePrefix() {
return rolePrefix == null ? "" : rolePrefix; return rolePrefix == null ? "" : rolePrefix;
} }
public void setRolePrefix(String string) { public void setRolePrefix(String string) {
rolePrefix = string; rolePrefix = string;
} }
private boolean isAddPrefixIfAlreadyExisting() { private boolean isAddPrefixIfAlreadyExisting() {
return addPrefixIfAlreadyExisting; return addPrefixIfAlreadyExisting;
} }
public void setAddPrefixIfAlreadyExisting(boolean b) { public void setAddPrefixIfAlreadyExisting(boolean b) {
addPrefixIfAlreadyExisting = b; addPrefixIfAlreadyExisting = b;
} }
} }

View File

@ -28,154 +28,156 @@ import org.xml.sax.SAXException;
/** /**
* This implementation for the MappableRolesRetriever interface retrieves the * This implementation for the MappableRolesRetriever interface retrieves the
* list of mappable roles from an XML file. * list of mappable roles from an XML file.
* * <p>
* This class is defined as abstract because it is too generic to be used * This class is defined as abstract because it is too generic to be used
* directly. As this class is usually used to read very specific XML files (e.g. * directly. As this class is usually used to read very specific XML files (e.g.
* web.xml, ejb-jar.xml), subclasses should usually define the actual * web.xml, ejb-jar.xml), subclasses should usually define the actual
* XPath-expression to use, and define a more specifically named setter for the * XPath-expression to use, and define a more specifically named setter for the
* XML InputStream (e.g. setWebXmlInputStream). * XML InputStream (e.g. setWebXmlInputStream).
*
* @author Ruud Senden
* @since 2.0
*/ */
public abstract class XmlMappableRolesRetriever implements MappableRolesRetriever, InitializingBean { public abstract class XmlMappableRolesRetriever implements MappableRolesRetriever, InitializingBean {
private static final Log LOG = LogFactory.getLog(XmlMappableRolesRetriever.class); private static final Log LOG = LogFactory.getLog(XmlMappableRolesRetriever.class);
private String[] mappableRoles = null; private String[] mappableRoles = null;
private InputStream xmlInputStream = null; private InputStream xmlInputStream = null;
private String xpathExpression = null; private String xpathExpression = null;
private boolean closeInputStream = true; private boolean closeInputStream = true;
/** /**
* Check whether all required properties have been set. * Check whether all required properties have been set.
*/ */
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.notNull(xmlInputStream, "An XML InputStream must be set"); Assert.notNull(xmlInputStream, "An XML InputStream must be set");
Assert.notNull(xpathExpression, "An XPath expression must be set"); Assert.notNull(xpathExpression, "An XPath expression must be set");
mappableRoles = getMappableRoles(xmlInputStream); mappableRoles = getMappableRoles(xmlInputStream);
} }
public String[] getMappableRoles() { public String[] getMappableRoles() {
String[] copy = new String[mappableRoles.length]; String[] copy = new String[mappableRoles.length];
System.arraycopy(mappableRoles, 0, copy, 0, copy.length); System.arraycopy(mappableRoles, 0, copy, 0, copy.length);
return copy; return copy;
} }
/** /**
* Get the mappable roles from the specified XML document. * Get the mappable roles from the specified XML document.
*/ */
private String[] getMappableRoles(InputStream aStream) { private String[] getMappableRoles(InputStream aStream) {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Reading mappable roles from XML document"); LOG.debug("Reading mappable roles from XML document");
} }
try { try {
Document doc = getDocument(aStream); Document doc = getDocument(aStream);
String[] roles = getMappableRoles(doc); String[] roles = getMappableRoles(doc);
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Mappable roles from XML document: " + ArrayUtils.toString(roles)); LOG.debug("Mappable roles from XML document: " + ArrayUtils.toString(roles));
} }
return roles; return roles;
} finally { } finally {
if (closeInputStream) { if (closeInputStream) {
try { try {
aStream.close(); aStream.close();
} catch (Exception e) { } catch (Exception e) {
LOG.debug("Input stream could not be closed", e); LOG.debug("Input stream could not be closed", e);
} }
} }
} }
}
} /**
* @return Document for the specified InputStream
*/
private Document getDocument(InputStream aStream) {
Document doc;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder db = factory.newDocumentBuilder();
db.setEntityResolver(new MyEntityResolver());
doc = db.parse(new IgnoreCloseInputStream(aStream));
return doc;
} catch (FactoryConfigurationError e) {
throw new RuntimeException("Unable to parse document object", e);
} catch (ParserConfigurationException e) {
throw new RuntimeException("Unable to parse document object", e);
} catch (SAXException e) {
throw new RuntimeException("Unable to parse document object", e);
} catch (IOException e) {
throw new RuntimeException("Unable to parse document object", e);
}
}
/** /**
* @return Document for the specified InputStream * @param doc
*/ * The Document from which to read the list of roles
private Document getDocument(InputStream aStream) { * @return String[] the list of roles.
Document doc; * @throws JaxenException
try { */
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); private String[] getMappableRoles(Document doc) {
factory.setValidating(false); try {
DocumentBuilder db = factory.newDocumentBuilder(); DOMXPath xpath = new DOMXPath(xpathExpression);
db.setEntityResolver(new MyEntityResolver()); List roleElements = xpath.selectNodes(doc);
doc = db.parse(new IgnoreCloseInputStream(aStream)); String[] roles = new String[roleElements.size()];
return doc; for (int i = 0; i < roles.length; i++) {
} catch (FactoryConfigurationError e) { roles[i] = ((Node) roleElements.get(i)).getNodeValue();
throw new RuntimeException("Unable to parse document object", e); }
} catch (ParserConfigurationException e) { return roles;
throw new RuntimeException("Unable to parse document object", e); } catch (JaxenException e) {
} catch (SAXException e) { throw new RuntimeException("Unable to retrieve mappable roles", e);
throw new RuntimeException("Unable to parse document object", e); } catch (DOMException e) {
} catch (IOException e) { throw new RuntimeException("Unable to retrieve mappable roles", e);
throw new RuntimeException("Unable to parse document object", e); }
} }
}
/** /**
* @param doc * Subclasses should provide this method with a more specific name (e.g.
* The Document from which to read the list of roles * indicating the type of XML file the subclass expects, like
* @return String[] the list of roles. * setWebXmlInputStream).
* @throws JaxenException */
*/ protected void setXmlInputStream(InputStream aStream) {
private String[] getMappableRoles(Document doc) { this.xmlInputStream = aStream;
try { }
DOMXPath xpath = new DOMXPath(xpathExpression);
List roleElements = xpath.selectNodes(doc);
String[] roles = new String[roleElements.size()];
for (int i = 0; i < roles.length; i++) {
roles[i] = ((Node) roleElements.get(i)).getNodeValue();
}
return roles;
} catch (JaxenException e) {
throw new RuntimeException("Unable to retrieve mappable roles", e);
} catch (DOMException e) {
throw new RuntimeException("Unable to retrieve mappable roles", e);
}
}
/** /**
* Subclasses should provide this method with a more specific name (e.g. * Subclasses usually want to set an XPath expression by themselves (e.g.
* indicating the type of XML file the subclass expects, like * not user-configurable). However subclasses may provide configuration
* setWebXmlInputStream). * options to for example choose from a list of predefined XPath expressions
*/ * (e.g. to support multiple versions of the same type of XML file), as such
protected void setXmlInputStream(InputStream aStream) { * we provide a setter instead of mandatory constructor argument.
this.xmlInputStream = aStream; */
} protected void setXpathExpression(String anXpathExpression) {
xpathExpression = anXpathExpression;
}
/** /**
* Subclasses usually want to set an XPath expression by themselves (e.g. * Define whether the provided InputStream must be closed after reading it.
* not user-configurable). However subclasses may provide configuration */
* options to for example choose from a list of predefined XPath expressions public void setCloseInputStream(boolean b) {
* (e.g. to support multiple versions of the same type of XML file), as such closeInputStream = b;
* we provide a setter instead of mandatory constructor argument. }
*/
protected void setXpathExpression(String anXpathExpression) {
xpathExpression = anXpathExpression;
}
/** /**
* Define whether the provided InputStream must be closed after reading it. * We do not need to resolve external entities, so just return an empty
*/ * String.
public void setCloseInputStream(boolean b) { */
closeInputStream = b; private static final class MyEntityResolver implements EntityResolver {
} public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new StringReader(""));
}
}
/** public static final class IgnoreCloseInputStream extends FilterInputStream {
* We do not need to resolve external entities, so just return an empty public IgnoreCloseInputStream(InputStream stream) {
* String. super(stream);
*/ }
private static final class MyEntityResolver implements EntityResolver {
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return new InputSource(new StringReader(""));
}
}
public static final class IgnoreCloseInputStream extends FilterInputStream { public void close() throws IOException {
public IgnoreCloseInputStream(InputStream stream) { // do nothing
super(stream); }
} }
public void close() throws IOException {
// do nothing
}
}
} }

View File

@ -29,118 +29,119 @@ import org.springframework.util.Assert;
* Base class for processing filters that handle pre-authenticated authentication requests. Subclasses must implement * Base class for processing filters that handle pre-authenticated authentication requests. Subclasses must implement
* the getPreAuthenticatedPrincipal() and getPreAuthenticatedCredentials() methods. * the getPreAuthenticatedPrincipal() and getPreAuthenticatedCredentials() methods.
* *
* @author Luke Taylor
* @author Ruud Senden * @author Ruud Senden
* @since 2.0 * @since 2.0
*/ */
public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSecurityFilter implements public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSecurityFilter implements
InitializingBean, ApplicationEventPublisherAware { InitializingBean, ApplicationEventPublisherAware {
private static final Log LOG = LogFactory.getLog(AbstractPreAuthenticatedProcessingFilter.class); protected final Log logger = LogFactory.getLog(getClass());
private ApplicationEventPublisher eventPublisher = null; private ApplicationEventPublisher eventPublisher = null;
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl(); private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
private AuthenticationManager authenticationManager = null; private AuthenticationManager authenticationManager = null;
/** /**
* Check whether all required properties have been set. * Check whether all required properties have been set.
*/ */
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.notNull(authenticationManager, "An AuthenticationManager must be set"); Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
} }
/** /**
* Try to authenticate a pre-authenticated user with Spring Security if the user has not yet been authenticated. * Try to authenticate a pre-authenticated user with Spring Security if the user has not yet been authenticated.
*/ */
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication()); logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
} }
if (SecurityContextHolder.getContext().getAuthentication() == null) { if (SecurityContextHolder.getContext().getAuthentication() == null) {
doAuthenticate(request, response); doAuthenticate(request, response);
} }
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }
/** /**
* Do the actual authentication for a pre-authenticated user. * Do the actual authentication for a pre-authenticated user.
*/ */
private void doAuthenticate(HttpServletRequest httpRequest, HttpServletResponse httpResponse) { private void doAuthenticate(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
Authentication authResult = null; Authentication authResult = null;
Object principal = getPreAuthenticatedPrincipal(httpRequest); Object principal = getPreAuthenticatedPrincipal(httpRequest);
Object credentials = getPreAuthenticatedCredentials(httpRequest); Object credentials = getPreAuthenticatedCredentials(httpRequest);
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("AbstractPreAuthenticatedProcessingFilter: preAuthenticatedPrincipal=" + principal + ", trying to authenticate"); logger.debug("AbstractPreAuthenticatedProcessingFilter: preAuthenticatedPrincipal=" + principal + ", trying to authenticate");
} }
try { try {
PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal, credentials); PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal, credentials);
authRequest.setDetails(authenticationDetailsSource.buildDetails(httpRequest)); authRequest.setDetails(authenticationDetailsSource.buildDetails(httpRequest));
authResult = authenticationManager.authenticate(authRequest); authResult = authenticationManager.authenticate(authRequest);
successfulAuthentication(httpRequest, httpResponse, authResult); successfulAuthentication(httpRequest, httpResponse, authResult);
} catch (AuthenticationException failed) { } catch (AuthenticationException failed) {
unsuccessfulAuthentication(httpRequest, httpResponse, failed); unsuccessfulAuthentication(httpRequest, httpResponse, failed);
} }
} }
/** /**
* Puts the <code>Authentication</code> instance returned by the * Puts the <code>Authentication</code> instance returned by the
* authentication manager into the secure context. * authentication manager into the secure context.
*/ */
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) { protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("Authentication success: " + authResult); logger.debug("Authentication success: " + authResult);
} }
SecurityContextHolder.getContext().setAuthentication(authResult); SecurityContextHolder.getContext().setAuthentication(authResult);
// Fire event // Fire event
if (this.eventPublisher != null) { if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
} }
} }
/** /**
* Ensures the authentication object in the secure context is set to null * Ensures the authentication object in the secure context is set to null
* when authentication fails. * when authentication fails.
*/ */
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) { protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
SecurityContextHolder.clearContext(); SecurityContextHolder.clearContext();
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("Cleared security context due to exception", failed); logger.debug("Cleared security context due to exception", failed);
} }
request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed); request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
} }
/** /**
* @param anApplicationEventPublisher * @param anApplicationEventPublisher
* The ApplicationEventPublisher to use * The ApplicationEventPublisher to use
*/ */
public void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) { public void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) {
this.eventPublisher = anApplicationEventPublisher; this.eventPublisher = anApplicationEventPublisher;
} }
/** /**
* @param authenticationDetailsSource * @param authenticationDetailsSource
* The AuthenticationDetailsSource to use * The AuthenticationDetailsSource to use
*/ */
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required"); Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource; this.authenticationDetailsSource = authenticationDetailsSource;
} }
/** /**
* @param authenticationManager * @param authenticationManager
* The AuthenticationManager to use * The AuthenticationManager to use
*/ */
public void setAuthenticationManager(AuthenticationManager authenticationManager) { public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
} }
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest); protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest);
protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest); protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest);
} }

View File

@ -13,6 +13,9 @@ import org.springframework.util.Assert;
/** /**
* This WebAuthenticationDetails implementation allows for storing a list of * This WebAuthenticationDetails implementation allows for storing a list of
* pre-authenticated Granted Authorities. * pre-authenticated Granted Authorities.
*
* @author Ruud Senden
* @since 2.0
*/ */
public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails implements public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends WebAuthenticationDetails implements
PreAuthenticatedGrantedAuthoritiesRetriever, PreAuthenticatedGrantedAuthoritiesSetter { PreAuthenticatedGrantedAuthoritiesRetriever, PreAuthenticatedGrantedAuthoritiesSetter {
@ -36,7 +39,7 @@ public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesRetriever#getPreAuthenticatedGrantedAuthorities() * @see org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesRetriever#getPreAuthenticatedGrantedAuthorities()
*/ */
public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities() { public GrantedAuthority[] getPreAuthenticatedGrantedAuthorities() {
@ -48,7 +51,7 @@ public class PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails extends
/* /*
* (non-Javadoc) * (non-Javadoc)
* *
* @see org.springframework.security.providers.preauth.j2ee.PreAuthenticatedGrantedAuthoritiesSetter#setJ2eeBasedGrantedAuthorities() * @see org.springframework.security.providers.preauth.j2ee.PreAuthenticatedGrantedAuthoritiesSetter#setJ2eeBasedGrantedAuthorities()
*/ */
public void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aJ2eeBasedGrantedAuthorities) { public void setPreAuthenticatedGrantedAuthorities(GrantedAuthority[] aJ2eeBasedGrantedAuthorities) {

View File

@ -20,26 +20,26 @@ import org.springframework.core.Ordered;
* user will already have been identified through some external mechanism and a * user will already have been identified through some external mechanism and a
* secure context established by the time the security-enforcement filter is * secure context established by the time the security-enforcement filter is
* invoked. * invoked.
* </p>
* <p> * <p>
* Therefore this class isn't actually responsible for the commencement of * Therefore this class isn't actually responsible for the commencement of
* authentication, as it is in the case of other providers. It will be called if * authentication, as it is in the case of other providers. It will be called if
* the user is rejected by the AbstractPreAuthenticatedProcessingFilter, * the user is rejected by the AbstractPreAuthenticatedProcessingFilter,
* resulting in a null authentication. * resulting in a null authentication.
* </p>
* <p> * <p>
* The <code>commence</code> method will always return an * The <code>commence</code> method will always return an
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error). * <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).
* </p>
* <p> * <p>
* This code is based on * This code is based on
* {@link org.springframework.security.ui.x509.X509ProcessingFilterEntryPoint}. * {@link org.springframework.security.ui.x509.X509ProcessingFilterEntryPoint}.
* </p> *
*
* @see org.springframework.security.ui.ExceptionTranslationFilter * @see org.springframework.security.ui.ExceptionTranslationFilter
*
* @author Luke Taylor
* @author Ruud Senden
* @since 2.0
*/ */
public class PreAuthenticatedProcesingFilterEntryPoint implements AuthenticationEntryPoint, Ordered { public class PreAuthenticatedProcessingFilterEntryPoint implements AuthenticationEntryPoint, Ordered {
private static final Log LOG = LogFactory.getLog(PreAuthenticatedProcesingFilterEntryPoint.class); private static final Log logger = LogFactory.getLog(PreAuthenticatedProcessingFilterEntryPoint.class);
private int order = Integer.MAX_VALUE; private int order = Integer.MAX_VALUE;
@ -48,8 +48,8 @@ public class PreAuthenticatedProcesingFilterEntryPoint implements Authentication
*/ */
public void commence(ServletRequest request, ServletResponse response, AuthenticationException arg2) throws IOException, public void commence(ServletRequest request, ServletResponse response, AuthenticationException arg2) throws IOException,
ServletException { ServletException {
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("J2EE entry point called. Rejecting access"); logger.debug("Pre-authenticated entry point called. Rejecting access");
} }
HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied"); httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");

View File

@ -18,84 +18,84 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert; import org.springframework.util.Assert;
public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl implements InitializingBean { public class J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource extends AuthenticationDetailsSourceImpl implements InitializingBean {
private static final Log LOG = LogFactory.getLog(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class); private static final Log logger = LogFactory.getLog(J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource.class);
private String[] j2eeMappableRoles; private String[] j2eeMappableRoles;
private Roles2GrantedAuthoritiesMapper j2eeUserRoles2GrantedAuthoritiesMapper; private Roles2GrantedAuthoritiesMapper j2eeUserRoles2GrantedAuthoritiesMapper;
/** /**
* Public constructor which overrides the default AuthenticationDetails * Public constructor which overrides the default AuthenticationDetails
* class to be used. * class to be used.
*/ */
public J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() { public J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource() {
super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class); super.setClazz(PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails.class);
} }
/** /**
* Check that all required properties have been set. * Check that all required properties have been set.
*/ */
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
Assert.notNull(j2eeMappableRoles, "J2EE defined roles not available"); Assert.notNull(j2eeMappableRoles, "J2EE defined roles not available");
Assert.notNull(j2eeUserRoles2GrantedAuthoritiesMapper, "J2EE user roles to granted authorities mapper not set"); Assert.notNull(j2eeUserRoles2GrantedAuthoritiesMapper, "J2EE user roles to granted authorities mapper not set");
} }
/** /**
* Build the authentication details object. If the speficied authentication * Build the authentication details object. If the speficied authentication
* details class implements the PreAuthenticatedGrantedAuthoritiesSetter, a * details class implements the PreAuthenticatedGrantedAuthoritiesSetter, a
* list of pre-authenticated Granted Authorities will be set based on the * list of pre-authenticated Granted Authorities will be set based on the
* J2EE roles for the current user. * J2EE roles for the current user.
* *
* @see org.springframework.security.ui.AuthenticationDetailsSource#buildDetails(javax.servlet.http.HttpServletRequest) * @see org.springframework.security.ui.AuthenticationDetailsSource#buildDetails(javax.servlet.http.HttpServletRequest)
*/ */
public Object buildDetails(HttpServletRequest request) { public Object buildDetails(HttpServletRequest request) {
Object result = super.buildDetails(request); Object result = super.buildDetails(request);
if (result instanceof PreAuthenticatedGrantedAuthoritiesSetter) { if (result instanceof PreAuthenticatedGrantedAuthoritiesSetter) {
((PreAuthenticatedGrantedAuthoritiesSetter) result) ((PreAuthenticatedGrantedAuthoritiesSetter) result)
.setPreAuthenticatedGrantedAuthorities(getJ2eeBasedGrantedAuthorities(request)); .setPreAuthenticatedGrantedAuthorities(getJ2eeBasedGrantedAuthorities(request));
} }
return result; return result;
} }
/** /**
* Get a list of Granted Authorities based on the current user's J2EE roles. * Get a list of Granted Authorities based on the current user's J2EE roles.
* *
* @param request * @param request
* The HttpServletRequest * The HttpServletRequest
* @return GrantedAuthority[] mapped from the user's J2EE roles. * @return GrantedAuthority[] mapped from the user's J2EE roles.
*/ */
private GrantedAuthority[] getJ2eeBasedGrantedAuthorities(HttpServletRequest request) { private GrantedAuthority[] getJ2eeBasedGrantedAuthorities(HttpServletRequest request) {
ArrayList j2eeUserRolesList = new ArrayList(); ArrayList j2eeUserRolesList = new ArrayList();
for (int i = 0; i < j2eeMappableRoles.length; i++) { for (int i = 0; i < j2eeMappableRoles.length; i++) {
if (request.isUserInRole(j2eeMappableRoles[i])) { if (request.isUserInRole(j2eeMappableRoles[i])) {
j2eeUserRolesList.add(j2eeMappableRoles[i]); j2eeUserRolesList.add(j2eeMappableRoles[i]);
} }
} }
String[] j2eeUserRoles = new String[j2eeUserRolesList.size()]; String[] j2eeUserRoles = new String[j2eeUserRolesList.size()];
j2eeUserRoles = (String[]) j2eeUserRolesList.toArray(j2eeUserRoles); j2eeUserRoles = (String[]) j2eeUserRolesList.toArray(j2eeUserRoles);
GrantedAuthority[] userGas = j2eeUserRoles2GrantedAuthoritiesMapper.getGrantedAuthorities(j2eeUserRoles); GrantedAuthority[] userGas = j2eeUserRoles2GrantedAuthoritiesMapper.getGrantedAuthorities(j2eeUserRoles);
if (LOG.isDebugEnabled()) { if (logger.isDebugEnabled()) {
LOG.debug("J2EE user roles [" + StringUtils.join(j2eeUserRoles) + "] mapped to Granted Authorities: [" logger.debug("J2EE user roles [" + StringUtils.join(j2eeUserRoles) + "] mapped to Granted Authorities: ["
+ StringUtils.join(userGas) + "]"); + StringUtils.join(userGas) + "]");
} }
return userGas; return userGas;
} }
/** /**
* @param aJ2eeMappableRolesRetriever * @param aJ2eeMappableRolesRetriever
* The MappableRolesRetriever to use * The MappableRolesRetriever to use
*/ */
public void setJ2eeMappableRolesRetriever(MappableRolesRetriever aJ2eeMappableRolesRetriever) { public void setJ2eeMappableRolesRetriever(MappableRolesRetriever aJ2eeMappableRolesRetriever) {
this.j2eeMappableRoles = aJ2eeMappableRolesRetriever.getMappableRoles(); this.j2eeMappableRoles = aJ2eeMappableRolesRetriever.getMappableRoles();
} }
/** /**
* @param mapper * @param mapper
* The Roles2GrantedAuthoritiesMapper to use * The Roles2GrantedAuthoritiesMapper to use
*/ */
public void setJ2eeUserRoles2GrantedAuthoritiesMapper(Roles2GrantedAuthoritiesMapper mapper) { public void setJ2eeUserRoles2GrantedAuthoritiesMapper(Roles2GrantedAuthoritiesMapper mapper) {
j2eeUserRoles2GrantedAuthoritiesMapper = mapper; j2eeUserRoles2GrantedAuthoritiesMapper = mapper;
} }
} }

View File

@ -3,36 +3,35 @@ package org.springframework.security.ui.preauth.j2ee;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter; import org.springframework.security.ui.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/** /**
* This AbstractPreAuthenticatedProcessingFilter implementation is based on the * This AbstractPreAuthenticatedProcessingFilter implementation is based on the
* J2EE container-based authentication mechanism. It will use the J2EE user * J2EE container-based authentication mechanism. It will use the J2EE user
* principal name as the pre-authenticated principal. * principal name as the pre-authenticated principal.
*
* @author Ruud Senden
* @since 2.0
*/ */
public class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter { public class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
private static final Log LOG = LogFactory.getLog(J2eePreAuthenticatedProcessingFilter.class); /**
* Return the J2EE user name.
*/
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) {
Object principal = httpRequest.getUserPrincipal() == null ? null : httpRequest.getUserPrincipal().getName();
if (logger.isDebugEnabled()) {
logger.debug("PreAuthenticated J2EE principal: " + principal);
}
return principal;
}
/** /**
* Return the J2EE user name. * For J2EE container-based authentication there is no generic way to
*/ * retrieve the credentials, as such this method returns a fixed dummy
protected Object getPreAuthenticatedPrincipal(HttpServletRequest httpRequest) { * value.
Object principal = httpRequest.getUserPrincipal() == null ? null : httpRequest.getUserPrincipal().getName(); */
if (LOG.isDebugEnabled()) { protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
LOG.debug("PreAuthenticated J2EE principal: " + principal); return "N/A";
} }
return principal;
}
/**
* For J2EE container-based authentication there is no generic way to
* retrieve the credentials, as such this method returns a fixed dummy
* value.
*/
protected Object getPreAuthenticatedCredentials(HttpServletRequest httpRequest) {
return "N/A";
}
public int getOrder() { public int getOrder() {
return 0; return 0;

View File

@ -9,44 +9,42 @@ import org.springframework.security.rolemapping.XmlMappableRolesRetriever;
* This MappableRolesRetriever implementation reads the list of defined J2EE * This MappableRolesRetriever implementation reads the list of defined J2EE
* roles from a web.xml file. It's functionality is based on the * roles from a web.xml file. It's functionality is based on the
* XmlMappableRolesRetriever base class. * XmlMappableRolesRetriever base class.
* </p>
*
* <p> * <p>
* Example on how to configure this MappableRolesRetriever in the Spring * Example on how to configure this MappableRolesRetriever in the Spring
* configuration file: * configuration file:
* *
* <pre> * <pre>
* *
* *
* &lt;bean id=&quot;j2eeMappableRolesRetriever&quot; class=&quot;org.springframework.security.ui.preauth.j2ee.WebXmlMappableRolesRetriever&quot;&gt; * &lt;bean id=&quot;j2eeMappableRolesRetriever&quot; class=&quot;org.springframework.security.ui.preauth.j2ee.WebXmlMappableRolesRetriever&quot;&gt;
* &lt;property name=&quot;webXmlInputStream&quot;&gt;&lt;bean factory-bean=&quot;webXmlResource&quot; factory-method=&quot;getInputStream&quot;/&gt;&lt;/property&gt; * &lt;property name=&quot;webXmlInputStream&quot;&gt;&lt;bean factory-bean=&quot;webXmlResource&quot; factory-method=&quot;getInputStream&quot;/&gt;&lt;/property&gt;
* &lt;/bean&gt; * &lt;/bean&gt;
* &lt;bean id=&quot;webXmlResource&quot; class=&quot;org.springframework.web.context.support.ServletContextResource&quot;&gt; * &lt;bean id=&quot;webXmlResource&quot; class=&quot;org.springframework.web.context.support.ServletContextResource&quot;&gt;
* &lt;constructor-arg&gt;&lt;ref local=&quot;servletContext&quot;/&gt;&lt;/constructor-arg&gt; * &lt;constructor-arg&gt;&lt;ref local=&quot;servletContext&quot;/&gt;&lt;/constructor-arg&gt;
* &lt;constructor-arg&gt;&lt;value&gt;/WEB-INF/web.xml&lt;/value&gt;&lt;/constructor-arg&gt; * &lt;constructor-arg&gt;&lt;value&gt;/WEB-INF/web.xml&lt;/value&gt;&lt;/constructor-arg&gt;
* &lt;/bean&gt; * &lt;/bean&gt;
* &lt;bean id=&quot;servletContext&quot; class=&quot;org.springframework.web.context.support.ServletContextFactoryBean&quot;/&gt; * &lt;bean id=&quot;servletContext&quot; class=&quot;org.springframework.web.context.support.ServletContextFactoryBean&quot;/&gt;
* *
*
* </pre> * </pre>
* *
* </p> * @author Ruud Senden
* @since 2.0
*/ */
public class WebXmlMappableRolesRetriever extends XmlMappableRolesRetriever { public class WebXmlMappableRolesRetriever extends XmlMappableRolesRetriever {
private static final String XPATH_EXPR = "/web-app/security-role/role-name/text()"; private static final String XPATH_EXPR = "/web-app/security-role/role-name/text()";
/** /**
* Constructor setting the XPath expression to use * Constructor setting the XPath expression to use
*/ */
public WebXmlMappableRolesRetriever() { public WebXmlMappableRolesRetriever() {
super.setXpathExpression(XPATH_EXPR); super.setXpathExpression(XPATH_EXPR);
} }
/** /**
* @param anInputStream * @param anInputStream
* The InputStream to read the XML data from * The InputStream to read the XML data from
*/ */
public void setWebXmlInputStream(InputStream anInputStream) { public void setWebXmlInputStream(InputStream anInputStream) {
super.setXmlInputStream(anInputStream); super.setXmlInputStream(anInputStream);
} }
} }

View File

@ -13,22 +13,22 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
/** /**
* *
* @author TSARDD * @author TSARDD
* @since 18-okt-2007 * @since 18-okt-2007
*/ */
public class PreAuthenticatedProcesingFilterEntryPointTests extends TestCase { public class PreAuthenticatedProcesingFilterEntryPointTests extends TestCase {
public void testGetSetOrder() { public void testGetSetOrder() {
PreAuthenticatedProcesingFilterEntryPoint fep = new PreAuthenticatedProcesingFilterEntryPoint(); PreAuthenticatedProcessingFilterEntryPoint fep = new PreAuthenticatedProcessingFilterEntryPoint();
fep.setOrder(333); fep.setOrder(333);
assertEquals(fep.getOrder(), 333); assertEquals(fep.getOrder(), 333);
} }
public void testCommence() { public void testCommence() {
MockHttpServletRequest req = new MockHttpServletRequest(); MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse resp = new MockHttpServletResponse(); MockHttpServletResponse resp = new MockHttpServletResponse();
PreAuthenticatedProcesingFilterEntryPoint fep = new PreAuthenticatedProcesingFilterEntryPoint(); PreAuthenticatedProcessingFilterEntryPoint fep = new PreAuthenticatedProcessingFilterEntryPoint();
try { try {
fep.commence(req,resp,new AuthenticationCredentialsNotFoundException("test")); fep.commence(req,resp,new AuthenticationCredentialsNotFoundException("test"));
assertEquals("Incorrect status",resp.getStatus(),HttpServletResponse.SC_FORBIDDEN); assertEquals("Incorrect status",resp.getStatus(),HttpServletResponse.SC_FORBIDDEN);
@ -37,6 +37,6 @@ public class PreAuthenticatedProcesingFilterEntryPointTests extends TestCase {
} catch (ServletException e) { } catch (ServletException e) {
fail("Unexpected exception thrown: "+e); fail("Unexpected exception thrown: "+e);
} }
} }
} }

116
samples/preauth/pom.xml Normal file
View File

@ -0,0 +1,116 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples</artifactId>
<version>2.0-SNAPSHOT</version>
</parent>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-samples-preauth</artifactId>
<name>Spring Security - Preauthentiation sample</name>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core-tiger</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.0.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.1</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-core</artifactId>
<version>1.0.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.directory.server</groupId>
<artifactId>apacheds-server-jndi</artifactId>
<version>1.0.2</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.4.3</version>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap</artifactId>
<version>1.2.1</version>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.5</version>
<configuration>
<contextPath>/preauth</contextPath>
<userRealms>
<userRealm implementation="org.mortbay.jetty.security.HashUserRealm">
<name>Preauth Realm</name>
<config>realm.properties</config>
</userRealm>
</userRealms>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,3 @@
rod: koala,ROLE_SUPERVISOR,ROLE_USER
bob: bobspassword,ROLE_USER
user: password

View File

@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Sample namespace-based configuration
-
- $Id: applicationContext-security-ns.xml 2396 2007-12-23 16:36:44Z luke_t $
-->
<b:beans xmlns="http://www.springframework.org/schema/security"
xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
<b:bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
<filter-chain-map path-type="ant">
<filter-chain pattern="/**" filters="sif,j2eePreAuthFilter,logoutFilter,etf,fsi"/>
</filter-chain-map>
</b:bean>
<b:bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
<b:property name="providers">
<b:list>
<b:ref local="preAuthenticatedAuthenticationProvider"/>
</b:list>
</b:property>
</b:bean>
<b:bean id="sif" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
<b:bean id="preAuthenticatedAuthenticationProvider" class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider">
<b:property name="preAuthenticatedUserDetailsService" ref="preAuthenticatedUserDetailsService"/>
</b:bean>
<b:bean id="preAuthenticatedUserDetailsService"
class="org.springframework.security.providers.preauth.PreAuthenticatedGrantedAuthoritiesUserDetailsService"/>
<b:bean id="j2eePreAuthFilter" class="org.springframework.security.ui.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
</b:bean>
<b:bean id="preAuthenticatedProcessingFilterEntryPoint"
class="org.springframework.security.ui.preauth.PreAuthenticatedProcessingFilterEntryPoint"/>
<b:bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
<b:constructor-arg value="/"/>
<b:constructor-arg>
<b:list>
<b:bean class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"/>
</b:list>
</b:constructor-arg>
</b:bean>
<b:bean id="authenticationDetailsSource" class="org.springframework.security.ui.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
<b:property name="j2eeMappableRolesRetriever">
<b:ref local="j2eeMappableRolesRetriever"/>
</b:property>
<b:property name="j2eeUserRoles2GrantedAuthoritiesMapper">
<b:ref local="j2eeUserRoles2GrantedAuthoritiesMapper"/>
</b:property>
</b:bean>
<b:bean id="j2eeUserRoles2GrantedAuthoritiesMapper" class="org.springframework.security.rolemapping.SimpleRoles2GrantedAuthoritiesMapper">
<b:property name="convertRoleToUpperCase" value="true"/>
</b:bean>
<b:bean id="j2eeMappableRolesRetriever" class="org.springframework.security.ui.preauth.j2ee.WebXmlMappableRolesRetriever">
<b:property name="webXmlInputStream"><b:bean factory-bean="webXmlResource" factory-method="getInputStream"/>
</b:property>
</b:bean>
<b:bean id="webXmlResource" class="org.springframework.web.context.support.ServletContextResource">
<b:constructor-arg ref="servletContext"/>
<b:constructor-arg value="/WEB-INF/web.xml"/>
</b:bean>
<b:bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>
<b:bean id="etf" class="org.springframework.security.ui.ExceptionTranslationFilter">
<b:property name="authenticationEntryPoint">
<b:ref local="preAuthenticatedProcessingFilterEntryPoint"/>
</b:property>
</b:bean>
<b:bean id="httpRequestAccessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<b:property name="allowIfAllAbstainDecisions" value="false"/>
<b:property name="decisionVoters">
<b:list>
<b:ref bean="roleVoter"/>
</b:list>
</b:property>
</b:bean>
<b:bean id="fsi" class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<b:property name="authenticationManager" ref="authenticationManager"/>
<b:property name="accessDecisionManager">
<b:ref local="httpRequestAccessDecisionManager"/>
</b:property>
<b:property name="objectDefinitionSource">
<b:value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/secure/extreme/**=ROLE_SUPERVISOR
/secure/**=ROLE_USER
/**=ROLE_USER
</b:value>
</b:property>
</b:bean>
<b:bean id="roleVoter" class="org.springframework.security.vote.RoleVoter"/>
<b:bean id="securityContextHolderAwareRequestFilter" class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter">
<b:property name="wrapperClass" value="org.springframework.security.wrapper.SecurityContextHolderAwareRequestWrapper"/>
</b:bean>
</b:beans>

View File

@ -0,0 +1,20 @@
# Global logging configuration
log4j.rootLogger=INFO, stdout, fileout
log4j.logger.org.springframework.security=DEBUG, stdout, fileout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.conversionPattern=[%p,%c{1},%t] %m%n
# Rolling log file output...
log4j.appender.fileout=org.apache.log4j.RollingFileAppender
log4j.appender.fileout.File=spring-security-preauth.log
#log4j.appender.fileout.File=${webapp.root}/WEB-INF/log4j.log
log4j.appender.fileout.MaxFileSize=1024KB
log4j.appender.fileout.MaxBackupIndex=1
log4j.appender.fileout.layout=org.apache.log4j.PatternLayout
log4j.appender.fileout.layout.conversionPattern=%d{ABSOLUTE} %5p %c{1},%t:%L - %m%n

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Tutorial web application
-
- $Id: web.xml 2476 2008-01-18 18:17:09Z luke_t $
-->
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>Spring Security Preauthentication Demo Application</display-name>
<!--
- Location of the XML file that defines the root application context
- Applied by ContextLoaderListener.
-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
</param-value>
</context-param>
<filter>
<filter-name>filterChainProxy</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>filterChainProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--
- Loads the root application context of this web app at startup.
- The application context is then available via
- WebApplicationContextUtils.getWebApplicationContext(servletContext).
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--
- Publishes events for session creation and destruction through the application
- context. Optional unless concurrent session control is being used.
-->
<listener>
<listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
</listener>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Preauth Realm</realm-name>
</login-config>
<security-role>
<role-name>ROLE_USER</role-name>
</security-role>
<security-role>
<role-name>ROLE_SUPERVISOR</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>All areas</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>ROLE_USER</role-name>
</auth-constraint>
</security-constraint>
</web-app>

View File

@ -0,0 +1,11 @@
<html>
<body>
<h1>Home Page</h1>
<p>Anyone can view this page.</p>
<p>Your principal object is....: <%= request.getUserPrincipal() %></p>
<p><a href="secure/index.jsp">Secure page</a></p>
<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
</body>
</html>

View File

@ -0,0 +1,15 @@
<%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %>
<html>
<body>
<h1>VERY Secure Page</h1>
This is a protected page. You can only see me if you are a supervisor.
<authz:authorize ifAllGranted="ROLE_SUPERVISOR">
You have "ROLE_SUPERVISOR" (this text is surrounded by &lt;authz:authorize&gt; tags).
</authz:authorize>
<p><a href="../../">Home</a>
<p><a href="../../j_spring_security_logout">Logout</a>
</body>
</html>

View File

@ -0,0 +1,15 @@
<html>
<body>
<h1>Secure Page</h1>
This is a protected page. You can get to me if you've been remembered,
or if you've authenticated this session.<br><br>
<%if (request.isUserInRole("ROLE_SUPERVISOR")) { %>
You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br><br>
<% } %>
<p><a href="../">Home</a>
<p><a href="../j_spring_security_logout">Logout</a>
</body>
</html>