栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Security After Invocation Handling

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Security After Invocation Handling

简介

基于spring-security-4.1.0.RELEASE和spring-security-oauth2-2.3.5.RELEASE

官方文档关于After Invocation Handling介绍大致的意思是说 有些应用程序需要一种修改secure object调用实际返回的对象的方法。我们自己可以用 AOP来实现这一点,但Spring Security 官方给咱们提供了一个方便的hook,意思就是我们可以用它提供的方式来处理来达到我们想要的效果。具体介绍可参考官方文档

官方给了一个具体的实现关系图:

上面的AclEntryAfterInvocationProvider的具体实现在spring-security-acl模块中也是域对象安全的相关内容,这里我们没有用到。

AfterInvocationManager的定义

After Invocation Handling是通过AfterInvocationManager来完成的。我这里通过IDE查找了下AfterInvocationManager对象在哪里定义过,一个在AbstractSecurityInterceptor中,另外一处是GlobalMethodSecurityConfiguration类中,我们都知道FilterSecurityInterceptor继承了AbstractSecurityInterceptor,FilterSecurityInterceptor是用来对HTTP 资源进行安全处理的,他的invoke方法中包含了Pre-Invocation Handling和After Invocation Handling处理流程。分别对应super.beforeInvocation和super.afterInvocation两个方法,但实际上super.afterInvocation并没有进行任何处理,因为AfterInvocationManager对象为null,我也没有找到设置AfterInvocationManager对象的地方在相关的配置中,所以暂时理解为在FilterSecurityInterceptor中AfterInvocationManager无用,虽然它调用了super.afterInvocation方法。
另外一处在GlobalMethodSecurityConfiguration,它是一个全局方法安全配置基于注解方式的。你可以通过注解@EnableGlobalMethodSecurity在任何@Configuration实例上使用来启用此功能,例如:

@EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}

既然GlobalMethodSecurityConfiguration用到了AfterInvocationManager那我们就来看看它是怎么来使用AfterInvocationManager的以及AfterInvocationManager是怎么样的一个处理流程,用它来了解整个After Invocation Handling的处理流程。

全局方法安全配置

你可以使用注解@EnableGlobalMethodSecurity也可以在spring 配置文件中使用“”标签,这里注意下,有可能你在使用注解时有不生效的问题,有可能是因为你注解添加的位置不对,你需要放在Spring MVC配置所在的相同配置类或xml文件中,这样才能检测到你控制器的bean,从而进行拦截操作。
我这里使用的在xml文件配置的方式,你需要在xml中加入Spring Security的namespace。



	
	
	......

在指定的Controller接口中添加注解@PostAuthorize(“hasRole(‘admin’)”),关于@EnableGlobalMethodSecurity以及注解@PostAuthorize的具体用法请参考官方文档。

@Controller
@RequestMapping("test")
public class TestContoller {

	@RequestMapping("getUserByID")
	@ResponseBody
	@PostAuthorize("hasRole('admin')")
	public User getUserByID(int id) {
		// .....	
	}
}

可以通过@EnableGlobalMethodSecurity或者GlobalMethodSecurityBeanDefinitionParser基于“”标签的方式来了解这个配置过程,我这里用的EnableGlobalMethodSecurity因为它调试时更加直观些。首先动态确定要包含哪些导入,相关实现在GlobalMethodSecuritySelector类中,根据现在的配置,需要MethodSecuritymetadataSourceAdvisorRegistrar、AutoProxyRegistrar、GlobalMethodSecurityConfiguration。然后在GlobalMethodSecurityConfiguration有一个bean的加载用来创建默认的 MethodInterceptor,它是一个 MethodSecurityInterceptor,在这个MethodSecurityInterceptor中设置了AfterInvocationManager属性,在被标注@PostAuthorize注解的接口访问时会被动态代理执行MethodSecurityInterceptor#invoke方法,先Pre-Invocation Handling处理也就是super.beforeInvocation(mi),在调用getUserByID(int id)对应mi.proceed()方法的调用,在 After Invocation Handling处理对应super.afterInvocation(token, result)方法的执行。

@Bean
public MethodInterceptor methodSecurityInterceptor() throws Exception {
	MethodSecurityInterceptor methodSecurityInterceptor = isAspectJ() ? new AspectJMethodSecurityInterceptor()
			: new MethodSecurityInterceptor();
	methodSecurityInterceptor.setAccessDecisionManager(accessDecisionManager());
	methodSecurityInterceptor.setAfterInvocationManager(afterInvocationManager());
	methodSecurityInterceptor.setAuthenticationManager(authenticationManager());
	methodSecurityInterceptor
			.setSecuritymetadataSource(methodSecuritymetadataSource());
	RunAsManager runAsManager = runAsManager();
	if (runAsManager != null) {
		methodSecurityInterceptor.setRunAsManager(runAsManager);
	}
	return methodSecurityInterceptor;
}
public Object invoke(MethodInvocation mi) throws Throwable {
	InterceptorStatusToken token = super.beforeInvocation(mi);

	Object result;
	try {
		result = mi.proceed();
	}
	finally {
		super.finallyInvocation(token);
	}
	return super.afterInvocation(token, result);
}
修改接口返回数据

因为我们这里标注的是@PostAuthorize(“hasRole(‘admin’)”)在接口的逻辑执行完,会获取授权列表中是否存在ROOT_admin这个角色,在方法验证前,先把接口返回的数据设置到MethodSecurityexpressionRoot的returnObject属性中然后判断是否存在,不存在则抛出访问拒绝的异常AccessDeniedException,如果存在则把接口返回值返回,这里并没有对返回值进行处理,直接正常执行下面的逻辑了。
这里就存在一个问题一般我们验证权限时是在方法执行前验证,所以一般会用@PreAuthorize而不会用@PostAuthorize,也就是说在super.beforeInvocation(mi)方法执行时如果不包含指定的权限直接就会中断,而不会在往下面执行。所以官方也对@PreAuthorize解释说这种用法不常见。
如果你想修改返回值的内容,你可以自定义AfterInvocationProvider实现,然后Spring 会把你的实现追加到AfterInvocationProviderManager的providers属性列表中,在默认Spring的PostInvocationAdviceProvider处理完,会把返回值传入下一个AfterInvocationProviderManager的实现类中,也就是我们自定义的AfterInvocationProvider实现,我这里定义为CustomPostInvocationAdviceProvider。

相关代码:



	
	
		
	
	
	
	......

public class CustomPostInvocationAdviceProvider implements AfterInvocationProvider {

	public CustomPostInvocationAdviceProvider() {
	}

	@Override
	public Object decide(Authentication authentication, Object object, Collection config,
			Object returnedObject) throws AccessDeniedException {
		Object result = returnedObject;
		// TODO 你自己的逻辑实现
		return result;
	}

	public boolean supports(ConfigAttribute attribute) {
		return attribute instanceof PostInvocationAttribute;
	}

	public boolean supports(Class clazz) {
		return clazz.isAssignableFrom(MethodInvocation.class);
	}
}

这样你就可以根据自己的需求进行相关逻辑的处理。实际上掌握他们之间的关系就更加容易理解,其实就是AfterInvocationProviderManager中有一个或多个AfterInvocationProvider,然后循环这些AfterInvocationProvider执行它们的decide方法。After Invocation Handling看官方的介绍多用于Method Security上,基于注解的配置,例如我们上面提到的@PreAuthorize和@PostAuthorize还有@Secured等等,具体操作还是看具体业务。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/315162.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号