Spring Security - Annotation Based Method level Security Handler - Part2
Now lets explore the code a little deeper to get a better understanding of how Spring in a best way performs all the Task.
So from the above configuration it is quite evident that at the heart lies the CustomMethodSecurityExpressionHandler,
which incorporates a permissionEvaluator i.e. DefaultPermissionEvaluator and priviledgeEvaluator i.e.
CustomPriviledgeEvaluator The code for permissionEvaluator is:
public class DefaultPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication auth, Object arg1, Object arg2) {
// TODO Auto-generated method stub
System.out.println(" *******1) The Authentication is:"+auth+"\n"+arg1+"\n"+arg2);
return true;
}
@Override
public boolean hasPermission(Authentication auth, Serializable arg1,
String arg2, Object arg3) {
// TODO Auto-generated method stub
System.out.println(" ******* 2) The Authentication is:"+auth+":"+arg1+":"+arg2+":"+arg3);
return true;
}
and Priviledge Evaluator is:
public class CustomPriviledgeEvaluator implements PriviledgeEvaluator {
@Override
public boolean hasPriviledge(Authentication paramAuthentication,
Object paramObject1, Object paramObject2) {
// TODO Auto-generated method stubn
System.out.println("********** 1) CustomPriviledgeEvaluator:"+paramAuthentication+":"+paramObject1+":"+paramObject2);
return true;
}
@Override
public boolean hasPriviledge(Authentication paramAuthentication,
Serializable paramSerializable, String paramString,
Object paramObject) {
// TODO Auto-generated method stub
System.out.println("********* Returning TRUE \n************* 2) CustomPriviledgeEvaluator:"+paramAuthentication+":"+paramSerializable+":"+paramString+":"+paramObject);
return true;
}
Now, the CustomPriviledgeEvaluator contains the logic corresponding to our custom annotation.The code for CustomMethodSecurityExpressionHandler is:
import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.aopalliance.intercept.MethodInvocation; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.security.access.PermissionCacheOptimizer; import org.springframework.security.access.expression.AbstractSecurityExpressionHandler; import org.springframework.security.access.expression.ExpressionUtils; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import org.springframework.security.core.Authentication; import org.springframework.security.core.parameters.DefaultSecurityParameterNameDiscoverer; import org.springframework.util.Assert; public class CustomMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandlerIn CustomMethodSecurityExpressionHandler class in createEvaluationContextInternal() aimplements MethodSecurityExpressionHandler{ //protected final Log logger = LogFactory.getLog(getClass()); private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultSecurityParameterNameDiscoverer(); private PermissionCacheOptimizer permissionCacheOptimizer = null; private PriviledgeEvaluator priviledgeEvaluator;// = new CustomPriviledgeEvaluator(); public StandardEvaluationContext createEvaluationContextInternal(Authentication auth, MethodInvocation mi) { System.out.println(" *********************** In Create createEvaluationContextInternal with auth:"+auth+" Class:"+auth.getClass()+":"+mi.getClass()); return new CustomMethodSecurityEvaluationContext(auth, mi, this.parameterNameDiscoverer); } protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) { System.out.println(" ******************** In createSecurityExpressionRoot authentication:"+authentication.getClass()+":"+invocation.getClass()); CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication); root.setThis(invocation.getThis()); root.setPermissionEvaluator(getPermissionEvaluator()); root.setTrustResolver(this.trustResolver); root.setRoleHierarchy(getRoleHierarchy()); root.setPriviledgeEvaluator(getPriviledgeEvaluator()); return root; } public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) { System.out.println(" In filter:"+filterTarget.getClass()+":"+filterExpression.getValue()+":"+ctx.getRootObject()); MethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations)ctx.getRootObject().getValue(); /*boolean debug = this.logger.isDebugEnabled(); if (debug) { this.logger.debug("Filtering with expression: " + filterExpression.getExpressionString()); }*/ if ((filterTarget instanceof Collection)) { Collection collection = (Collection)filterTarget; List retainList = new ArrayList(collection.size()); /*if (debug) { this.logger.debug("Filtering collection with " + collection.size() + " elements"); }*/ if (this.permissionCacheOptimizer != null) { this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), collection); } for (Object filterObject : (Collection)filterTarget) { rootObject.setFilterObject(filterObject); if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) { retainList.add(filterObject); } } /*if (debug) { this.logger.debug("Retaining elements: " + retainList); }*/ collection.clear(); collection.addAll(retainList); return filterTarget; } if (filterTarget.getClass().isArray()) { Object[] array = (Object[])filterTarget; List retainList = new ArrayList(array.length); /* if (debug) { this.logger.debug("Filtering array with " + array.length + " elements"); }*/ if (this.permissionCacheOptimizer != null) { this.permissionCacheOptimizer.cachePermissionsFor(rootObject.getAuthentication(), Arrays.asList(array)); } for (Object o : array) { rootObject.setFilterObject(o); if (ExpressionUtils.evaluateAsBoolean(filterExpression, ctx)) { retainList.add(o); } } /*if (debug) { this.logger.debug("Retaining elements: " + retainList); }*/ Object[] filtered = (Object[])Array.newInstance(filterTarget.getClass().getComponentType(), retainList.size()); for (int i = 0; i < retainList.size(); i++) { filtered[i] = retainList.get(i); } return filtered; } throw new IllegalArgumentException("Filter target must be a collection or array type, but was " + filterTarget); } public void setTrustResolver(AuthenticationTrustResolver trustResolver) { Assert.notNull(trustResolver, "trustResolver cannot be null"); this.trustResolver = trustResolver; } public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) { this.parameterNameDiscoverer = parameterNameDiscoverer; } public void setPermissionCacheOptimizer(PermissionCacheOptimizer permissionCacheOptimizer) { this.permissionCacheOptimizer = permissionCacheOptimizer; } public void setReturnObject(Object returnObject, EvaluationContext ctx) { ((MethodSecurityExpressionOperations)ctx.getRootObject().getValue()).setReturnObject(returnObject); } public PriviledgeEvaluator getPriviledgeEvaluator() { return priviledgeEvaluator; } public void setPriviledgeEvaluator(PriviledgeEvaluator priviledgeEvaluator) { this.priviledgeEvaluator = priviledgeEvaluator; } }
CustomMethodSecurityEvaluationContext object is created which performs method level security evaluation task.
The code is:
public class CustomMethodSecurityEvaluationContext extends
StandardEvaluationContext {
private ParameterNameDiscoverer parameterNameDiscoverer;
private final MethodInvocation mi;
private boolean argumentsAdded;
public CustomMethodSecurityEvaluationContext(Authentication user, MethodInvocation mi)
{
this(user, mi, new DefaultSecurityParameterNameDiscoverer());
}
public CustomMethodSecurityEvaluationContext(Authentication user, MethodInvocation mi, ParameterNameDiscoverer parameterNameDiscoverer)
{
this.mi = mi;
System.out.println("1)********************"+this.mi.getClass()+":"+this.mi.getMethod());
this.parameterNameDiscoverer = parameterNameDiscoverer;
//System.out.println( this.parameterNameDiscoverer.getParameterNames(this.mi.getMethod()).length);
}
public Object lookupVariable(String name)
{
Object variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
if (!this.argumentsAdded)
{
addArgumentsAsVariables();
this.argumentsAdded = true;
}
variable = super.lookupVariable(name);
if (variable != null) {
return variable;
}
return null;
}
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer)
{
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
private void addArgumentsAsVariables()
{
Object[] args = this.mi.getArguments();
if (args.length == 0) {
return;
}
Object targetObject = this.mi.getThis();
Class targetClass = AopProxyUtils.ultimateTargetClass(targetObject);
if (targetClass == null) {
targetClass = targetObject.getClass();
}
Method method = AopUtils.getMostSpecificMethod(this.mi.getMethod(), targetClass);
String[] paramNames = this.parameterNameDiscoverer.getParameterNames(method);
if (paramNames == null)
{
// logger.warn("Unable to resolve method parameter names for method: " + method + ". Debug symbol information is required if you are using parameter names in expressions.");
return;
}
for (int i = 0; i < args.length; i++) {
super.setVariable(paramNames[i], args[i]);
}
}
}
In CustomMethodSecurityExpressionHandler class in createSecurityExpressionRoot() the CustomPermissionEvaluator and Custom PriviledgeEvaluator are being set in the CustomMethodSecurityExpressionRoot,
which performs the rest of the operations:
The Code for CustomMethodSecurityExpressionRoot is:
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.core.Authentication;
public class CustomMethodSecurityExpressionRoot extends CustomSecurityExpressionRoot
implements MethodSecurityExpressionOperations {
private Object filterObject;
private Object returnObject;
private Object target;
CustomMethodSecurityExpressionRoot(Authentication a)
{
super(a);
}
public void setFilterObject(Object filterObject)
{
this.filterObject = filterObject;
}
public Object getFilterObject()
{
return this.filterObject;
}
public void setReturnObject(Object returnObject)
{
this.returnObject = returnObject;
}
public Object getReturnObject()
{
return this.returnObject;
}
void setThis(Object target)
{
this.target = target;
}
public Object getThis()
{
return this.target;
}
}
The code for CustomSecurityExpressionRoot is:
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
public class CustomSecurityExpressionRoot implements
CustomSecurityExpressionOperations {
protected final Authentication authentication;
private AuthenticationTrustResolver trustResolver;
private RoleHierarchy roleHierarchy;
private Set roles;
public final boolean permitAll = true;
public final boolean denyAll = false;
private PermissionEvaluator permissionEvaluator;
public final String read = "read";
public final String write = "write";
public final String create = "create";
public final String delete = "delete";
public final String admin = "administration";
private PriviledgeEvaluator priviledgeEvaluator;
public CustomSecurityExpressionRoot(Authentication authentication)
{
if (authentication == null) {
throw new IllegalArgumentException("Authentication object cannot be null");
}
this.authentication = authentication;
}
public final boolean hasAuthority(String authority)
{
return hasRole(authority);
}
public final boolean hasAnyAuthority(String... authorities)
{
return hasAnyRole(authorities);
}
public final boolean hasRole(String role)
{
return getAuthoritySet().contains(role);
}
public final boolean hasAnyRole(String... roles)
{
Set roleSet = getAuthoritySet();
for (String role : roles) {
if (roleSet.contains(role)) {
return true;
}
}
return false;
}
public final Authentication getAuthentication()
{
return this.authentication;
}
public final boolean permitAll()
{
return true;
}
public final boolean denyAll()
{
return false;
}
public final boolean isAnonymous()
{
return this.trustResolver.isAnonymous(this.authentication);
}
public final boolean isAuthenticated()
{
return !isAnonymous();
}
public final boolean isRememberMe()
{
return this.trustResolver.isRememberMe(this.authentication);
}
public final boolean isFullyAuthenticated()
{
return (!this.trustResolver.isAnonymous(this.authentication)) && (!this.trustResolver.isRememberMe(this.authentication));
}
public Object getPrincipal()
{
return this.authentication.getPrincipal();
}
public void setTrustResolver(AuthenticationTrustResolver trustResolver)
{
this.trustResolver = trustResolver;
}
public void setRoleHierarchy(RoleHierarchy roleHierarchy)
{
this.roleHierarchy = roleHierarchy;
}
private Set getAuthoritySet()
{
if (this.roles == null)
{
this.roles = new HashSet();
Collection userAuthorities = this.authentication.getAuthorities();
if (this.roleHierarchy != null) {
userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
}
this.roles = AuthorityUtils.authorityListToSet(userAuthorities);
}
return this.roles;
}
public boolean hasPermission(Object target, Object permission)
{
return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
}
public boolean hasPermission(Object targetId, String targetType, Object permission)
{
return this.permissionEvaluator.hasPermission(this.authentication, (Serializable)targetId, targetType, permission);
}
public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator)
{
this.permissionEvaluator = permissionEvaluator;
}
public void setPriviledgeEvaluator(PriviledgeEvaluator priviledgeEvaluator) {
this.priviledgeEvaluator = priviledgeEvaluator;
}
@Override
public boolean hasPriviledge(Object paramObject1, Object paramObject2) {
// TODO Auto-generated method stubn
System.out.println(" ************* In Has priviledge 1");
return this.priviledgeEvaluator.hasPriviledge(this.authentication, paramObject1, paramObject2);
}
@Override
public boolean hasPriviledge(Object paramObject1, String paramString,
Object paramObject2) {
System.out.println(" ************** In Has priviledge 2");
return this.priviledgeEvaluator.hasPriviledge(this.authentication, (Serializable)paramObject1, paramString, paramObject2);
// TODO Auto-generated method stub
}
}
& the code for CustomSecurityExpressionOperations is:
import org.springframework.security.access.expression.SecurityExpressionOperations;
public interface CustomSecurityExpressionOperations extends SecurityExpressionOperations {
public abstract boolean hasPriviledge(Object paramObject1, Object paramObject2);
public abstract boolean hasPriviledge(Object paramObject1, String paramString, Object paramObject2);
}
This is in short the Method level Security Implementation Architecture in Spring.
Any Queries and Suggestions are always welcome.
& Happy Coding till Next Time.
Comments
Post a Comment