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 Custom
PermissionEvaluator 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& the code for CustomSecurityExpressionOperations is: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 } }
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