亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

spring security動態(tài)配置url權(quán)限的2種實(shí)現(xiàn)方法

 更新時間:2018年06月08日 09:47:08   作者:JadePeng  
對于使用spring security來說,存在一種需求,就是動態(tài)去配置url的權(quán)限,即在運(yùn)行時去配置url對應(yīng)的訪問角色。下面這篇文章主要給大家介紹了關(guān)于spring security動態(tài)配置url權(quán)限的2種實(shí)現(xiàn)方法,需要的朋友可以參考下

緣起

標(biāo)準(zhǔn)的RABC, 權(quán)限需要支持動態(tài)配置,spring security默認(rèn)是在代碼里約定好權(quán)限,真實(shí)的業(yè)務(wù)場景通常需要可以支持動態(tài)配置角色訪問權(quán)限,即在運(yùn)行時去配置url對應(yīng)的訪問角色。

基于spring security,如何實(shí)現(xiàn)這個需求呢?

最簡單的方法就是自定義一個Filter去完成權(quán)限判斷,但這脫離了spring security框架,如何基于spring security優(yōu)雅的實(shí)現(xiàn)呢?

spring security 授權(quán)回顧

spring security 通過FilterChainProxy作為注冊到web的filter,F(xiàn)ilterChainProxy里面一次包含了內(nèi)置的多個過濾器,我們首先需要了解spring security內(nèi)置的各種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,該過濾器實(shí)現(xiàn)了主要的鑒權(quán)邏輯,最核心的代碼在這里:

protected InterceptorStatusToken beforeInvocation(Object object) { 
 // 獲取訪問URL所需權(quán)限
 Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
 .getAttributes(object);

 
 Authentication authenticated = authenticateIfRequired();

 // 通過accessDecisionManager鑒權(quán)
 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);
 }
 }

從上面可以看出,要實(shí)現(xiàn)動態(tài)鑒權(quán),可以從兩方面著手:

  • 自定義SecurityMetadataSource,實(shí)現(xiàn)從數(shù)據(jù)庫加載ConfigAttribute
  • 另外就是可以自定義accessDecisionManager,官方的UnanimousBased其實(shí)足夠使用,并且他是基于AccessDecisionVoter來實(shí)現(xiàn)權(quán)限認(rèn)證的,因此我們只需要自定義一個AccessDecisionVoter就可以了

下面來看分別如何實(shí)現(xiàn)。

自定義AccessDecisionManager

官方的三個AccessDecisionManager都是基于AccessDecisionVoter來實(shí)現(xiàn)權(quán)限認(rèn)證的,因此我們只需要自定義一個AccessDecisionVoter就可以了。

自定義主要是實(shí)現(xiàn)AccessDecisionVoter接口,我們可以仿照官方的RoleVoter實(shí)現(xiàn)一個:

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;
 }
}

如何加入動態(tài)權(quán)限呢?

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

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

因此這里擴(kuò)展空間就大了,可以從DB動態(tài)加載,然后判斷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只要實(shí)現(xiàn)接口即可,在接口里從DB動態(tài)加載規(guī)則。

為了復(fù)用代碼里的定義,我們可以將代碼里生成的SecurityMetadataSource帶上,在構(gòu)造函數(shù)里傳入默認(rèn)的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 從數(shù)據(jù)庫加載權(quán)限配置
 }

 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());
  }
 }
 // 返回代碼定義的默認(rèn)配置
 return superMetadataSource.getAttributes(object);
 }

 @Override
 public boolean supports(Class<?> clazz) {
 return FilterInvocation.class.isAssignableFrom(clazz);
 }
}

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

發(fā)現(xiàn)一個擴(kuò)展方法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;
}

小結(jié)

本文介紹了兩種基于spring security實(shí)現(xiàn)動態(tài)權(quán)限的方法,一是自定義accessDecisionManager,二是自定義FilterInvocationSecurityMetadataSource。實(shí)際項(xiàng)目里可以根據(jù)需要靈活選擇。

延伸閱讀:

Spring Security 架構(gòu)與源碼分析

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關(guān)文章

  • mybatis如何批量修改數(shù)據(jù)

    mybatis如何批量修改數(shù)據(jù)

    這篇文章主要介紹了mybatis如何批量修改數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例(保留換行)

    在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例(保留換行)

    這篇文章主要介紹了在Java中輕松將HTML格式文本轉(zhuǎn)換為純文本的方法示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Java流程控制語句最全匯總(下篇)

    Java流程控制語句最全匯總(下篇)

    這篇文章主要介紹了Java流程控制語句最全匯總(下篇),本文章內(nèi)容詳細(xì),通過案例可以更好的理解數(shù)組的相關(guān)知識,本模塊分為了三部分,本次為下篇,需要的朋友可以參考下
    2023-01-01
  • 詳解ZXing-core生成二維碼的方法并解析

    詳解ZXing-core生成二維碼的方法并解析

    本文給大家介紹ZXing-core生成二維碼的方法并解析,主要用到goggle發(fā)布的jar來實(shí)現(xiàn)二維碼功能,對此文感興趣的朋友一起學(xué)習(xí)吧
    2016-05-05
  • springcloud中Feign超時提示Read timed out executing POST的問題及解決方法

    springcloud中Feign超時提示Read timed out executing

    Feign接口調(diào)用分兩層,Ribbon的調(diào)用和Hystrix調(diào)用,理論上設(shè)置Ribbon的時間即可,但是Ribbon的超時時間和Hystrix的超時時間需要結(jié)合起來,這篇文章給大家介紹springcloud之Feign超時提示Read timed out executing POST問題及解決方法,感興趣的朋友一起看看吧
    2024-01-01
  • Spring中的PathVariable注釋解析

    Spring中的PathVariable注釋解析

    這篇文章主要介紹了Spring中的PathVariable注釋用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • 一篇文章帶你深入了解Java線程池

    一篇文章帶你深入了解Java線程池

    這篇文章主要介紹了Java 線程池的相關(guān)資料,文中講解非常細(xì)致,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下,希望能給你帶來幫助
    2021-08-08
  • SpringCloud Netflix Ribbon源碼解析(推薦)

    SpringCloud Netflix Ribbon源碼解析(推薦)

    這篇文章主要介紹了SpringCloud Netflix Ribbon源碼解析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Java獲取電腦真實(shí)IP地址的示例代碼

    Java獲取電腦真實(shí)IP地址的示例代碼

    這篇文章主要介紹了Java如何獲取電腦真實(shí)IP地址,忽略虛擬機(jī)等IP地址的干擾,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-09-09
  • 基于javascript實(shí)現(xiàn)獲取最短路徑算法代碼實(shí)例

    基于javascript實(shí)現(xiàn)獲取最短路徑算法代碼實(shí)例

    這篇文章主要介紹了基于javascript實(shí)現(xiàn)獲取最短路徑算法代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02

最新評論