电竞比分网-中国电竞赛事及体育赛事平台

分享

EL-ADMIN學(xué)習(xí)筆記

 冒險(xiǎn)的K 2021-09-17

一,支持接口限流,避免惡意請(qǐng)求導(dǎo)致服務(wù)層壓力過大


常見的限流功能一般有兩個(gè)關(guān)注點(diǎn):

1.限流原則,即以什么樣的條件對(duì)請(qǐng)求進(jìn)行識(shí)別以及放行。常見的作法是給予每個(gè)調(diào)用API的系統(tǒng)不同的唯一編碼,用于監(jiān)控某一編碼的調(diào)用是否超出上限。

2.限流機(jī)制,即通過什么樣的機(jī)制實(shí)現(xiàn)限流。常見的作法是通過RedisKeyTTL失效機(jī)制來限制訪問頻率。

比較常見的處理方案是使用RedisKeyTTL失效機(jī)制來限制訪問頻率,然后通過切面實(shí)現(xiàn)處理限流邏輯,并使用自定義注解將零散的關(guān)注點(diǎn)集中起來。

接下來按照上述兩個(gè)關(guān)注點(diǎn)對(duì)EL-ADMIN中如何實(shí)現(xiàn)的接口限流進(jìn)行拆解:

首先是它的限流原則

圖一,限流使用實(shí)例

通過me.zhengjie.modules.system.rest.LimitController的test方法就可以看到EL-ADMIN對(duì)限流的封裝還是比較易用的,只需通過注解設(shè)置指定時(shí)間段內(nèi)允許訪問的次數(shù)以及限流的鍵名(前綴+key),其中name是指的該接口的功能備注,并不會(huì)影響限流的功能,只是方便在日志中檢索對(duì)應(yīng)接口的信息。

圖二,限流切面

但是不止于此(見上圖),進(jìn)一步挖掘處理這個(gè)注解的Around類型切面(me.zhengjie.aspect.LimitAspect)的時(shí)候發(fā)現(xiàn)限流的原則不止是通過key的方式還有通過客戶端IP的方式來限制,Around切面可以理解為在所有Limit注解處添加了一個(gè)執(zhí)行攔截器,判斷是否滿足允許執(zhí)行的條件后通過joinPoint.proceed()執(zhí)行原來的方法(這里類似攔截器的過濾鏈處理模式)??偨Y(jié)如下:在使用Redis作為限流核心機(jī)制的設(shè)計(jì)方案中,Key的設(shè)置直接決定了限流原則。

其次是它的限流機(jī)制

通過圖二的限流切面分析可知,相比于一般的限流邏輯,EL-ADMIN進(jìn)一步對(duì)Redis的操作做了優(yōu)化,使用了Lua腳本來執(zhí)行限流的判斷機(jī)制。具體的限流機(jī)制是通過將接口地址結(jié)合Limit注解的prefix+key+url的方式構(gòu)建了一個(gè)接口對(duì)應(yīng)的唯一鍵,通過查詢Redis上該鍵TTL生命周期period)時(shí)間段內(nèi)的值(訪問次數(shù)count)實(shí)現(xiàn)限流的邏輯。限流機(jī)制這部分的核心Lua實(shí)現(xiàn)如下圖所示,需要注意的是在第6行和第7行,在Redis中假如直接對(duì)一個(gè)key執(zhí)行incr命令會(huì)將這個(gè)key設(shè)置為1。

圖三,限流機(jī)制Lua腳本

另外相比一般場(chǎng)景下使用RedisTemplate執(zhí)行這一系列操作,使用Lua腳本操作Redis的優(yōu)勢(shì)如下:

1.保證對(duì)Redis操作的原子性,防止出現(xiàn)對(duì)同一個(gè)key的非原子性操作。

2.減少網(wǎng)絡(luò)數(shù)據(jù)傳輸節(jié)省帶寬。


二,支持接口級(jí)別的功能權(quán)限與數(shù)據(jù)權(quán)限,可自定義操作


