国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

基于自定義token消息通知系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)

2020-08-26 07:46:55李毓麗陳泰宏
電腦知識(shí)與技術(shù) 2020年20期

李毓麗 陳泰宏

摘要:為了提供給Web應(yīng)用以及Web開(kāi)發(fā)者一種即時(shí)消息通知的解決方案,該設(shè)計(jì)主要以當(dāng)下流行的vuejs+Laravel+Mysql的技術(shù)組合方式,將前后端完全分離,降低開(kāi)發(fā)的耦合性,有利于多階段的部署。并在此基礎(chǔ)上,白定義了一套令牌校驗(yàn)機(jī)制以及利用swoole下的websocket接口實(shí)現(xiàn)消息通知功能,并且使用redis提高性能。

關(guān)鍵詞:redis;websocket;自定義token;消息通知

中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A

文章編號(hào):1009-3044(2020)20-0084-03

1背景

一個(gè)成熟的Web應(yīng)用必定少不了消息通知。例如商城中的到貨提醒功能,又如社交類型的網(wǎng)站,當(dāng)用戶A關(guān)注了用戶B,或者用戶C收藏了用戶D的話題,系統(tǒng)會(huì)定時(shí)發(fā)送更新的消息,去提醒對(duì)應(yīng)的用戶,所以,消息通知在當(dāng)下的WEB應(yīng)用中是非常重要的組成部分。

2消息通知系統(tǒng)的設(shè)計(jì)

消息通知系統(tǒng)是以用戶為單位來(lái)進(jìn)行實(shí)現(xiàn),主要的技術(shù)棧為前端采用vue2.x,利用vue-cli創(chuàng)建項(xiàng)目,是一個(gè)單頁(yè)Web應(yīng)用,也就是我們說(shuō)的SPA應(yīng)用。SPA應(yīng)用有許多的優(yōu)點(diǎn),良好的交互體驗(yàn),用戶不需要頻繁的刷新頁(yè)面,通過(guò)Ajax從后臺(tái)瀆取數(shù)據(jù),利用vuex對(duì)數(shù)據(jù)進(jìn)行管理。良好的前后端分離開(kāi)發(fā)方式,讓單頁(yè)Web應(yīng)用可以和RESTful規(guī)約一起使用,通過(guò)RESTAPI提供接口數(shù)據(jù),并使用Ajax獲取數(shù)據(jù),這樣有助于分離客戶端和服務(wù)器端工作。在客戶端也可以分解為靜態(tài)頁(yè)面和頁(yè)面數(shù)據(jù)響應(yīng)兩個(gè)部分。最重要的是可以減輕服務(wù)器壓力,服務(wù)器只需要傳輸數(shù)據(jù)以及對(duì)一些存在安全隱患的數(shù)據(jù)進(jìn)行驗(yàn)證,不用管展示邏輯和頁(yè)面合成,吞吐能力會(huì)提高幾倍。但SPA單頁(yè)應(yīng)用也有一個(gè)比較大的缺點(diǎn),就是SEO難度較高,所以在優(yōu)化期間會(huì)將頁(yè)面修改成服務(wù)器渲染的方式。

后端采用的是PHP的框架Laravel,Laravel框架提供了許多軟件包,Laravel有一個(gè)好處就是倉(cāng)庫(kù)非常多,加上有compos-er這一包管理器,使得其拓展性非常大。包括前端的一些組件包以及后臺(tái)的一些插件。由于我們采用的是前后端完全分離這一模式,會(huì)存在跨域問(wèn)題,因此使用了laravel-cors這一插件包,解決跨域問(wèn)題。

根據(jù)需求,主要分為三部分,第一部分是用戶的登錄機(jī)制的實(shí)現(xiàn)。登錄采用前后分離的方式,這就需要令牌token的驗(yàn)證,利用自定義實(shí)現(xiàn)的令牌機(jī)制,在登錄注冊(cè)這一功能上,根據(jù)OAUTH2.0協(xié)議,對(duì)用戶的登錄進(jìn)行加密傳遞,通過(guò)用戶提供的用戶名密碼認(rèn)證成功后,傳遞回來(lái)的access_token去獲取用戶數(shù)據(jù)。同時(shí),還集成了短信登錄(阿里云短信接口)和微博登錄(微博開(kāi)放平臺(tái)),方便用戶登錄,更好的拓展用戶量。

