mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-24 13:02:13 +00:00
SEC-309: Patch for Authentication tag to use property of authentication object, rather than invoking an operation on the principal. Allows use of nested properties.
This commit is contained in:
parent
e0d0cc20c7
commit
10ab4136d1
@ -20,137 +20,109 @@ import org.springframework.security.Authentication;
|
||||
import org.springframework.security.context.SecurityContext;
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
|
||||
import org.springframework.security.userdetails.UserDetails;
|
||||
import org.springframework.beans.BeanWrapperImpl;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.web.util.TagUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.jsp.JspException;
|
||||
import javax.servlet.jsp.PageContext;
|
||||
import javax.servlet.jsp.tagext.Tag;
|
||||
import javax.servlet.jsp.tagext.TagSupport;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link javax.servlet.jsp.tagext.Tag} implementation that allows convenient access to the current
|
||||
* <code>Authentication</code> object.<p>Whilst JSPs can access the <code>SecurityContext</code> directly, this tag
|
||||
* avoids handling <code>null</code> conditions. The tag also properly accommodates
|
||||
* <code>Authentication.getPrincipal()</code>, which can either be a <code>String</code> or a
|
||||
* <code>UserDetails</code>.</p>
|
||||
* <code>Authentication</code> object. The <tt>operation</tt> attribute
|
||||
* <p>
|
||||
* Whilst JSPs can access the <code>SecurityContext</code> directly, this tag avoids handling <code>null</code> conditions.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Thomas Champagne
|
||||
* @version $Id$
|
||||
*/
|
||||
public class AuthenticationTag extends TagSupport {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
||||
private static final Set methodPrefixValidOptions = new HashSet();
|
||||
|
||||
static {
|
||||
methodPrefixValidOptions.add("get");
|
||||
methodPrefixValidOptions.add("is");
|
||||
}
|
||||
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private String methodPrefix = "get";
|
||||
private String operation = "";
|
||||
private String var;
|
||||
private String property;
|
||||
private int scope;
|
||||
private boolean scopeSpecified;
|
||||
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
public AuthenticationTag() {
|
||||
init();
|
||||
}
|
||||
|
||||
// resets local state
|
||||
private void init() {
|
||||
var = null;
|
||||
scopeSpecified = false;
|
||||
scope = PageContext.PAGE_SCOPE;
|
||||
}
|
||||
public void setVar(String var) {
|
||||
this.var = var;
|
||||
}
|
||||
|
||||
public void setProperty(String operation) {
|
||||
this.property = operation;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = TagUtils.getScope(scope);
|
||||
this.scopeSpecified = true;
|
||||
}
|
||||
|
||||
public int doStartTag() throws JspException {
|
||||
if ((null == operation) || "".equals(operation)) {
|
||||
return Tag.SKIP_BODY;
|
||||
}
|
||||
|
||||
validateArguments();
|
||||
|
||||
if ((SecurityContextHolder.getContext() == null)
|
||||
|| !(SecurityContextHolder.getContext() instanceof SecurityContext)
|
||||
|| (((SecurityContext) SecurityContextHolder.getContext()).getAuthentication() == null)) {
|
||||
return Tag.SKIP_BODY;
|
||||
}
|
||||
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
if (auth.getPrincipal() == null) {
|
||||
return Tag.SKIP_BODY;
|
||||
} else if (auth.getPrincipal() instanceof UserDetails) {
|
||||
writeMessage(invokeOperation(auth.getPrincipal()));
|
||||
|
||||
return Tag.SKIP_BODY;
|
||||
} else {
|
||||
writeMessage(auth.getPrincipal().toString());
|
||||
|
||||
return Tag.SKIP_BODY;
|
||||
}
|
||||
return super.doStartTag();
|
||||
}
|
||||
|
||||
public String getMethodPrefix() {
|
||||
return methodPrefix;
|
||||
}
|
||||
public int doEndTag() throws JspException {
|
||||
Object result = null;
|
||||
// determine the value by...
|
||||
if (property != null) {
|
||||
if ((SecurityContextHolder.getContext() == null)
|
||||
|| !(SecurityContextHolder.getContext() instanceof SecurityContext)
|
||||
|| (SecurityContextHolder.getContext().getAuthentication() == null)) {
|
||||
return Tag.EVAL_PAGE;
|
||||
}
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
|
||||
protected String invokeOperation(Object obj) throws JspException {
|
||||
Class clazz = obj.getClass();
|
||||
String methodToInvoke = getOperation();
|
||||
StringBuffer methodName = new StringBuffer();
|
||||
methodName.append(getMethodPrefix());
|
||||
methodName.append(methodToInvoke.substring(0, 1).toUpperCase());
|
||||
methodName.append(methodToInvoke.substring(1));
|
||||
|
||||
Method method = null;
|
||||
|
||||
try {
|
||||
method = clazz.getMethod(methodName.toString(), (Class[]) null);
|
||||
} catch (SecurityException se) {
|
||||
throw new JspException(se);
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
throw new JspException(nsme);
|
||||
if (auth.getPrincipal() == null) {
|
||||
return Tag.EVAL_PAGE;
|
||||
} else {
|
||||
try {
|
||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(auth);
|
||||
result = wrapper.getPropertyValue(property);
|
||||
} catch (BeansException e) {
|
||||
throw new JspException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object retVal = null;
|
||||
|
||||
try {
|
||||
retVal = method.invoke(obj, (Object[]) null);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new JspException(iae);
|
||||
} catch (IllegalAccessException iae) {
|
||||
throw new JspException(iae);
|
||||
} catch (InvocationTargetException ite) {
|
||||
throw new JspException(ite);
|
||||
}
|
||||
|
||||
if (retVal == null) {
|
||||
retVal = "";
|
||||
}
|
||||
|
||||
return retVal.toString();
|
||||
}
|
||||
|
||||
public void setMethodPrefix(String methodPrefix) {
|
||||
this.methodPrefix = methodPrefix;
|
||||
}
|
||||
|
||||
public void setOperation(String operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
|
||||
protected void validateArguments() throws JspException {
|
||||
if ((getMethodPrefix() != null) && !getMethodPrefix().equals("")) {
|
||||
if (!methodPrefixValidOptions.contains(getMethodPrefix())) {
|
||||
throw new JspException("Authorization tag : no valid method prefix available");
|
||||
if (var != null) {
|
||||
/*
|
||||
* Store the result, letting an IllegalArgumentException
|
||||
* propagate back if the scope is invalid (e.g., if an attempt
|
||||
* is made to store something in the session without any
|
||||
* HttpSession existing).
|
||||
*/
|
||||
if (result != null) {
|
||||
pageContext.setAttribute(var, result, scope);
|
||||
} else {
|
||||
if (scopeSpecified) {
|
||||
pageContext.removeAttribute(var, scope);
|
||||
} else {
|
||||
pageContext.removeAttribute(var);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new JspException("Authorization tag : no method prefix available");
|
||||
writeMessage(result.toString());
|
||||
}
|
||||
return EVAL_PAGE;
|
||||
}
|
||||
|
||||
protected void writeMessage(String msg) throws JspException {
|
||||
|
@ -68,7 +68,7 @@ public class AuthzImpl implements Authz {
|
||||
public String getPrincipal() {
|
||||
MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
|
||||
|
||||
authenticationTag.setOperation("username");
|
||||
authenticationTag.setProperty("username");
|
||||
|
||||
try {
|
||||
authenticationTag.doStartTag();
|
||||
|
@ -8,8 +8,8 @@
|
||||
<short-name>security</short-name>
|
||||
<uri>http://www.springframework.org/security/tags</uri>
|
||||
<description>
|
||||
Spring Securitys Authorization Tag Library
|
||||
$Id: authz.tld 2176 2007-10-03 14:02:39Z luke_t $
|
||||
Spring Security Authorization Tag Library
|
||||
$Id$
|
||||
</description>
|
||||
|
||||
<tag>
|
||||
@ -51,35 +51,43 @@
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>authentication</name>
|
||||
<tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class>
|
||||
<description>
|
||||
<tag>
|
||||
<name>authentication</name>
|
||||
<tag-class>org.springframework.security.taglibs.authz.AuthenticationTag</tag-class>
|
||||
<description>
|
||||
Allows access to the current Authentication object.
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>operation</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<description>
|
||||
Must be one of the methods of an instance that implements the UserDetails
|
||||
interface. Use the JavaBean style property, you can provide a custom prefix
|
||||
for the method to call.
|
||||
</description>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<name>methodPrefix</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<description>
|
||||
Must be get or is. This is used to determine the name of the
|
||||
method to be called. The default is get.
|
||||
</description>
|
||||
</attribute>
|
||||
</tag>
|
||||
</description>
|
||||
|
||||
<attribute>
|
||||
<name>property</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
<description>
|
||||
Property of the Authentication object which should be output. Supports nested
|
||||
properties. For example if the principal object is an instance of UserDetails,
|
||||
te property "principal.username" will return the username. Alternatively, using
|
||||
"name" will call getName method on the Authentication object directly.
|
||||
</description>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>false</rtexprvalue>
|
||||
<description>
|
||||
Name of the exported scoped variable for the
|
||||
exception thrown from a nested action. The type of the
|
||||
scoped variable is the type of the exception thrown.
|
||||
</description>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>false</rtexprvalue>
|
||||
<description>
|
||||
Scope for var.
|
||||
</description>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<name>acl</name>
|
||||
|
@ -19,11 +19,8 @@ import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.GrantedAuthority;
|
||||
|
||||
import org.springframework.security.context.SecurityContextHolder;
|
||||
|
||||
import org.springframework.security.providers.TestingAuthenticationToken;
|
||||
|
||||
import org.springframework.security.userdetails.User;
|
||||
|
||||
import javax.servlet.jsp.JspException;
|
||||
@ -40,6 +37,8 @@ public class AuthenticationTagTests extends TestCase {
|
||||
//~ Instance fields ================================================================================================
|
||||
|
||||
private final MyAuthenticationTag authenticationTag = new MyAuthenticationTag();
|
||||
private final Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
|
||||
true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
|
||||
|
||||
//~ Methods ========================================================================================================
|
||||
|
||||
@ -47,86 +46,67 @@ public class AuthenticationTagTests extends TestCase {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
public void testOperationAndMethodPrefixWhenPrincipalIsAUserDetailsInstance()
|
||||
throws JspException {
|
||||
Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
|
||||
true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
|
||||
public void testOperationWhenPrincipalIsAUserDetailsInstance()throws JspException {
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
authenticationTag.setOperation("username");
|
||||
authenticationTag.setMethodPrefix("get");
|
||||
authenticationTag.setProperty("name");
|
||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||
assertEquals("rodUserDetails", authenticationTag.getLastMessage());
|
||||
}
|
||||
|
||||
public void testOperationWhenPrincipalIsAString() throws JspException {
|
||||
Authentication auth = new TestingAuthenticationToken("rodAsString", "koala", new GrantedAuthority[] {});
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken("rodAsString", "koala", new GrantedAuthority[] {}));
|
||||
|
||||
authenticationTag.setOperation("principal");
|
||||
authenticationTag.setProperty("principal");
|
||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||
assertEquals("rodAsString", authenticationTag.getLastMessage());
|
||||
}
|
||||
|
||||
public void testOperationWhenPrincipalIsAUserDetailsInstance()
|
||||
throws JspException {
|
||||
Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
|
||||
true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
|
||||
public void testNestedPropertyIsReadCorrectly() throws JspException {
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
|
||||
authenticationTag.setOperation("username");
|
||||
authenticationTag.setProperty("principal.username");
|
||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||
assertEquals("rodUserDetails", authenticationTag.getLastMessage());
|
||||
}
|
||||
|
||||
public void testOperationWhenPrincipalIsNull() throws JspException {
|
||||
Authentication auth = new TestingAuthenticationToken(null, "koala", new GrantedAuthority[] {});
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
SecurityContextHolder.getContext().setAuthentication(
|
||||
new TestingAuthenticationToken(null, "koala", new GrantedAuthority[] {}));
|
||||
|
||||
authenticationTag.setOperation("principal");
|
||||
authenticationTag.setProperty("principal");
|
||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||
}
|
||||
|
||||
public void testOperationWhenSecurityContextIsNull() throws Exception {
|
||||
SecurityContextHolder.getContext().setAuthentication(null);
|
||||
|
||||
authenticationTag.setOperation("principal");
|
||||
authenticationTag.setProperty("principal");
|
||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||
assertEquals(null, authenticationTag.getLastMessage());
|
||||
}
|
||||
|
||||
public void testSkipsBodyIfNullOrEmptyOperation() throws Exception {
|
||||
authenticationTag.setOperation("");
|
||||
assertEquals("", authenticationTag.getOperation());
|
||||
authenticationTag.setProperty("");
|
||||
assertEquals(Tag.SKIP_BODY, authenticationTag.doStartTag());
|
||||
assertEquals(Tag.EVAL_PAGE, authenticationTag.doEndTag());
|
||||
}
|
||||
|
||||
public void testThrowsExceptionForUnrecognisedMethodPrefix() {
|
||||
Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
|
||||
true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
|
||||
public void testThrowsExceptionForUnrecognisedProperty() {
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
authenticationTag.setOperation("username");
|
||||
authenticationTag.setMethodPrefix("qrq");
|
||||
|
||||
try {
|
||||
authenticationTag.doStartTag();
|
||||
fail("Should have thrown a JspException");
|
||||
} catch (JspException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void testThrowsExceptionForUnrecognisedOperation() {
|
||||
Authentication auth = new TestingAuthenticationToken(new User("rodUserDetails", "koala", true, true, true,
|
||||
true, new GrantedAuthority[] {}), "koala", new GrantedAuthority[] {});
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
authenticationTag.setOperation("qsq");
|
||||
authenticationTag.setProperty("qsq");
|
||||
|
||||
try {
|
||||
authenticationTag.doStartTag();
|
||||
authenticationTag.doEndTag();
|
||||
fail("Should have throwns JspException");
|
||||
} catch (JspException expected) {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,18 @@
|
||||
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Home Page</h1>
|
||||
Anyone can view this page.<br><br>
|
||||
|
||||
If you're logged in, you can <a href="listAccounts.html">list accounts</a>.<br><br>
|
||||
|
||||
|
||||
Your principal object is....: <%= request.getUserPrincipal() %><br><br>
|
||||
<p>
|
||||
Anyone can view this page.
|
||||
</p>
|
||||
<p>
|
||||
If you're logged in, you can <a href="listAccounts.html">list accounts</a>.
|
||||
</p>
|
||||
<p>
|
||||
Your principal object is....: <%= request.getUserPrincipal() %>
|
||||
</p>
|
||||
|
||||
<p><a href="secure/index.jsp">Secure page</a>
|
||||
<p><a href="secure/extreme/index.jsp">Extremely secure page</a>
|
||||
<p><a href="secure/index.jsp">Secure page</a></p>
|
||||
<p><a href="secure/extreme/index.jsp">Extremely secure page</a></p>
|
||||
</body>
|
||||
</html>
|
@ -1,13 +1,34 @@
|
||||
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
|
||||
|
||||
<html>
|
||||
<body>
|
||||
<h1>Secure Page</h1>
|
||||
<p>
|
||||
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>
|
||||
<% } %>
|
||||
|
||||
or if you've authenticated this session.
|
||||
</p>
|
||||
|
||||
<sec:authorize ifAllGranted="ROLE_SUPERVISOR">
|
||||
You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br/><br/>
|
||||
</sec:authorize>
|
||||
|
||||
<h3>Properties obtained using <sec:authentication /> tag</h3>
|
||||
<table border="1">
|
||||
<tr><th>Tag</th><th>Value</th></tr>
|
||||
<tr>
|
||||
<td><sec:authentication property='name' /></td><td><sec:authentication property="name"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><sec:authentication property='principal.username' /></td><td><sec:authentication property="principal.username"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><sec:authentication property='principal.enabled' /></td><td><sec:authentication property="principal.enabled"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><sec:authentication property='principal.accountNonLocked' /></td><td><sec:authentication property="principal.accountNonLocked"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
<p><a href="../">Home</a>
|
||||
<p><a href="../j_spring_security_logout">Logout</a>
|
||||
|
Loading…
x
Reference in New Issue
Block a user