這里提到的功能權(quán)限一般是指使用者能否使用系統(tǒng)的功能,數(shù)據(jù)權(quán)限同理則是指使用者能否訪問對(duì)應(yīng)的數(shù)據(jù),如果看過上一篇《RuoYi-Vue學(xué)習(xí)筆記-分析》就不難理解這里的數(shù)據(jù)權(quán)限的使用場(chǎng)景了。

一般的功能權(quán)限都是基于RBAC思想設(shè)置一套基于角色的權(quán)限授予方案,讓用戶的權(quán)限通過角色這一層抽象和系統(tǒng)中的權(quán)限發(fā)生交互。

常見的功能權(quán)限的解決方案是使用Spring Security + Jwt Token前者提供了成套的權(quán)限過濾器,只需要將我們?cè)O(shè)計(jì)權(quán)限系統(tǒng)的判斷邏輯接入過濾器即可,后者則有效的解決了客戶端身份盜用以及服務(wù)端的橫向擴(kuò)展的問題。數(shù)據(jù)權(quán)限在使用MyBatis框架時(shí)可以通過超類冗余字段+請(qǐng)求處理切面實(shí)現(xiàn),在使用Spring Boot Jpa中的具體實(shí)現(xiàn)可以探索一番。在這一章節(jié)我們可以拆解以下兩個(gè)功能的具體實(shí)現(xiàn):

1.Spring Boot Jpa數(shù)據(jù)權(quán)限解決方案的實(shí)現(xiàn)。

2.基于Spring Security框架實(shí)現(xiàn)的功能權(quán)限如何實(shí)現(xiàn)全局接口自定義權(quán)限放行,即通常情況下需要使用@PreAuthorize("hasAnyRole('admin','menu:edit')")這類注解才能讓接口放行,但是超管可以訪問所有的接口,那么我們就要給每個(gè)接口都添加admin的注解值了,這樣做是效率很低的,通過全局接口自定義權(quán)限放行的方案即可實(shí)現(xiàn)在一處配置即可全局接口免去這個(gè)配置(這一特性的具體實(shí)現(xiàn)在之后的第三章節(jié)進(jìn)行解構(gòu))。

首先,一起來看看EL-ADMIN對(duì)數(shù)據(jù)權(quán)限的封裝,在拆解這部分之前需要先對(duì)Jpa有個(gè)大概的了解,不然會(huì)對(duì)這部分實(shí)現(xiàn)拆解的理解會(huì)有影響。當(dāng)下常見的應(yīng)用訪問數(shù)據(jù)的方式有以下兩種,一種是以Hibernate為首的信奉"程序員就不該寫SQL"的一眾ORM框架,這類框架不會(huì)讓程序員寫一行SQL,統(tǒng)統(tǒng)都交給框架處理,與之對(duì)應(yīng)的則是封裝了SQL變化的一眾注解以及類(個(gè)人感覺這個(gè)抽象層的機(jī)制比SQL還難...);一種是以MyBatis為代表的通過JDBC橋接了數(shù)據(jù)庫,讓程序員能夠靈活的自定義SQL語句來執(zhí)行,這類框架則是注重靈活,與之對(duì)應(yīng)的則是需要有一定的SQL基礎(chǔ)。

JPA就是Hibernate所遵守的標(biāo)準(zhǔn)名稱,使用這個(gè)標(biāo)準(zhǔn)的框架可以通過以下列表的注解實(shí)現(xiàn)SQL語句的對(duì)等功能。

圖四,JPA注解及其功能

參考RuoYi對(duì)數(shù)據(jù)權(quán)限的處理,要想實(shí)現(xiàn)數(shù)據(jù)權(quán)限至少有兩個(gè)點(diǎn)需要關(guān)注,

一是查詢前用戶數(shù)據(jù)權(quán)限的獲取;

二是查詢時(shí)數(shù)據(jù)權(quán)限注入;

首先是用戶數(shù)據(jù)權(quán)限的獲取,這部分?jǐn)?shù)據(jù)一般是通過登錄的時(shí)候放置在用戶的cookies中或是登錄后的session中,這部分在EL-ADMIN中是通過下圖中的幾步實(shí)現(xiàn)的,需要注意的是使用了Vuex的異步接口請(qǐng)求的功能(圖中第一步)。

圖五,前端登錄流程