第二部分是基于用戶登錄完成后的消息通知系統(tǒng),當(dāng)用戶登錄成功后,系統(tǒng)會(huì)自動(dòng)連接上消息系統(tǒng),本部分的功能基于PHP異步框架swoole的實(shí)現(xiàn),通過(guò)swoole底層的封裝,達(dá)成對(duì)websocket協(xié)議的支持,從而使得用戶能夠作為長(zhǎng)鏈接穩(wěn)定的接受系統(tǒng)通知消息。

第三部分是基于Redis的以用戶為單位的域分離隊(duì)列消息推送。消息通知系統(tǒng)中少不了訂閱推送的功能,當(dāng)系統(tǒng)中的管理員,想給所有的用戶推送一條消息,假設(shè)平臺(tái)有10萬(wàn)個(gè)用戶,如果按照原來(lái)的思路來(lái)實(shí)現(xiàn),那么就意味著每向所有用戶推送一條消息,則會(huì)通過(guò)消息表,向消息表中增加10萬(wàn)條數(shù)據(jù),這樣做無(wú)疑會(huì)對(duì)數(shù)據(jù)庫(kù)的性能造成非常大的影響,會(huì)對(duì)服務(wù)器造成很大的壓力。而利用redis,則可以實(shí)現(xiàn)只插入一條數(shù)據(jù),通知所有用戶,并對(duì)用戶的其他消息不造成影響。

3基于JWT標(biāo)準(zhǔn)與HMACSHA256+base64加密規(guī)則實(shí)現(xiàn)的自定義token機(jī)制

Laravel框架白帶了一個(gè)實(shí)現(xiàn)OAUTH2.0協(xié)議和JWT標(biāo)準(zhǔn)的擴(kuò)展包,名為L(zhǎng)aravel-passport,該擴(kuò)展包將token和Refresh-Token存于數(shù)據(jù)庫(kù)。并且安全性等各方面也已經(jīng)做得比較完善,但是缺點(diǎn)也很明顯,復(fù)雜的實(shí)現(xiàn)邏輯,繁重的trait,還有麻煩的將token記錄到數(shù)據(jù)庫(kù)(這樣無(wú)異于session)這些特點(diǎn),都使得這個(gè)擴(kuò)展包性能變得非常緩慢,并且很明顯,擴(kuò)展性以及對(duì)外友好性不強(qiáng),耦合性太高(只適用于Laravel框架)。因此實(shí)現(xiàn)一個(gè)自定義的token機(jī)制。

3.1基于JWT標(biāo)準(zhǔn)的token定義

根據(jù)JWT標(biāo)準(zhǔn),token被拆分為三部分:頭部、載荷、簽名。頭部中存著實(shí)現(xiàn)簽名的算法規(guī)則,以及實(shí)現(xiàn)方式,如下面代碼所示:

private statiC $header= array(

'alg'=>'HS256',//生成signature的算法

'typ'=>'JWT'//類型

);

payload主要存著我們需要的數(shù)據(jù)內(nèi)容,叫作jwt載荷,需要幾個(gè)基本的字段,用于在后期的解析中使用,其中包括用戶idtoken的頒發(fā)時(shí)間和過(guò)期時(shí)間,以及jti(該token的唯一標(biāo)識(shí))等。payload是token中最主要的部分。下面是定義access_token和refresh_token的payload的實(shí)現(xiàn)。

accesstoken:

$data= array(

'iss'=>self::loadConf($iss)['secretuser'],//該JWT的簽發(fā)者

'iat'=> time(),//簽發(fā)時(shí)間

'exp'=> time()+ $exp,//過(guò)期時(shí)間

'nbf'=> time0+ 10,//該時(shí)間之前不接收處理該Token

'uid'=>$uid.

'type'=>'access',

'refresh_id'=>$refresh_id,

'jti'=> $jti//該token的id);

refresh_token:

$data= array(

'iss'=>self::loadConf($iss)['secret_user'],//該JWT的簽發(fā)者

'iat'=> time(),//簽發(fā)時(shí)間

'exp'=> time()+ $exp,//過(guò)期時(shí)間

'nbf'=> time()+ 60,//該時(shí)間之前不接收處理該Token

'uid'=>$uid.

'type'=>'refresh',

'jti'=>$jti);

第三部分是簽名部分,簽名部分是由一個(gè)白定義的隨機(jī)字符串組成的key,由這個(gè)key,對(duì)前面兩部分進(jìn)行base64編碼和HMACSHA256加密形成簽名。這一部分是確保token安全性的必須內(nèi)容。生成token代碼:

public static function getToken(array $payload, string $key)

{if(is_array($payload)){

$base64header=self:: base64UrIEncode(json_encode(self::$header, JSON_UNESCAPED_UNICODE));

$base64payload=self:: base64UrlEncode(json_encode($payload, JSON_UNESCAPED_UNICODE));

retum $base64header.'.'.$base64payload.'.'.self::signature($base64header.'.'.$base64payload, $key, self'::$header['alg']);

}else{

return false;

}}

當(dāng)生成token之后,再通過(guò)指定的key,將token解析,當(dāng)校對(duì)無(wú)誤后,解析出完整的載荷內(nèi)容,當(dāng)解析正確,中間件將認(rèn)為這是一個(gè)允許訪問(wèn)的請(qǐng)求,則可以進(jìn)入控制器,執(zhí)行相應(yīng)的功能邏輯。為了給不同的用戶定制不同的配置,例如管理員to-ken和用戶token的secret key肯定不能相同,或者過(guò)期時(shí)間等配置,所以會(huì)獨(dú)立出來(lái),以使用者為單位進(jìn)行配置,配置文件如下所示:

/*用戶*/

'user'=>[

'secret_key'=>'vhjcUExrBL5q6kWW"',//Str:: random()生成

'secret user'=>'Jasper_User',

'access_token_expire_time'=>7200.//access_token過(guò)期時(shí)間

'ref'resh_token_expire_time'=>86400.//refresh_token過(guò)期時(shí)間

],

/*管理員*/

'admin'=>[

'seCret_key'=>'IlzzMTZAX6tycbkM',//Str::random()生成

'seCret user'=>'Jasper_Admin',

'accesstoken_expiretime'=>84600,//access_token過(guò)期時(shí)間

'refresh_token_expire_time'=>86400.//refresh_token過(guò)期時(shí)間

],

對(duì)前臺(tái)用戶的中間件攔截請(qǐng)求如下面代碼所示,對(duì)于管理員的攔截也是類似的寫法,開(kāi)發(fā)者只需要簡(jiǎn)單的修改一下使用者的身份并且在配置文件配置好,則可以實(shí)現(xiàn)不同的秘鑰。這樣很好的解決了耦合性的問(wèn)題,使開(kāi)發(fā)者很方便的就能夠使用不同的機(jī)制開(kāi)發(fā)其他不同類型的功能。

$token= SerAuth::getFinalToken($Auth);

if (!$token) return response(retumAPI(6000));

$result=SerAuth::verifyToken($token);

if(! $result['token'])

retum resp.nse(returnAPl($result['code']));

$info= $result['payload']['uid'];

$request->payload= $info;

return $next($request);

4基于websocket協(xié)議和swoole的消息通知系統(tǒng)實(shí)現(xiàn)

系統(tǒng)的實(shí)現(xiàn)較為復(fù)雜,采用點(diǎn)對(duì)多的方式。管理員給用戶發(fā)送一條消息,消息將會(huì)通過(guò)某個(gè)控制器的方法,調(diào)用封裝好的發(fā)送消息的service方法,將消息發(fā)送websocket服務(wù)器。該ServiCe作為一個(gè)短暫的客戶端,將信息轉(zhuǎn)發(fā)給webs。cket服務(wù)器之后,立即斷開(kāi),因?yàn)樵撨B接不需要進(jìn)行長(zhǎng)連接占用資源,它只負(fù)責(zé)講消息發(fā)送到服務(wù)器。再由已經(jīng)連接上websocket的前臺(tái)客戶端,接收服務(wù)器所發(fā)送的消息。

獲取信息的內(nèi)容,并將信息入庫(kù)。然后利用composer中的websocket建立一個(gè)短鏈接客戶端,鏈接到服務(wù)器,將消息通知給對(duì)應(yīng)的用戶。當(dāng)管理員發(fā)送消息請(qǐng)求時(shí)候,需要將消息的主體入庫(kù),以此來(lái)達(dá)到同步數(shù)據(jù)的目的。同步數(shù)據(jù)是為了準(zhǔn)確的提高用戶了解未讀信息的數(shù)量。當(dāng)管理員發(fā)起一條消息,消息表入庫(kù),未讀消息則加l,在客戶端的store倉(cāng)庫(kù)中,會(huì)將用戶顯示的未讀消息數(shù)量增1,所以當(dāng)用戶刷新或者退出登錄狀態(tài)時(shí),即使收不到及時(shí)信息,但是消息表中依然有記錄,等待用戶登錄的時(shí)候,依舊能夠看到所有的信息。這就完成了一整套的消息通知流程。具體如下圖1所示。

5基于Redis緩存的數(shù)據(jù)讀取,減少直接讀取數(shù)據(jù)庫(kù)以及對(duì)數(shù)據(jù)庫(kù)的操作

本系統(tǒng)是基于websocket下的swoole框架來(lái)完成業(yè)務(wù),配合Mysql數(shù)據(jù)庫(kù)的消息表進(jìn)行消息的存取。其中的一個(gè)業(yè)務(wù)是管理員需要向平臺(tái)的所有用戶推送某一消息,例如商品上線通知等系統(tǒng)消息。那么,如果平臺(tái)有十萬(wàn)甚至百萬(wàn)個(gè)用戶,推送的數(shù)據(jù)量存入消息表就非常大。如果每發(fā)送一條消息,消息表都需要增加10萬(wàn)條數(shù)據(jù),那么這樣對(duì)數(shù)據(jù)庫(kù)性能和服務(wù)器要求無(wú)疑是非常大的。因此,我們可以通過(guò)緩存來(lái)實(shí)現(xiàn)全部推送。管理員向所有用戶推送信息,只需要在數(shù)據(jù)庫(kù)中加入一個(gè)字段“is_all",來(lái)判斷該消息是否是所有用戶的消息即可。在查詢某個(gè)用戶的消息時(shí),會(huì)有兩部分,一是擁有自己的消息的數(shù)據(jù),二是帶有“is_all”字段的消息。當(dāng)用戶已讀消息時(shí),正常消息會(huì)修改“is_read”字段標(biāo)識(shí)為已讀,而對(duì)于“is_all,類型的消息,為了避免其他用戶的消息受到影響,則會(huì)需要用緩存將每個(gè)用戶的已讀消息存人,在數(shù)據(jù)查詢時(shí)忽略這些內(nèi)容,即可完成已讀未讀區(qū)分。刪除功能也是如此。

經(jīng)過(guò)多次的測(cè)試發(fā)現(xiàn),這種消息通知入庫(kù)的方式可行,并可以極大程度的避免了數(shù)據(jù)庫(kù)中數(shù)據(jù)的冗余性,有利于系統(tǒng)高效的運(yùn)行,減少了頻繁入庫(kù)的操作。

6結(jié)束語(yǔ)

論文主要論述了基于白定義token以用戶為單位的消息通知系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)。重點(diǎn)闡述自定義token機(jī)制的實(shí)現(xiàn),包括頭部、載荷、簽名。實(shí)現(xiàn)基于websocket協(xié)議和swoole的消息通知流程,以及消息通知入庫(kù)的方式,提高系統(tǒng)的性能和運(yùn)行效率,

