一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - spring security動態配置url權限的2種實現方法

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

2021-05-08 10:41JadePeng Java教程

對于使用spring security來說,存在一種需求,就是動態去配置url的權限,即在運行時去配置url對應的訪問角色。下面這篇文章主要給大家介紹了關于spring security動態配置url權限的2種實現方法,需要的朋友可以參考下

緣起

標準的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,該過濾器實現了主要的鑒權邏輯,最核心的代碼在這里:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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實現一個:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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:

?
1
2
filterinvocation fi = (filterinvocation) object;
string url = fi.getrequesturl();

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@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。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@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。實際項目里可以根據需要靈活選擇。

延伸閱讀:

spring security 架構與源碼分析

總結

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

原文鏈接:http://www.cnblogs.com/xiaoqi/p/spring-security-rabc.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 女医学护士一级毛片 | 国内精品视频九九九九 | 男人天堂网av | 贵妇的私人性俱乐部 | 千金肉奴隶在线观看 | 视频一区久久 | 无码人妻少妇色欲AV一区二区 | 成人福利 | 久久精品18 | 久久国产精品免费网站 | 性满足久久久久久久久 | 男人操女人视频 | 色老板在线免费视频 | 国产探花在线观看 | 91粉色视频在线导航 | 咪咪爱991 | 欧美亚洲国产精品久久第一页 | 闺蜜的样子小说安沁在线阅读 | 草草在线影院 | 把女的下面扒开添视频 | 日本www色视频成人免费 | 亚洲精品一区二区三区在线看 | 日本搜子同屋的日子2国语 日本爽p大片免费观看 | 久久婷婷丁香五月色综合啪免费 | 涩色网| 3d蒂法精品啪啪一区二区免费 | 高清在线观看免费 | 亚洲国产日韩成人综合天堂 | 欧美艳星kagneyiynn| 久久青青草视频在线观 | 日韩一区二区三区在线 | 欧美同性gayvidoes | 久久99视热频国只有精品 | 婷婷久久热99在线精品 | 果冻传媒在线视频观看免费 | 99久久国产视频 | 亚洲黄色天堂 | 亚洲精品一区二区三区在线播放 | 免费一级夫妻a | 美女任你摸 | sxx免费看观看美女 sss亚洲国产欧美一区二区 |