spring security实现动态配置url权限的两种方法

标准的RABC, 权限需要支持动态配置,spring security默认是在代码里约定好权限,真实的业务场景通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色。

基于spring security,如何实现这个需求呢?

最简单的方法就是自定义一个Filter去完成权限判断,但这脱离了spring security框架,如何基于spring security优雅的实现呢?

spring security 授权回顾

spring security 通过FilterChainProxy作为注册到web的filter,FilterChainProxy里面一次包含了内置的多个过滤器,我们首先需要了解spring security内置的各种filter:

Alias Filter Class Namespace Element or Attribute
CHANNEL_FILTER   ChannelProcessingFilter   http/intercept-url@requires-channel  
SECURITY_CONTEXT_FILTER   SecurityContextPersistenceFilter   http  
CONCURRENT_SESSION_FILTER   ConcurrentSessionFilter   session-management/concurrency-control  
HEADERS_FILTER   HeaderWriterFilter   http/headers  
CSRF_FILTER   CsrfFilter   http/csrf  
LOGOUT_FILTER   LogoutFilter   http/logout  
X509_FILTER   X509AuthenticationFilter   http/x509  
PRE_AUTH_FILTER   AbstractPreAuthenticatedProcessingFilter Subclasses   N/A  
CAS_FILTER   CasAuthenticationFilter   N/A  
FORM_LOGIN_FILTER   UsernamePasswordAuthenticationFilter   http/form-login  
BASIC_AUTH_FILTER   BasicAuthenticationFilter   http/http-basic  
SERVLET_API_SUPPORT_FILTER   SecurityContextHolderAwareRequestFilter   http/@servlet-api-provision  
JAAS_API_SUPPORT_FILTER   JaasApiIntegrationFilter   http/@jaas-api-provision  
REMEMBER_ME_FILTER   RememberMeAuthenticationFilter   http/remember-me  
ANONYMOUS_FILTER   AnonymousAuthenticationFilter   http/anonymous  
SESSION_MANAGEMENT_FILTER   SessionManagementFilter   session-management  
EXCEPTION_TRANSLATION_FILTER   ExceptionTranslationFilter   http  
FILTER_SECURITY_INTERCEPTOR   FilterSecurityInterceptor   http  
SWITCH_USER_FILTER   SwitchUserFilter   N/A  

最重要的是FilterSecurityInterceptor,该过滤器实现了主要的鉴权逻辑,最核心的代码在这里:

protected InterceptorStatusToken beforeInvocation(Object object) { // 获取访问URL所需权限 Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource() .getAttributes(object); Authentication authenticated = authenticateIfRequired(); // 通过accessDecisionManager鉴权 try { this.accessDecisionManager.decide(authenticated, object, attributes); } catch (AccessDeniedException accessDeniedException) { publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, accessDeniedException)); throw accessDeniedException; } if (debug) { logger.debug("Authorization successful"); } if (publishAuthorizationSuccess) { publishEvent(new AuthorizedEvent(object, attributes, authenticated)); } // Attempt to run as a different user Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes); if (runAs == null) { if (debug) { logger.debug("RunAsManager did not change Authentication object"); } // no further work post-invocation return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object); } else { if (debug) { logger.debug("Switching to RunAs Authentication: " + runAs); } SecurityContext origCtx = SecurityContextHolder.getContext(); SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext()); SecurityContextHolder.getContext().setAuthentication(runAs); // need to revert to token.Authenticated post-invocation return new InterceptorStatusToken(origCtx, true, attributes, object); } }

从上面可以看出,要实现动态鉴权,可以从两方面着手:

自定义SecurityMetadataSource,实现从数据库加载ConfigAttribute

另外就是可以自定义accessDecisionManager,官方的UnanimousBased其实足够使用,并且他是基于AccessDecisionVoter来实现权限认证的,因此我们只需要自定义一个AccessDecisionVoter就可以了

下面来看分别如何实现。

自定义AccessDecisionManager

官方的三个AccessDecisionManager都是基于AccessDecisionVoter来实现权限认证的,因此我们只需要自定义一个AccessDecisionVoter就可以了。

自定义主要是实现AccessDecisionVoter接口,我们可以仿照官方的RoleVoter实现一个:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgszz.html