亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

spring security動態配置url權限的2種實現方法

2024-07-14 08:41:12
字體:
來源:轉載
供稿:網友

緣起

標準的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實現一個:

public class RoleBasedVoter implements AccessDecisionVoter<Object> { @Override public boolean supports(ConfigAttribute attribute) { return true; } @Override public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) { if(authentication == null) { return ACCESS_DENIED; } int result = ACCESS_ABSTAIN; Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication); for (ConfigAttribute attribute : attributes) { if(attribute.getAttribute()==null){ continue; } if (this.supports(attribute)) { result = ACCESS_DENIED; // Attempt to find a matching granted authority for (GrantedAuthority authority : authorities) {  if (attribute.getAttribute().equals(authority.getAuthority())) {  return ACCESS_GRANTED;  } } } } return result; } Collection<? extends GrantedAuthority> extractAuthorities( Authentication authentication) { return authentication.getAuthorities(); } @Override public boolean supports(Class clazz) { return true; }}

如何加入動態權限呢?

vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)里的Object object的類型是FilterInvocation,可以通過getRequestUrl獲取當前請求的URL:

 FilterInvocation fi = (FilterInvocation) object; String url = fi.getRequestUrl();

因此這里擴展空間就大了,可以從DB動態加載,然后判斷URL的ConfigAttribute就可以了。

如何使用這個RoleBasedVoter呢?在configure里使用accessDecisionManager方法自定義,我們還是使用官方的UnanimousBased,然后將自定義的RoleBasedVoter加入即可。

@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) .exceptionHandling() .authenticationEntryPoint(problemSupport) .accessDeniedHandler(problemSupport) .and() .csrf() .disable() .headers() .frameOptions() .disable() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() // 自定義accessDecisionManager .accessDecisionManager(accessDecisionManager())  .and() .apply(securityConfigurerAdapter()); } @Bean public AccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<? extends Object>> decisionVoters = Arrays.asList( new WebExpressionVoter(), // new RoleVoter(), new RoleBasedVoter(), new AuthenticatedVoter()); return new UnanimousBased(decisionVoters); }

自定義SecurityMetadataSource

自定義FilterInvocationSecurityMetadataSource只要實現接口即可,在接口里從DB動態加載規則。

為了復用代碼里的定義,我們可以將代碼里生成的SecurityMetadataSource帶上,在構造函數里傳入默認的FilterInvocationSecurityMetadataSource。

public class AppFilterInvocationSecurityMetadataSource implements org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource { private FilterInvocationSecurityMetadataSource superMetadataSource; @Override public Collection<ConfigAttribute> getAllConfigAttributes() { return null; } public AppFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource expressionBasedFilterInvocationSecurityMetadataSource){  this.superMetadataSource = expressionBasedFilterInvocationSecurityMetadataSource;  // TODO 從數據庫加載權限配置 } private final AntPathMatcher antPathMatcher = new AntPathMatcher();  // 這里的需要從DB加載 private final Map<String,String> urlRoleMap = new HashMap<String,String>(){{ put("/open/**","ROLE_ANONYMOUS"); put("/health","ROLE_ANONYMOUS"); put("/restart","ROLE_ADMIN"); put("/demo","ROLE_USER"); }}; @Override public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { FilterInvocation fi = (FilterInvocation) object; String url = fi.getRequestUrl(); for(Map.Entry<String,String> entry:urlRoleMap.entrySet()){  if(antPathMatcher.match(entry.getKey(),url)){  return SecurityConfig.createList(entry.getValue());  } } // 返回代碼定義的默認配置 return superMetadataSource.getAttributes(object); } @Override public boolean supports(Class<?> clazz) { return FilterInvocation.class.isAssignableFrom(clazz); }}

怎么使用?和accessDecisionManager不一樣,ExpressionUrlAuthorizationConfigurer 并沒有提供set方法設置FilterSecurityInterceptor的FilterInvocationSecurityMetadataSource,how to do?

發現一個擴展方法withObjectPostProcessor,通過該方法自定義一個處理FilterSecurityInterceptor類型的ObjectPostProcessor就可以修改FilterSecurityInterceptor。

@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http  .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class)  .exceptionHandling()  .authenticationEntryPoint(problemSupport)  .accessDeniedHandler(problemSupport) .and()  .csrf()  .disable()  .headers()  .frameOptions()  .disable() .and()  .sessionManagement()  .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and()  .authorizeRequests()  // 自定義FilterInvocationSecurityMetadataSource  .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {  @Override  public <O extends FilterSecurityInterceptor> O postProcess(   O fsi) {   fsi.setSecurityMetadataSource(mySecurityMetadataSource(fsi.getSecurityMetadataSource()));   return fsi;  }  }) .and()  .apply(securityConfigurerAdapter()); } @Bean public AppFilterInvocationSecurityMetadataSource mySecurityMetadataSource(FilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource) { AppFilterInvocationSecurityMetadataSource securityMetadataSource = new AppFilterInvocationSecurityMetadataSource(filterInvocationSecurityMetadataSource); return securityMetadataSource;}

小結