至此,系統(tǒng)已經(jīng)獲取到了用戶的數(shù)據(jù)權(quán)限的信息,系統(tǒng)前端請(qǐng)求接口的時(shí)候攜帶對(duì)應(yīng)的用戶Token即可,后端接口在登錄的時(shí)候已經(jīng)對(duì)Token和用戶信息(包含了用戶數(shù)據(jù)權(quán)限記錄)進(jìn)行了綁定和Redis緩存,便于接口調(diào)用的時(shí)候直接通過緩存獲取。接下來就是第二步查詢時(shí)數(shù)據(jù)權(quán)限注入的拆解,這部分RuoYi是使用了注解+切面+超類基本和業(yè)務(wù)代碼不侵入的方式實(shí)現(xiàn)的,EL-ADMIN的實(shí)現(xiàn)對(duì)比之下和業(yè)務(wù)代碼的耦合性較大(這個(gè)鍋其實(shí)和JPA的設(shè)計(jì)哲學(xué)“程序員絕不寫SQL”有關(guān),也不能讓EL-ADMIN背)。如下圖所示,自定義數(shù)據(jù)權(quán)限注解在這個(gè)功能中的職責(zé)一如既往的是用來實(shí)現(xiàn)查詢條件以及字段的個(gè)性化的注入到查詢過程中。

圖六,后端數(shù)據(jù)權(quán)限查詢拼接流程

上圖中需要注意的有這么兩點(diǎn):

1.左側(cè)line62,數(shù)據(jù)權(quán)限信息獲取中是通過Spring Security提供的用戶信息容器SecurityContextHolder獲取的,可以理解為通過線程池綁定了用戶信息構(gòu)成的上下文。

2.右側(cè)line45同理,也是通過1中的這個(gè)上下文獲取的用戶對(duì)應(yīng)的數(shù)據(jù)權(quán)限信息。


三,自定義權(quán)限注解與匿名接口注解,可快速對(duì)接口攔截與放行


這個(gè)特性其實(shí)是特性二的補(bǔ)充,相當(dāng)于在接口前做了一個(gè)切面,針對(duì)特定的角色可以不去做權(quán)限校驗(yàn)。針對(duì)這個(gè)特性,一般的解決方案是通過注解對(duì)接口的權(quán)限進(jìn)行標(biāo)記,然后通過切面/攔截器/過濾器對(duì)接口調(diào)用的請(qǐng)求進(jìn)行匹配判斷。至此引出這一特性的幾個(gè)關(guān)注點(diǎn):

1.用于標(biāo)記攔截以及放行的自定義注解

2.整合入Spring Security的切面/過濾器/攔截器

首先關(guān)注攔截以及放行的注解,這部分標(biāo)記權(quán)限攔截的注解使用了常見的@PreAuthorize,只不過其中的值是使用了"@el.check('****')"這種形式的字符串。這部分是通過SpringEL表達(dá)式獲取了容器中的Bean,然后計(jì)算對(duì)應(yīng)的結(jié)果,這部分邏輯可以參考原始的hasAnyRole方法返回的結(jié)果來實(shí)現(xiàn)的。究其本質(zhì)就是針對(duì)權(quán)限判斷這部分,框架其他的部分不做變更,添加一個(gè)模塊輸入輸出的類型不變,那么就是可以契合原有框架的,通過下圖的原生的寫法比對(duì)更好理解一些,可以看到兩者的返回類型都是布爾值,且根據(jù)面向接口編程的規(guī)范我們甚至可以實(shí)現(xiàn)一個(gè)該方法所在的接口實(shí)現(xiàn)類來定制我們自己的權(quán)限判斷邏輯。

圖七,自定義權(quán)限判斷規(guī)則

放行的則是使用了自定義的注解,值得一看的是EL-ADMIN通過對(duì)Spring MVC@RequestMapping注解的擴(kuò)展實(shí)現(xiàn)了框架需要的功能,充分的體現(xiàn)了六大設(shè)計(jì)原則之一的開閉原則。如下圖所示,放行的注解有以下兩種使用場(chǎng)景,一是針對(duì)現(xiàn)有代碼直接添加注解即可,一是針對(duì)新的方法可以把對(duì)應(yīng)的注解當(dāng)作SpringMVC框架原生的注解使用。