參考文獻(xiàn):

[1] Vue CLI[EB/OLl.[2019-12-20l.https://cli.vuejs.org/zh/guitle/.

[2] larave16.0中文文檔[EB/OL].[2019-12-20].https://leamku.com/docs/laraveU6.0.

[3] Vanessa Wang.HTML5 WebSocket指南2018[M].北京:機(jī)械工業(yè)出版社,2014: 21-250.

【通聯(lián)編輯:謝媛媛】

收稿日期:2020-05-08

作者簡(jiǎn)介:李毓麗( 1980-),女,廣東普寧人,副教授,碩士,主要從事網(wǎng)絡(luò)技術(shù)、網(wǎng)絡(luò)編程方向的教學(xué)研究。

绵竹市| 黄石市| 海丰县| 南宁市| 海门市| 科技| 无棣县| 察哈| 天长市| 大荔县| 杨浦区| 慈利县| 巴青县| 银川市| 霍林郭勒市| 哈密市| 集安市| 壶关县| 敦化市| 黄梅县| 方山县| 武安市| 墨竹工卡县| 壶关县| 平昌县| 韶关市| 四平市| 承德县| 玉溪市| 岳阳县| 定州市| 乐安县| 鄂温| 东港市| 新乡市| 青铜峡市| 定南县| 象山县| 喀喇沁旗| 方山县| 湘西|