Spring Security 实现 antMatchers 配置路径的动态获取

本文探讨了如何在Spring Security中实现在不硬编码的情况下动态获取antMatchers等配置数据,以适应API网关统一处理权限验证的需求。通过分析Spring Security框架,尤其是FilterSecurityInterceptor的工作原理,找到了替换SecurityMetadataSource的切入点,并详细介绍了实现过程,包括自定义SecurityMetadataSource和利用反射调用private函数。最后,提供了一个简单的配置代码示例和相关依赖。
摘要由CSDN通过智能技术生成

1. 为什么要实现动态的获取 antMatchers 配置的数据  

        这两天由于公司项目的需求,对 spring security 的应用过程中需要实现动态的获取 antMatchers ,permitAll , hasAnyRole , hasIpAddress 等这些原本通过硬编码的方式配置的数据。为了让每一个业务服务不用再去处理权限验证等这些和业务无关的逻辑,而是只专注于它所负责的业务,就要将认证、授权统一的放在 API 网关层去处理。但是每个不同的业务服务有的接口需要认证后才能访问,有的接口是不需要认证就可以访问的,有的接口可能是需要某些权限、角色才可以访问。这样依赖 API 网关就必须知道并且能够区分出来每个业务服务的接口哪些是需要认证后才可以访问的,那些接口是不需要经过认证就可以访问的。 为了实现这个功能 spring security 提供的 antMatchers 函数硬编码的方式就不适用了。而是应该提供一个管理端,每个业务服务把他们这些个性化的接口通过管理端去进行配置,统一的存储起来,spring security 在获取这些数据的时候从统一的存储中来获取这些数据。基于这个需求前提我来考虑如何实现这个功能。配套视频讲解地址 :http://www.iqiyi.com/w_19s456x5b5.html?pltfm=11&pos=title&flashvars=videoIsFromQidan%3Ditemviewclk_a#vfrm=5-6-0-1

2.  从 Spring Security 框架中找到适合实现该功能的切入点

        想要找个框架的切入点必须对框架如何工作,源码要熟悉,不然很难找到一个合适的切入点。有点见缝插针的意思,首先就需要找到一个适合“插针”的位置。

2.1 FilterSecurityInterceptor

        FilterSecurityInterceptor 过滤器是 Spring Security 过滤器链条中的最后一个过滤器,它的任务是来最终决定一个请求是否可以被允许访问。

org.springframework.security.web.access.intercept.FilterSecurityInterceptor#invoke 函数源码:这个函数中做了调用下一个过滤器的操作,也就是这行代码 fi.getChain().doFilter(fi.getRequest(), fi.getResponse()) 。因为 FilterSecurityInterceptor 是Security 过滤器链条中的最后一个过滤器,再去调用下一个过滤器就是调用原始过滤器链条中的下一个过滤器了,这也就意味着请求是被允许访问的。但是在调用下一个过滤器之前还有一行代码 ,InterceptorStatusToken token = super.beforeInvocation(fi); 这一行代码就会决定本次请求是否会被放行。

public void invoke(FilterInvocation fi) throws IOException, ServletException {
		if ((fi.getRequest() != null)
				&& (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
				&& observeOncePerRequest) {
			// filter already applied to this request and user wants us to observe
			// once-per-request handling, so don't re-do security checking
			fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
		}
		else {
			// first time this request being called, so perform security checking
			if (fi.getRequest() != null) {
				fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
			}

			InterceptorStatusToken token = super.beforeInvocation(fi);

			try {
				fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
			}
			finally {
				super.finallyInvocation(token);
			}

			super.afterInvocation(token, null);
		}
	}

 

        org.springframework.security.access.intercept.AbstractSecurityInterceptor#beforeInvocation 函数源码:这个函数做的事情大致是对这次请求是禁止访问还是允许访问进行投票,如果投票都通过的话就允许访问,如果有一票反对就会禁止访问抛出异常结束后续处理流程。投票的依据就是通过这行代码 

Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object); 获取到的。这行代码也就是我实现功能的切入点。它先获取了一个 SecurityMetadataSource 对象,然后通过这个对象获取了投票的依据。 我的思路就是自定义 SecurityMetadataSource 类的子类,来替换掉 FilterSecurityInterceptor 中的 SecurityMetadataSource 实例。

protected InterceptorStatusToken beforeInvocation(Object object) {
		Assert.notNull(object, "Object was null");
		final boolean debug = logger.isDebugEnabled();

		if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
			throw new IllegalArgumentException(
					"Security invocation attempted for object "
							+ object.getClass().getName()
							+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
							+ getSecureObjectClass());
		}

		Collection<ConfigAttribute> attributes =
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值