本文介紹了兩種基于spring security實現動態權限的方法,一是自定義accessDecisionManager,二是自定義FilterInvocationSecurityMetadataSource。實際項目里可以根據需要靈活選擇。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VeVb武林網的支持。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美乱人伦中文字幕在线| 久久久久免费视频| 91在线免费看网站| 亚洲欧美国产精品| 国产精品久久国产精品99gif| 精品国产91乱高清在线观看| 久久久久久综合网天天| 国产一区二区丝袜高跟鞋图片| 国产亚洲精品久久久久久777| 精品国产一区二区三区在线观看| 色噜噜狠狠狠综合曰曰曰| 欧美激情在线观看| 91社区国产高清| 亚洲国产古装精品网站| 97在线视频免费观看| 国产精品自拍网| 中文字幕国产日韩| 亚洲高清福利视频| 自拍偷拍亚洲区| 欧美极品xxxx| 国产精品专区h在线观看| 国自产精品手机在线观看视频| 欧美激情视频免费观看| 日韩电影在线观看中文字幕| 色婷婷综合成人av| 国产极品精品在线观看| 国产视频久久久| 久久久国产91| 欧美性理论片在线观看片免费| 国内精品小视频在线观看| 日本一区二区三区四区视频| 国产主播欧美精品| 欧美日韩亚洲视频一区| 97人人爽人人喊人人模波多| 中文字幕日韩av综合精品| 日韩欧美极品在线观看| 亚洲欧洲中文天堂| 国产成人精品国内自产拍免费看| 国产成人免费av| 欧美中文字幕在线播放| 91精品国产91久久久久久最新| 国产精品免费久久久| 欧美天堂在线观看| 亚洲成人免费网站| 日本欧美中文字幕| 久久精品视频免费播放| 一区二区福利视频| 亚洲精品久久久久中文字幕二区| 国产69精品99久久久久久宅男| 精品国产999| 欧美激情视频一区二区| 在线观看久久av| 精品亚洲国产视频| 亚洲精品国产品国语在线| 国产精品亚洲激情| 午夜精品一区二区三区在线| 日韩色av导航| 亚洲伊人久久大香线蕉av| xvideos亚洲人网站| 色久欧美在线视频观看| 国产精品美乳一区二区免费| 亚洲xxxx妇黄裸体| 亚洲高清色综合| 最近2019中文免费高清视频观看www99| 久久这里有精品视频| 成人免费网站在线| 欧美成人精品激情在线观看| 日产精品99久久久久久| 久久久久久久一区二区| 欧美成人精品三级在线观看| 国产欧美一区二区| 欧美人在线观看| 亚洲欧美精品伊人久久| 国内精品国产三级国产在线专| 久久天天躁狠狠躁夜夜av| 欧美日韩午夜视频在线观看| 亚洲精品综合精品自拍| 久久艳片www.17c.com| 欧美日韩在线视频首页| 91av视频在线免费观看| 1769国产精品| 狠狠色狠色综合曰曰| 亚洲国产欧美久久| 欧美日韩亚洲成人| 欧美片一区二区三区| 亚洲成人av片| 亚洲天堂视频在线观看| 最新中文字幕亚洲| 国产美女直播视频一区| 亚洲精品视频久久| 国产在线播放不卡| 91精品久久久久久久久久| 欧美重口另类videos人妖| 国产xxx69麻豆国语对白| 欧美激情第6页| 久久久久久国产精品久久| 九九热精品视频在线播放| 欧美一区二区三区艳史| 国产精品久久久久久久久粉嫩av| 亚洲国产美女精品久久久久∴| 日韩中文字幕网站| 亚洲视频专区在线| 高潮白浆女日韩av免费看| 欧美成人久久久| 色综合五月天导航| 欧美高清视频在线播放| 亚洲自拍偷拍在线| 亚洲成人亚洲激情| 久久青草精品视频免费观看| 中文字幕久热精品视频在线| 亚洲欧洲xxxx| 国产日韩在线精品av| 欧美区二区三区| 日韩精品电影网| 亚洲欧洲在线视频| 91亚洲精品一区| 欧美电影电视剧在线观看| 2019中文字幕在线| 久久久伊人欧美| 成人国产在线视频| 91精品国产综合久久久久久久久| 日韩中文在线视频| 亚洲品质视频自拍网| 欧美一区亚洲一区| 精品国产依人香蕉在线精品| 日韩在线免费观看视频| 欧美风情在线观看| 久久频这里精品99香蕉| 国产精品日韩专区| 精品久久久香蕉免费精品视频| 在线视频一区二区| 亚洲伊人成综合成人网| 欧美日韩在线第一页| 国产精品18久久久久久麻辣| 欧美日韩国内自拍| 热re91久久精品国99热蜜臀| 欧美亚洲一区在线| 亚洲欧美激情视频| 亚洲精品自在久久| 欧美黑人性猛交| 久久躁狠狠躁夜夜爽| 亚洲欧美在线免费观看| 日韩一区av在线| 丁香五六月婷婷久久激情| 国产主播精品在线| 成人日韩av在线| 亚洲福利视频专区| 亚洲精品国产suv| 国产精品久久久久aaaa九色| 91地址最新发布| 国产精品白嫩美女在线观看| 欧美日韩加勒比精品一区| 欧美成人午夜激情在线| 日韩美女在线观看| 亚洲午夜性刺激影院| 欧美成人激情在线| 国产精品都在这里| 亚洲欧美激情视频| 91成人精品网站| 97视频在线观看免费高清完整版在线观看| 亚洲精品中文字幕av| 亚洲精品久久久久久下一站| 青青草原一区二区| 亚洲综合在线中文字幕|