圖八,自定義注解的放行策略

自定義注解實(shí)現(xiàn)的放行策略這部分需要注意的有幾處:

1.通過圖八中的方法,通過覆蓋原有的注解實(shí)現(xiàn)帶有權(quán)限放行+路由功能的自定義注解。

2.自定義權(quán)限放行和Spring Security框架的整合,通過SpringMVC中提供的Bean收集所有帶有放行注解的路由實(shí)現(xiàn)無配置自動(dòng)化的放行。

3.放行所有的Options為HTTP請(qǐng)求動(dòng)詞的請(qǐng)求,這是因?yàn)榭缬蛘?qǐng)求需要用到這類動(dòng)詞的請(qǐng)求,攔截就無法實(shí)現(xiàn)跨域了。


四,前后端統(tǒng)一異常攔截處理,統(tǒng)一輸出異常,避免繁瑣的判斷


一般來說統(tǒng)一的異常攔截都可以通過過濾器或是攔截器實(shí)現(xiàn),Spring很貼心的幫我們做了這部分的封裝,這一章節(jié)主要是拆解前后端結(jié)合的異常攔截的工程實(shí)踐方案。

1.首先需要在前端對(duì)響應(yīng)有全局的處理邏輯,讓所有的響應(yīng)都能通過前端邏輯進(jìn)行轉(zhuǎn)化提示,說白了就是axios做的事情了。

2.然后在后端通過過濾器,切面,攔截器等等實(shí)現(xiàn)對(duì)響應(yīng)的修改操作。

這里主要對(duì)后端的實(shí)現(xiàn)做拆解,常見的異常處理機(jī)制都是在業(yè)務(wù)層、控制層拋出,通過@ExceptionHandler注解處理異常拋出的過程。該機(jī)制的實(shí)現(xiàn)是通過切面實(shí)現(xiàn)的,其中切面的切點(diǎn)聲明是通過注解@RestControllerAdvice或@ControllerAdvice實(shí)現(xiàn)。

基于切面,這部分功能特性可以做的拓展有異常處理(注解@ExceptionHandler),初始化數(shù)據(jù)綁定器用于特定數(shù)據(jù)格式的參數(shù)綁定(注解@InitBinder),特殊請(qǐng)求參數(shù)的綁定(注解@ModelAttribute)。對(duì)應(yīng)的使用場(chǎng)景分別是,根據(jù)拋出的不同的異常封裝不同的請(qǐng)求流轉(zhuǎn)以及響應(yīng),將前端請(qǐng)求的字符串類型的日期轉(zhuǎn)化為Date類型的日期,不通過控制器層將參數(shù)綁定到Model中。

總之,將@ControllerAdvice這個(gè)注解理解為在過濾器,攔截器,之后的最后一道攔在請(qǐng)求和我們應(yīng)用的處理邏輯之間的處理流程即可。通過這個(gè)切面結(jié)合注解@ExceptionHandler可以將控制器層以及業(yè)務(wù)層拋出的所有異常根據(jù)異常類型,異常所屬的包進(jìn)行差異化的攔截處理。


五,支持運(yùn)維管理,可方便地對(duì)遠(yuǎn)程服務(wù)器的應(yīng)用進(jìn)行部署與管理


這個(gè)特性已經(jīng)讓EL-ADMIN脫離了后端框架的定位了,雖然這個(gè)功能還是比較常用的,但是這個(gè)特性中包含的功能只要單獨(dú)拿出來,然后稍微拓展就可以當(dāng)作服務(wù)器管理工具來用了。

通過官方文檔的描述可以把重點(diǎn)放在這幾個(gè)功能的實(shí)現(xiàn)上:

  1. 新增服務(wù)器,整合ssh客戶端連接服務(wù)器。

  2. 新增應(yīng)用,通過ftp等命令將應(yīng)用發(fā)送至服務(wù)器指定目錄。

  3. 部署應(yīng)用,通過shell命令實(shí)現(xiàn)應(yīng)用的部署,啟動(dòng)等操作。結(jié)合2后還可以執(zhí)行上傳的sql腳本。

