springboot之security?FilterSecurityInterceptor的使用要點(diǎn)記錄
spring security FilterSecurityInterceptor使用要點(diǎn)
FilterSecurityInterceptor是一個(gè)方法級(jí)的權(quán)限過(guò)濾器, 基本位于過(guò)濾鏈的最底部

該過(guò)濾器用于控制method級(jí)別的權(quán)限控制. 官方提供了2種默認(rèn)的方法權(quán)限控制寫法
一種是在方法上加注釋實(shí)現(xiàn),另一種是在configure配置中通過(guò)
@Secured("ROLE_ADMIN") //法1, 方法定義處加注釋, 需先在具體的配置里開啟此類配置
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
法2, 在復(fù)寫的configure里直接定義
.antMatchers("your match rule").authenticated()
.antMatchers("your match rule").hasRole("ADMIN") //使用時(shí)權(quán)限會(huì)自動(dòng)加前綴ROLE_ADMIN
具體細(xì)節(jié)的代碼就不貼了,官方文檔一模一樣的都有.
上面兩種方法最終都會(huì)生成一個(gè)FilterSecurityInterceptor實(shí)例,放在上面過(guò)濾鏈底部. 用于方法級(jí)的鑒權(quán).
官方還提到了第三種方法,關(guān)于如何把過(guò)濾的規(guī)則放到更為靈活的位置,數(shù)據(jù)庫(kù)/本地文件/等等.
貼一段官方代碼
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
//此方法用于鑒權(quán)過(guò)程中獲取當(dāng)前的請(qǐng)求URL需要哪種權(quán)限
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
String url = fi.getRequestUrl();
String httpMethod = fi.getRequest().getMethod();
List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>();
// Lookup your database (or other source) using this information and populate the
// list of attributes
return attributes;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
具體思路就是通過(guò)自定義過(guò)濾器的MetadataSource來(lái)實(shí)現(xiàn)規(guī)則的靈活配置,該部分實(shí)例默認(rèn)使用的是DefaultFilterInvocationSecurityMetadataSource, 可以根據(jù)這里的源碼來(lái)編寫自己的MetadataSource.
內(nèi)部使用下面這個(gè)結(jié)構(gòu)來(lái)維護(hù)匹配規(guī)則和對(duì)應(yīng)的權(quán)限集
Map<RequestMatcher, Collection<ConfigAttribute>> requestMap
RequestMatcher和ConfigAttribute都是抽象類,需要找個(gè)能用的類,通過(guò)查閱源碼可以找到RequestMathcer的具體構(gòu)造實(shí)現(xiàn)
RequestMatchers.antMatchers(...)
不過(guò)該方法不能直接拿來(lái)用,寫了私有
public static List<RequestMatcher> antMatchers(HttpMethod httpMethod,
String... antPatterns) {
String method = httpMethod == null ? null : httpMethod.toString();
List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : antPatterns) {
matchers.add(new AntPathRequestMatcher(pattern, method));
}
return matchers;
}
改用new AntPathRequestMatcher(pattern, method)這個(gè)就行
ConfigAttribute有一個(gè)叫SecurityConfig的實(shí)例, 構(gòu)造時(shí)傳入String就可以構(gòu)造對(duì)應(yīng)的權(quán)限實(shí)例
不過(guò)官方好像沒(méi)寫具體怎么注入這個(gè)自定義的MetadataSource???
找了半天好像都沒(méi)找到能直接注入到默認(rèn)的Filter的途徑, 沒(méi)辦法只能寫一個(gè)新的自定義FilterSecurityInterceptor來(lái)注入, 通過(guò)configure里的addFilter()方法放入過(guò)濾鏈
setSecurityMetadataSource()方法寫入自定義的rules源, 還需要注入AccessDecision和Authentication,
前者用于驗(yàn)證訪問(wèn)權(quán)限, 繼承AccessDecisionManager實(shí)現(xiàn)decide方法來(lái)編寫自定義的驗(yàn)證邏輯
decide方法包含 Authentication, 一個(gè)Object類型的FilterInvocation實(shí)例, 一組ConfigAttribute(要求的權(quán)限列表, 從MetaSource的getAttributes()方法里取的, 完整的獲取和校驗(yàn)上層邏輯都封裝在AbstractSecurityInterceptor的beforeInvocation()方法里)
后者用于驗(yàn)證登錄授權(quán), 后者使用默認(rèn)的super.authenticationManager()即可
完成自定義方法級(jí)過(guò)濾后碰到幾個(gè)問(wèn)題,一個(gè)是加入了這個(gè)Filter后原先方法1和方法2設(shè)置的就都失效了.
這里直接說(shuō)看源碼打斷點(diǎn)后的結(jié)論, 主要是因?yàn)樽远x的filter加入后, 和原先的默認(rèn)FilterSecurityInterceptor會(huì)有互相排斥的問(wèn)題, 具體表現(xiàn)為只要這兩個(gè)中的其中一個(gè)先執(zhí)行invoke()方法, 就會(huì)在request里追加一個(gè)名為__spring_security_filterSecurityInterceptor_filterApplied的attribute表示FilterSecurityInterceptor這個(gè)類型的過(guò)濾器已經(jīng)執(zhí)行過(guò)了. 當(dāng)另一個(gè)同類的FilterSecurityInterceptor進(jìn)來(lái)時(shí)就直接跳過(guò)具體的invoke方法直接執(zhí)行下一個(gè)過(guò)濾器了.
過(guò)濾器的位置排序上, addFilter()加的自定義FilterSecurityInterceptor排到了默認(rèn)的FilterSecurityInterceptor之前,如果要放在默認(rèn)的后面, 用addFilterAfter()方法, 指定需要放在哪個(gè)過(guò)濾器后面.
所以對(duì)應(yīng)的解決辦法也很簡(jiǎn)單,覆寫自定義過(guò)濾器中的invoke方法,把加attribute那段去掉.
我就不處理這個(gè)問(wèn)題了, 其實(shí)這個(gè)地方算不算一個(gè)問(wèn)題還得單獨(dú)考慮的, 包括上面自定義Metadata也是.
有這么幾個(gè)其實(shí)寫之前就該考慮好的問(wèn)題.
- 系統(tǒng)里的方法級(jí)權(quán)限真的需要通過(guò)數(shù)據(jù)庫(kù)靈活配置嗎?
- 系統(tǒng)真的需要讓自定義的filter和默認(rèn)filter的權(quán)限規(guī)則同時(shí)生效嗎?
其實(shí)spring官方是推薦方法級(jí)權(quán)限就直接硬編碼的. 因?yàn)榭紤]到放在數(shù)據(jù)庫(kù)后, 安全上的風(fēng)險(xiǎn)實(shí)在太大了.
僅僅通過(guò)修改數(shù)據(jù)庫(kù),即使非admin角色的賬戶也是能獲取所有的操作權(quán)限的.
另一點(diǎn)是操作權(quán)限定義上的變更(哪些角色該有哪些操作權(quán)限?)本身就應(yīng)該是需要審計(jì)的,并且非常低頻的.
硬編碼在排除風(fēng)險(xiǎn)之余,對(duì)于實(shí)際使用的影響其實(shí)也是微乎其微的(無(wú)非每次確定要改了,發(fā)一次版)
至于我為何要寫自定義的FilterSecurityInterceptor, 主要是系統(tǒng)的security集成在路由層,那邊不定義方法,法2在configure里硬編碼好像又太繁瑣,所以想在Metadatasource層用文件或者什么靜態(tài)常量的方法硬編碼.
不過(guò)最后寫完發(fā)現(xiàn)好像也不便利?并不快樂(lè)??? 為了這個(gè)目標(biāo)多寫了好多實(shí)現(xiàn)類,并不能輕松愉快地直接注入Metadatasource.
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java8優(yōu)雅的字符串拼接工具類StringJoiner實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Java8優(yōu)雅的字符串拼接工具類StringJoiner的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
SpringBoot項(xiàng)目打成war和jar的區(qū)別說(shuō)明
這篇文章主要介紹了SpringBoot項(xiàng)目打成war和jar的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
java動(dòng)態(tài)規(guī)劃算法——硬幣找零問(wèn)題實(shí)例分析
這篇文章主要介紹了java動(dòng)態(tài)規(guī)劃算法——硬幣找零問(wèn)題,結(jié)合實(shí)例形式分析了java動(dòng)態(tài)規(guī)劃算法——硬幣找零問(wèn)題相關(guān)原理、實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-05-05
在idea中使用JaCoCo插件統(tǒng)計(jì)單元測(cè)試覆蓋率的實(shí)現(xiàn)
這篇文章主要介紹了在idea中使用JaCoCo插件統(tǒng)計(jì)單元測(cè)試覆蓋率的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01
Java線程安全和鎖Synchronized知識(shí)點(diǎn)詳解
在本篇文章里小編給大家分享的是關(guān)于Java線程安全和鎖Synchronized相關(guān)知識(shí)點(diǎn),有需要的朋友們可以參考下。2019-08-08