通過下圖九可以更好的理解上述的特性點(diǎn),首先是第一步,用戶通過本地和EL-ADMIN的交互將應(yīng)用包上傳到服務(wù)器上,此時(shí)應(yīng)用文件會(huì)通過相關(guān)的配置參數(shù)推送到遠(yuǎn)端的服務(wù)器,然后通過部署腳本進(jìn)行部署。這里比較關(guān)鍵點(diǎn)是需要能夠監(jiān)控遠(yuǎn)端服務(wù)器此時(shí)指定的應(yīng)用的狀態(tài),一般使用微服務(wù)架構(gòu)的應(yīng)用可以使用服務(wù)注冊(cè)與發(fā)現(xiàn)中心來實(shí)現(xiàn)這個(gè)監(jiān)控,如果是單體的巨石應(yīng)用則可以監(jiān)控對(duì)應(yīng)的端口進(jìn)程狀態(tài)即可。

圖九,用戶對(duì)遠(yuǎn)程服務(wù)器應(yīng)用部署與管理

上述幾個(gè)功能用到了兩個(gè)開源組件以及一個(gè)之前所沒有接觸過的技術(shù)點(diǎn),分別是:

jsch,連接指定的機(jī)器后執(zhí)行shell命令,并獲取對(duì)應(yīng)的返回值。

ganymed-ssh2,連接指定的機(jī)器后獲取文件或上傳文件的工具。

websocket,這個(gè)技術(shù)的確是做CURD Boy不經(jīng)常接觸的技術(shù)點(diǎn),定義是這樣描述的:

WebSocket 是 HTML5 開始提供的一種在單個(gè) TCP 連接上進(jìn)行全雙工通訊的協(xié)議。

這里有兩個(gè)點(diǎn)需要解釋一下,一個(gè)是單個(gè),怎么理解這個(gè)單個(gè)呢,一般來說你在網(wǎng)頁上點(diǎn)擊一個(gè)超鏈接就是單個(gè)的概念了,一個(gè)請(qǐng)求就是單個(gè)TCP連接了。其次就是全雙工的定義,這是一個(gè)通訊概念,同屬一類的有單工,半雙工,全雙工。他們的區(qū)別就是從兩個(gè)維度上來區(qū)分,其一是信息的傳遞是單向的還是雙向的,單向的就是單工,雙向的就是雙工。其二是發(fā)送和接受能否同時(shí)執(zhí)行,能夠就是全雙工,不能就是半雙工。對(duì)應(yīng)我們實(shí)際在開發(fā)中使用到的通信技術(shù)和協(xié)議,http,ajax這些就是半雙工的,因?yàn)檎?qǐng)求時(shí)信息的流向時(shí)雙向的,但是不能同時(shí)進(jìn)行接受信息和發(fā)送信息,websocket則是提供了一種在發(fā)送消息的時(shí)候接受消息的技術(shù)標(biāo)準(zhǔn)。

有了websocket這個(gè)新玩具, EL-ADMIN把它用到了遠(yuǎn)端服務(wù)器狀態(tài)的實(shí)時(shí)信息推送,比如應(yīng)用部署完成,應(yīng)用卸載,應(yīng)用當(dāng)前運(yùn)行狀態(tài)的獲取等等。在廣闊的業(yè)務(wù)場(chǎng)景中,因?yàn)?/span>websocket的實(shí)時(shí)性比ajax要高的多,所以也可以用到聊天的場(chǎng)景中。


參考文獻(xiàn):

[0]EL-ADMIN特性https:///guide/kslj.html#%E9%A1%B9%E7%9B%AE%E7%AE%80%E4%BB%8B

[1]Lua語法https://www.runoob.com/lua/lua-basic-syntax.html

[2]Redis中使用Lua腳本https://www.cnblogs.com/kaituorensheng/p/11098194.html

[3]JPA常見注解https://blog.csdn.net/wang_1220/article/details/107915815

[4]數(shù)據(jù)權(quán)限https://www.cnblogs.com/anderson-question/articles/14907553.html

[5]JpaSpecificationExecutor的使用https://zhuanlan.zhihu.com/p/139107843

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多