文檔修改歷史
版本號(hào) | 修訂人 | 摘要 | 日期 |
---|---|---|---|
1.0 | 鐘大 | 創(chuàng)建文檔 | 6.12 |
1.1 | 鐘大 | 完成文檔 | 6.13 |
1. 概述
1.1. 術(shù)語(yǔ)
術(shù)語(yǔ) | 描述 |
---|---|
SpringBoot | 簡(jiǎn)化Spring應(yīng)用的初始搭建以及開發(fā)過(guò)程蔬顾。該框架使用了特定的方式來(lái)進(jìn)行配置,從而使開發(fā)人員不再需要定義樣板化的配置 |
MyBatis | 基于Java的DB持久層框架 |
JWT | Json web token文留, 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開放標(biāo)準(zhǔn)埃儿。該token被設(shè)計(jì)為緊湊且安全的缀蹄,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場(chǎng)景 |
CSRF | Cross Site Request Forgery攒射,跨站請(qǐng)求偽造 |
XSS | Cross Site Scripting搁宾,跨站腳本漏洞 |
1.2. 需求背景
用Java完成一個(gè)網(wǎng)站的后臺(tái),實(shí)現(xiàn)注冊(cè)误证、登錄继薛、鑒權(quán)、登出的功能愈捅。
1.3. 目標(biāo)
1.3.1. 商業(yè)目標(biāo)
- 3個(gè)月拉新用戶XX遏考,一年拉新用戶YY,MAU ZZ蓝谨;(運(yùn)營(yíng)數(shù)據(jù)待定)
- 構(gòu)建賬號(hào)體系灌具,可以圍繞用戶賬號(hào)做運(yùn)營(yíng)活動(dòng),收集用戶數(shù)據(jù)像棘,比如手機(jī)用戶基本信息稽亏、行為習(xí)慣,為同類型用戶提供更個(gè)性缕题、定制化的內(nèi)容;
- 有利于公司后期多條產(chǎn)品線的構(gòu)建胖腾,老帶新等產(chǎn)品整合烟零,實(shí)現(xiàn)用戶一個(gè)賬號(hào)登錄,多平臺(tái)使用咸作,節(jié)約用戶使用成本锨阿,有利于商業(yè)變現(xiàn)。
1.3.2. 系統(tǒng)目標(biāo)
- 支持商業(yè)目標(biāo)的用戶容量记罚,通過(guò)分布式系統(tǒng)來(lái)負(fù)載均衡和動(dòng)態(tài)擴(kuò)容能力墅诡;
- 注冊(cè)和登錄方式可橫向擴(kuò)展,比如快速支撐手機(jī)號(hào)碼桐智、郵箱末早、第三方平臺(tái)登錄等方式烟馅;
- 后臺(tái)系統(tǒng)的安全性考慮,比如防密碼暴力破解然磷、防拖庫(kù)郑趁、不要明文傳輸存儲(chǔ)用戶密碼等。
1.4. 相關(guān)文檔
- 前端認(rèn)證登錄鑒權(quán)--Session 和 JWT簡(jiǎn)介
- QQ登錄的加密傳輸安全 姿搜,這篇解釋即使使用HTTPS寡润,傳輸明文密碼也可能被中間人攻擊后獲取。
- travist/jsencrypt 舅柜,前端使用公鑰加密 明文密碼的js庫(kù)梭纹。
- BCrypt密碼加密-加鹽機(jī)制 ,增加破解難度致份,比如增加彩虹表破解難度变抽。
2. 業(yè)務(wù)流程和架構(gòu)分析
2.1. 整體業(yè)務(wù)流程
- 如下圖,本期實(shí)現(xiàn)了賬密 注冊(cè)知举、登錄瞬沦、鑒權(quán)、登出 4大基礎(chǔ)功能雇锡;
- 忘記密碼和重置密碼本次未實(shí)現(xiàn)逛钻,后一個(gè)迭代考慮加上該功能;
- 手機(jī)號(hào)碼锰提、郵箱曙痘、第三方平臺(tái)登錄本期也未實(shí)現(xiàn),但是代碼和DB表結(jié)構(gòu)支撐快速擴(kuò)展實(shí)現(xiàn)立肘,待PD提需求边坤。
2.2. 整體業(yè)務(wù)架構(gòu)
- 用戶賬密注冊(cè)、登錄谅年、鑒權(quán)茧痒、登出等行為是賬號(hào)體系的功能反應(yīng),本文構(gòu)建基礎(chǔ)的賬號(hào)體系融蹂,支持多種賬號(hào)方式及第三方平臺(tái)的登錄方式旺订。
- 要素包括用戶基本信息、行為習(xí)慣等超燃;
- 我們經(jīng)常接觸到的賬號(hào)組合方式可能如下幾種区拳,本次實(shí)現(xiàn)用戶名&密碼的注冊(cè)方式,但是代碼和DB表結(jié)構(gòu)支持快速擴(kuò)展意乓。
- 用戶名&密碼
- 手機(jī)&密碼|驗(yàn)證碼
- 郵箱&密碼|驗(yàn)證碼
- 第三方平臺(tái)賬號(hào)(微信樱调、QQ、微博等)
- 游客登錄
- 后臺(tái)分配固定權(quán)限賬號(hào)登錄(后臺(tái)系統(tǒng)常見(jiàn))
2.3. 登錄注冊(cè)常見(jiàn)的漏洞和攻擊分析
本次需求分析了常見(jiàn)的漏洞攻擊,將在下面的“4. 業(yè)務(wù)用例分析”一節(jié)重點(diǎn)解決笆凌。
2.3.1. 常見(jiàn)漏洞
- 賬戶密碼明文傳輸
- 驗(yàn)證碼重復(fù)使用
- 任意用戶注冊(cè)
- 登錄賬號(hào)泄露
- 弱賬號(hào)和密碼
2.3.2. 上述漏洞容易引發(fā)的攻擊方式
- 撞庫(kù)
- 中間人劫持賬戶密碼
- 密碼定向破解
- 批量注冊(cè)賬戶
- 弱密碼
- 驗(yàn)證碼爆破和繞過(guò)
2.3.3. 漏洞解決方案
- 賬戶密碼明文傳輸 —— 密碼在前端通過(guò)公鑰加密傳輸圣猎,服務(wù)端用私鑰解碼后消費(fèi);
- 驗(yàn)證碼重復(fù)使用 —— 每個(gè)驗(yàn)證碼使用后菩颖,在redis刪除样漆,防止重復(fù)使用;
- 任意用戶注冊(cè) —— 每次注冊(cè)都要驗(yàn)證圖片驗(yàn)證碼晦闰,后續(xù)采用手機(jī)號(hào)碼和郵箱注冊(cè)放祟,解決方案更徹底;
- 弱賬號(hào)和密碼 —— 限制賬號(hào)和密碼長(zhǎng)度呻右,密碼必須大小寫+數(shù)字跪妥。
2.4. 用例
3. 系統(tǒng)架構(gòu)和領(lǐng)域模型
3.1. 系統(tǒng)整體架構(gòu)
- 本次只使用mysql和redis來(lái)實(shí)現(xiàn)賬號(hào)密碼注冊(cè)和登錄,后續(xù)第三方平臺(tái)登錄可以接入微信和QQ声滥。
- 比如 AccountController 依賴 AccountService眉撵,AccountService再依賴數(shù)據(jù)庫(kù)Mapper、Redis封裝類落塑、util類等纽疟。
3.2. 關(guān)鍵技術(shù)及第三方框架依賴
3.2.1. 開發(fā)框架
- 引入SpringBoot搭建Java Web框架,簡(jiǎn)化Spring應(yīng)用的初始搭建以及開發(fā)過(guò)程憾赁。該框架使用了特定的方式來(lái)進(jìn)行配置污朽,從而使開發(fā)人員不再需要定義樣板化的配置。
- MyBatis基于Java的DB持久層框架龙考,封裝底層Mapper訪問(wèn)DB蟆肆。
3.2.2. 數(shù)據(jù)庫(kù)技術(shù)
- MySQL,通過(guò)關(guān)系數(shù)據(jù)庫(kù)持久化存儲(chǔ)用戶信息晦款、賬戶密碼等炎功。
3.2.3. 緩存技術(shù)
- Redis緩存圖片驗(yàn)證碼,防止黑客暴力破解密碼缓溅。同時(shí)緩存登出token蛇损,解決token過(guò)期時(shí)效性問(wèn)題。
3.2.4. BCrypt密碼加密
Bcrypt加密原理:
- 加密時(shí)坛怪,對(duì)于同一個(gè)密碼州藕,每次生成的hash是不同的,但是hash中包含了salt(hash產(chǎn)生過(guò)程:先隨機(jī)生成salt酝陈,salt跟password進(jìn)行hash)
- 校驗(yàn)時(shí),從hash中取出salt毁涉,salt跟password進(jìn)行hash沉帮,得到的結(jié)果跟數(shù)據(jù)庫(kù)中提取的的hash進(jìn)行比對(duì)返回Boolean類型:true/false
Bcrypt與MD5進(jìn)行對(duì)比:
- 首先MD5加密后存儲(chǔ)為32位,Bcrypt為60位。
- 相對(duì)來(lái)說(shuō)BCrypt比MD5更安全穆壕,但是加密更慢待牵。(簡(jiǎn)單MD5加密后密碼一樣,數(shù)據(jù)庫(kù)當(dāng)中不同用戶存儲(chǔ)了相同的密碼值喇勋,那么當(dāng)知道其中一個(gè)用戶的密碼后就可以窺探出相同密碼的用戶缨该,BCrypt加密每次生成的密文是不一樣的。如:dH/eH7tuzPCkaFSfm44QXePkTxbfuhguLwC.hPmZ2Sp81bcdWbL1W 即使得到了原文密碼川背,破譯其他用戶密碼的機(jī)率是很小的)贰拿。
- Bcrypt加密有利也有弊,優(yōu)點(diǎn)是安全性較高熄云,弊端就是如果存儲(chǔ)量比較大膨更,性能消耗也是非常大的。但是用戶登錄注冊(cè)并不是一個(gè)高并發(fā)的接口缴允,所以影響并不會(huì)特別大荚守。
3.2.5. JWT實(shí)現(xiàn)鑒權(quán)
相比Session 的以下缺點(diǎn),JWT(Json Web Token)有很大優(yōu)勢(shì)练般,本次鑒權(quán)方案采用JWT矗漾。
3.2.5.1. Session的缺點(diǎn)
- cookie + session 在跨域場(chǎng)景表現(xiàn)不好。
- 如果是分布式部署, 需要做多機(jī)共享 session 機(jī)制薄料。
- 基于 cookie 的機(jī)制很容易被 CSRF敞贡。
- 查詢 session 信息可能會(huì)有數(shù)據(jù)庫(kù)查詢操作
3.2.5.2. JWT 工作原理
- 瀏覽器端通過(guò) POST 請(qǐng)求傳遞用戶名和密碼給服務(wù)端, 服務(wù)端校驗(yàn)后, 如果成功將用戶 id 等其他信息作為 jwt 的有效載荷和頭部進(jìn)行 base64 編碼之后形成了一個(gè) jwt, 這段 jwt 就像以點(diǎn)分割的亂碼字符串。
- 然后后端將這個(gè)字符串作為登錄成功的返回結(jié)果和 200 狀態(tài)碼返回給前端, 前端將其保存在 localstorage 和 sessionstorage 中, 每次請(qǐng)求都把這個(gè) jwt 字符串作為 http 頭里的 Authorization 加上 Bearer 和 jwt 字符串, 發(fā)送給后端, 后端檢查是否存在和有效性(例如檢查簽名正確與否, 令牌過(guò)期與否等), 驗(yàn)證通過(guò)后后端使用 jwt 中包含的用戶信息, 這段用戶信息保存在 jwt 的有效載荷中, 驗(yàn)證解密后就拿到用戶信息, 進(jìn)行其他業(yè)務(wù)邏輯, 返回業(yè)務(wù)結(jié)果。
- 退出登錄的時(shí)候就刪除這段 jwt字符串即可。
3.2.5.3. JWT vs. Session
可拓展性
session 多以redis,數(shù)據(jù)庫(kù)等保存在服務(wù)器中, 水平拓展方案下需要?jiǎng)?chuàng)建一個(gè)獨(dú)立中心的存儲(chǔ)店雅。所以 jwt 要比 session 要好一點(diǎn), jwt 可以無(wú)縫接入拓展, 因?yàn)榛趖oken 令牌的驗(yàn)證是無(wú)狀態(tài)的, 所以不需要在 session 中存儲(chǔ)用戶信息咆霜。
安全性
對(duì)于XSS跨站腳本攻擊. js 可以修改 JWT, 因?yàn)?JWT 通常放在 localstorage 或 cookie 中, js 可以修改這些變量, 所以也可以修改 JWT, 此時(shí)就會(huì)出現(xiàn) xss 攻擊, 比如壞人把代碼注入頁(yè)面中.
如何防范呢?可以通過(guò)簽名, 加密兩種方式. 還有不要把敏感信息放在 jwt 中, 防止密鑰泄露導(dǎo)致信息泄露。
RESTful API 方面
RESTful 架構(gòu)要求程序應(yīng)該是無(wú)狀態(tài)的盖淡。因此 session 這種有狀態(tài)的認(rèn)證方式, 顯然違反了 RESTful 的基本限制。
性能
jwt 性能不太好。因?yàn)樵诳蛻舳讼蚍?wù)端發(fā)出請(qǐng)求的時(shí)候, 可能有大量的用戶信息在 jwt 中, 那么每個(gè) http 請(qǐng)求都產(chǎn)生大量的開銷, 而 session 只用少量的開銷, 因?yàn)?session-id 比較小. jwt 是json, 而且包含了完整的信息, 所以可能是它的好幾倍大. jwt 是空間換時(shí)間啦桌,完整信息都在字符串里, 不需要查詢。而 session的缺點(diǎn)是每一個(gè)請(qǐng)求都要在服務(wù)器上查找 session, 因?yàn)槟玫降氖?session-id, 沒(méi)有完整的信息, 要用 id 查完整信息. 所以要消耗性能及皂。
有效期
無(wú)狀態(tài)是JWT的特點(diǎn)甫男,JWT是一次性的。想修改里面的內(nèi)容验烧,比如token續(xù)期板驳,就必須簽發(fā)一個(gè)新的JWT。最簡(jiǎn)單的一種方式是每次請(qǐng)求刷新JWT碍拆,即每個(gè)HTTP請(qǐng)求都返回一個(gè)新的JWT若治。這個(gè)方法不僅暴力不優(yōu)雅慨蓝,而且每次請(qǐng)求都要做JWT的加密解密,會(huì)帶來(lái)性能問(wèn)題端幼。另一種方法是在redis中單獨(dú)為每個(gè)JWT設(shè)置過(guò)期時(shí)間礼烈,每次訪問(wèn)時(shí)刷新JWT的過(guò)期時(shí)間,本文采用方法二婆跑。
3.3. 系統(tǒng)依賴關(guān)系
- 前端實(shí)現(xiàn)注冊(cè)此熬、登錄、會(huì)員中心等頁(yè)面功能滑进,同時(shí)調(diào)用后端API進(jìn)行功能交互邏輯犀忱;
- 后臺(tái)系統(tǒng)采用SpringBoot+Mybatis框架實(shí)現(xiàn)注冊(cè)、登錄等邏輯處理郊供;
- MySQL的關(guān)系數(shù)據(jù)庫(kù)存儲(chǔ)用戶的賬號(hào)峡碉、密碼、用戶信息驮审;
- Redis保存圖片驗(yàn)證碼和校驗(yàn)鲫寄,同時(shí)保存失效后的JWT token。
3.4. 領(lǐng)域模型
3.4.1. MySQL數(shù)據(jù)庫(kù)模型
表DDL如下疯淫,user表維護(hù)用戶基本信息地来,local_auth支持賬密、手機(jī)號(hào)熙掺、郵箱驗(yàn)證碼等多種本地實(shí)現(xiàn)的登錄方式未斑,oauth支持微信、QQ币绩、微博等開放平臺(tái)的第三方登錄蜡秽。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
`nickname` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用戶昵稱',
`gender` varchar(2) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '性別',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_nickname` (`nickname`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `local_auth`;
CREATE TABLE `local_auth` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
`user_id` int(11) NOT NULL COMMENT 'user表主鍵id',
`login_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '登錄id',
`login_type` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '登錄id類型,用戶名缆镣、手機(jī)號(hào)碼芽突、郵箱',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '用戶hash密碼',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user_id` (`user_id`) USING BTREE,
UNIQUE KEY `idx_login_id` (`login_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
DROP TABLE IF EXISTS `oauth`;
CREATE TABLE `oauth` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
`user_id` int(11) NOT NULL COMMENT 'user表主鍵id',
`oauth_id` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '第三方平臺(tái)id',
`oauth_type` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '第三方平臺(tái)標(biāo)識(shí)(qq、wechat董瞻、weibo)',
`access_token` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '第三方獲取的access_token,校驗(yàn)使用',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user_id` (`user_id`) USING BTREE,
UNIQUE KEY `idx_oauth_id_type` (`oauth_id`, `oauth_type`) USING BTREE,
KEY `idx_access_token` (`access_token`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
3.4.2. Redis模型
- 賬密登錄的圖片驗(yàn)證碼校驗(yàn)通過(guò)Redis 的key-value的String實(shí)現(xiàn)寞蚌,支持set/get/del/getExpire等。
3.4.3. 代碼結(jié)構(gòu)UML
- 展示層的Controller 通過(guò)聚合引入業(yè)務(wù)層的Service服務(wù)钠糊;
- 業(yè)務(wù)層登錄和注冊(cè)的驗(yàn)證碼和密碼校驗(yàn)通過(guò)接口 VerificationService 來(lái)繼承 賬密挟秤、手機(jī)號(hào)、郵箱地址等多種方式的快速擴(kuò)展抄伍。
3.4.4. 狀態(tài)機(jī)
不涉及
4. 業(yè)務(wù)用例分析
4.1. 賬密注冊(cè)
4.1.1. 用例描述
支持賬號(hào)和密碼注冊(cè)艘刚,使用圖片驗(yàn)證碼防止批量注冊(cè)。
4.1.2. 接口說(shuō)明
4.1.2.1. 獲取圖片驗(yàn)證碼接口說(shuō)明
- 獲取圖片驗(yàn)證碼API: /api/code/genImageCode截珍, Class/Method: VerificationCodeController#genImageCode
- 不能鑒權(quán)昔脯,GET請(qǐng)求返回Content-Type: image/jpeg啄糙,前端直接展示圖片。
4.1.2.2. 獲取公鑰接口說(shuō)明
- 獲取公鑰API: /api/account/getPublicKey云稚, Class/Method: AccountController#getPublicKey
輸入?yún)?shù):
無(wú)入?yún)?/p>
輸出參數(shù):
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
code | Integer | 是 | 結(jié)果處理碼,200成功沈堡,其他code表示多種失敗場(chǎng)景 |
message | String | 否 | 結(jié)果碼的文案描述 |
data | String | 否 | 公鑰字符串 |
錯(cuò)誤碼:
code錯(cuò)誤碼 | 對(duì)客文案 | 解釋 |
---|---|---|
550 | 系統(tǒng)異常 | 系統(tǒng)處理未知異常 |
響應(yīng)結(jié)果樣例:
{
"code": 200,
"message": "獲取公鑰成功",
"data": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf5nUMwxDdYiVtfpYCfkNzxuh5ASHoRLpCQjdWoZmnpaBOK9pwPT3knhLGXYTLMUZNRMgvn81dzJz4HCgNVH1m8tYtvhdwOUSw4V82LcpwYVE2mnWPkrmf3uSy+kzxWZnZjgSptk7YmaHQByfgv+ZyJKPKkmGLXclrhmxqsl5alQIDAQAB"
}
4.1.2.3. 賬密注冊(cè)接口說(shuō)明
- 注冊(cè)API: /api/account/register 静陈, Class/Method:AccountController#register
輸入?yún)?shù):
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
loginId | String | 是 | 登錄賬號(hào),可能是賬號(hào)诞丽、手機(jī)號(hào)鲸拥、郵箱地址 |
loginType | String | 否 | 登錄類型,username/mobile/email |
password | String | 否 | 公鑰加密的用戶密碼 |
verificationCode | String | 否 | 圖片碼 or 短信驗(yàn)證碼 or 郵箱驗(yàn)證碼 |
入?yún)永?/strong>
{
"loginId": "aa123456",
"loginType": "username",
"password": "ewD+yhHSWpmqfbh/zbLKGEu70rJWdF8Fo6c4roajzUUhk8DqKmcrAhzh+NokKMUw3TZhN4ejo7huXFsIUuUy5D6AY1SrqkFTh5mmsQjyJ3UsCAuQZSYwNWVpwjbl/XZDbq1BMeyA0nD1hIHcZY5dqEqOqu3P4bN5WdZa1nMLM64=",
"verificationCode": "GSMB"
}
輸出參數(shù):
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
code | Integer | 是 | 結(jié)果處理碼僧免,200成功刑赶,其他code表示多種失敗場(chǎng)景 |
message | String | 否 | 結(jié)果碼的文案描述 |
data | UserResponseDTO | 否 | 公鑰字符串 |
UserResponseDTO
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
userId | Long | 否 | user id |
nickname | String | 否 | 用戶昵稱 |
token | String | 否 | 鑒權(quán)token |
響應(yīng)結(jié)果樣例:
{"code":200,"data":{"nickname":"wechat_abc123456","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiNiIsImYzZDgwNDYxLTRlMDItNDRjMy05NzhlLWM3ZDQ4MGVhNGViMSJdLCJleHAiOjE2MjM2NTY1MzIsImlhdCI6MTYyMzU3MDEzMn0.mYo0Tk9Pd0E4xYP0SOWKAbuugg2ao5pmIbvCOx-ewQM","userId":6},"message":"登錄成功"}
錯(cuò)誤碼:
code錯(cuò)誤碼 | 對(duì)客文案 | 解釋 |
---|---|---|
550 | 系統(tǒng)異常 | 系統(tǒng)處理未知異常 |
452 | 用戶已存在,不能注冊(cè) | 數(shù)據(jù)庫(kù)已存在該loginId |
551 | 注冊(cè)失敗 | 插入user入庫(kù)失敗 |
4.1.3. 外部依賴接口
- MySQL: insert/update/select
- Redis:set/get/del
4.1.4. 業(yè)務(wù)流程說(shuō)明
4.1.4.1. 交互時(shí)序圖
- 前端獲取服務(wù)端的公鑰加密 賬戶密碼懂衩,防止明文傳輸被竊取撞叨,更加安全辦法再啟用HTTPS防止竊聽(tīng)和篡改加密的密碼。
- 用戶提交賬密時(shí)浊洞,前端賬號(hào)長(zhǎng)度和密碼長(zhǎng)度牵敷,要求密碼必須大小寫+數(shù)字,后端二次校驗(yàn)法希。
- 不能明文密碼入庫(kù)枷餐,防止密碼泄露,通過(guò)加鹽hash后入庫(kù)苫亦,再次防止彩虹表等方式的暴力破解毛肋。
- 提交注冊(cè)的賬號(hào)密碼需要輸入驗(yàn)證碼,防止機(jī)器批量注冊(cè)賬號(hào)或者破解密碼屋剑。
4.1.5. 非功能點(diǎn)分析
4.1.5.1. 冪等性設(shè)計(jì)
- 1润匙、第一次校驗(yàn) 圖片驗(yàn)證碼成功,刪除redis驗(yàn)證碼饼丘,如果重復(fù)提交驗(yàn)證碼趁桃,系統(tǒng)報(bào)錯(cuò),防止重復(fù)注冊(cè)肄鸽;
- 2卫病、注冊(cè)賬號(hào)在數(shù)據(jù)庫(kù)的表是唯一鍵,不能重復(fù)插入典徘,初步達(dá)到冪等效果蟀苛。
- 3、更好的方案是token機(jī)制逮诲,防止頁(yè)面重復(fù)提交帜平,但是囿于工作量幽告,本文暫未實(shí)現(xiàn)。
4.1.5.2. 安全性分析
SQL注入攻擊
- SQL注入會(huì)造成拖庫(kù)裆甩,原因絕大多數(shù)是直接使用輸入?yún)?shù)拼接SQL語(yǔ)句造成冗锁,防范方法:使用預(yù)編譯語(yǔ)句(PreparedStatement):可以加速SQL執(zhí)行,也可以防止SQL注入嗤栓。
數(shù)據(jù)庫(kù)權(quán)限配置
- 防止越權(quán)入侵服務(wù)器冻河,同時(shí)也要安裝入侵檢測(cè)系統(tǒng),監(jiān)控告警茉帅。
公私鑰保存好
- 防止密碼加密的公私鑰泄露導(dǎo)致用戶密碼被破解泄露叨叙。
4.2. 賬密登錄
4.2.1. 用例描述
支持賬號(hào)和密碼登錄,使用圖片驗(yàn)證碼防止登錄密碼的暴力破解堪澎。
4.2.2. 接口說(shuō)明
4.2.2.1. 獲取圖片驗(yàn)證碼接口說(shuō)明
- 同 4.1.2.1
4.2.2.2. 獲取公鑰接口說(shuō)明
- 同4.1.2.2
4.2.2.3. 賬密登錄接口說(shuō)明
- 注冊(cè)API: /api/account/login擂错, Class/Method:AccountController#login
輸入?yún)?shù):
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
loginId | String | 是 | 登錄賬號(hào),可能是賬號(hào)樱蛤、手機(jī)號(hào)钮呀、郵箱地址 |
loginType | String | 否 | 登錄類型,username/mobile/email |
password | String | 否 | 公鑰加密的用戶密碼 |
verificationCode | String | 否 | 圖片碼 or 短信驗(yàn)證碼 or 郵箱驗(yàn)證碼 |
入?yún)永?/strong>
{
"loginId": "aa123456",
"loginType": "username",
"password": "ewD+yhHSWpmqfbh/zbLKGEu70rJWdF8Fo6c4roajzUUhk8DqKmcrAhzh+NokKMUw3TZhN4ejo7huXFsIUuUy5D6AY1SrqkFTh5mmsQjyJ3UsCAuQZSYwNWVpwjbl/XZDbq1BMeyA0nD1hIHcZY5dqEqOqu3P4bN5WdZa1nMLM64=",
"verificationCode": "GSMB"
}
輸出參數(shù):
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
code | Integer | 是 | 結(jié)果處理碼刹悴,200成功行楞,其他code表示多種失敗場(chǎng)景 |
message | String | 否 | 結(jié)果碼的文案描述 |
data | UserResponseDTO | 否 | 公鑰字符串 |
UserResponseDTO
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
userId | Long | 否 | user id |
nickname | String | 否 | 用戶昵稱 |
token | String | 否 | 鑒權(quán)token |
響應(yīng)結(jié)果樣例:
{"code":200,"data":{"nickname":"wechat_abc123456","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiNiIsImYzZDgwNDYxLTRlMDItNDRjMy05NzhlLWM3ZDQ4MGVhNGViMSJdLCJleHAiOjE2MjM2NTY1MzIsImlhdCI6MTYyMzU3MDEzMn0.mYo0Tk9Pd0E4xYP0SOWKAbuugg2ao5pmIbvCOx-ewQM","userId":6},"message":"登錄成功"}
錯(cuò)誤碼:
code錯(cuò)誤碼 | 對(duì)客文案 | 解釋 |
---|---|---|
550 | 系統(tǒng)異常 | 系統(tǒng)處理未知異常 |
452 | 用戶已存在,不能注冊(cè) | 數(shù)據(jù)庫(kù)已存在該loginId |
551 | 注冊(cè)失敗 | 插入user入庫(kù)失敗 |
4.2.3. 外部依賴接口
- MySQL: insert/update/select
- Redis:set/get/del
4.2.4. 業(yè)務(wù)流程說(shuō)明
4.2.4.1. 交互時(shí)序圖
- 前端獲取服務(wù)端的公鑰加密 賬戶密碼土匀,防止明文傳輸被竊取子房,更加安全辦法再啟用HTTPS防止竊聽(tīng)和篡改加密的密碼。
- 用戶提交賬密時(shí)就轧,前端賬號(hào)長(zhǎng)度和密碼長(zhǎng)度证杭,要求密碼必須大小寫+數(shù)字,后端二次校驗(yàn)妒御。
- 不能明文密碼入庫(kù)解愤,防止密碼泄露,通過(guò)加鹽hash后入庫(kù)乎莉,再次防止彩虹表等方式的暴力破解送讲。
- 提交登錄的賬號(hào)密碼需要輸入驗(yàn)證碼,防止機(jī)器批量注冊(cè)賬號(hào)或者破解密碼惋啃。
4.2.5. 非功能點(diǎn)分析
4.2.5.1. 冪等性設(shè)計(jì)
- 登錄接口主要是校驗(yàn)密碼和驗(yàn)證碼哼鬓,無(wú)冪等性風(fēng)險(xiǎn)。
4.2.5.2. 安全性分析
- 同 4.2.5.1. 的安全性分析边灭。
4.3. 鑒權(quán)
4.3.1. 用例描述
沒(méi)有在Controller方法打注解@PassToken异希,一律鑒權(quán)jwt token。
4.3.2. 接口說(shuō)明
無(wú)對(duì)外API
4.3.3. 外部依賴接口
- MySQL: insert/update/select
- Redis:set/get/del
4.3.4. 業(yè)務(wù)流程說(shuō)明
4.3.4.1. JWT組成
JWT 由三部分組成绒瘦,分別是 header(頭部)称簿,payload(載荷)扣癣,signature(簽證) 這三部分以小數(shù)點(diǎn)連接起來(lái)。
例如使用名為 jwt-token 的cookie來(lái)存儲(chǔ) JWT 例如:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiMTIzNDU2IiwiMDI5ZDdlNDItZDAxYy00ZjQ4LTk3ZTgtNGE2NzNhZDQ4M2Y4Il0sImV4cCI6MTYyMzYzOTU5NSwiaWF0IjoxNjIzNTUzMTk1fQ.gG7QRGPCnOY0pyRbHPcULwXY0duh2YdIR-HHMFDbM1Q
使用.分割值可以得到三部分組成元素憨降,按照順序分別為:
header:
- 值:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
- Base64 解碼:
{"typ":"JWT","alg":"HS256"}
payload:
- eyJhdWQiOlsiMTIzNDU2IiwiMDY3MDEzZDEtOWU2ZS00ZmQ4LWE3MTMtMmMyOTcyNzQzNTQxIl0sImV4cCI6MTYyMzYzOTg3OSwiaWF0IjoxNjIzNTUzNDc5fQ
- Base64 解碼:
{
"aud": ["123456", "067013d1-9e6e-4fd8-a713-2c2972743541"],
"exp": 1623639879,
"iat": 1623553479
}
signature:
- 值:gG7QRGPCnOY0pyRbHPcULwXY0duh2YdIR-HHMFDbM1Q
- 服務(wù)端根據(jù)header和payload計(jì)算出 signature父虑,和要校驗(yàn)的JWT中的 signature 部分進(jìn)行對(duì)比,如果 signature 部分相等則是一個(gè)有效的 JWT券册。
4.3.4.2. 交互時(shí)序圖
- 使用SpringBoot攔截器技術(shù) AuthenticationInterceptor 來(lái)實(shí)現(xiàn)频轿;
- 在redis中單獨(dú)為每個(gè)JWT設(shè)置過(guò)期時(shí)間,每次訪問(wèn)時(shí)如果過(guò)期時(shí)間短于30分鐘烁焙,刷新JWT的過(guò)期時(shí)間。
4.3.5. 非功能點(diǎn)分析
4.3.5.1. 冪等性設(shè)計(jì)
- 不涉及
4.3.5.2. 安全性分析
- 不要把敏感信息放在 jwt 中耕赘。
- 防止密鑰泄露導(dǎo)致信息泄露骄蝇。
4.4. 退出登錄
4.4.1. 用例描述
支持用戶主動(dòng)登出賬號(hào),系統(tǒng)清除登錄態(tài)token操骡。
4.4.2. 接口說(shuō)明
4.4.2.1. 登出接口說(shuō)明
- 登出API: /api/account/logout九火, Class/Method:AccountController#logout
輸入?yún)?shù):
無(wú)入?yún)?/p>
輸出參數(shù):
參數(shù)名稱 | 類型 | 是否必傳 | 說(shuō)明 |
---|---|---|---|
code | Integer | 是 | 結(jié)果處理碼,200成功册招,其他code表示多種失敗場(chǎng)景 |
message | String | 否 | 結(jié)果碼的文案描述 |
響應(yīng)結(jié)果樣例:
{"code":200,"message":"登出成功"}
錯(cuò)誤碼:
code錯(cuò)誤碼 | 對(duì)客文案 | 解釋 |
---|---|---|
550 | 系統(tǒng)異常 | 系統(tǒng)處理未知異常 |
4.4.3. 外部依賴接口
- Redis:set/get/del
4.4.4. 業(yè)務(wù)流程說(shuō)明
4.4.4.1. 交互時(shí)序圖
- 登出用戶需要到redis清除userId保存的jwt token岔激。
4.4.5. 非功能點(diǎn)分析
4.4.5.1. 冪等性設(shè)計(jì)
- 退出登錄會(huì)清除redis的key,支持冪等性是掰。
4.4.5.2. 安全性分析
- 登出用戶需要到redis清除userId保存的jwt token虑鼎。
5. 配置類專項(xiàng)分析
5.1. 秘鑰配置
- 用戶密碼的加密公私鑰需要安全保存,比如阿里采用KMI系統(tǒng)保存键痛;
- JWT Token生成的秘鑰也要安全保存炫彩。
5.2. 數(shù)據(jù)庫(kù)鏈接配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wechat?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf8
username: root
password:
5.3. Redis鏈接配置
redis:
database: 0 # Redis數(shù)據(jù)庫(kù)索引(默認(rèn)為0)
host: 127.0.0.1 # Redis服務(wù)器地址
port: 6379 # Redis服務(wù)器連接端口
password: # Redis服務(wù)器連接密碼(默認(rèn)為空)
max-wait: 30000 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制)
max-active: 100 # 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制)
max-idle: 20 # 連接池中的最大空閑連接
min-idle: 0 # 連接池中的最小空閑連接
timeout: 5000 # 連接超時(shí)時(shí)間(毫秒)
6. 兼容性分析
全部新增功能,無(wú)兼容性風(fēng)險(xiǎn)絮短。
7. 對(duì)周邊系統(tǒng)影響分析
新增功能江兢,無(wú)原有功能影響。
8. 后續(xù)計(jì)劃
8.1. 運(yùn)營(yíng)
- 注冊(cè)送紅包丁频,定期登錄送積分杉允,拉新促活。
- 開放給更多公司的業(yè)務(wù)線使用席里,作為通用的注冊(cè)登錄平臺(tái)叔磷,用戶一個(gè)賬號(hào),多個(gè)業(yè)務(wù)使用胁勺。
8.2. 功能增強(qiáng)
- 支持主動(dòng)修改密碼 和 忘記密碼的充值密碼世澜;
- 手機(jī)號(hào)碼、郵箱署穗、第三方平臺(tái)登錄寥裂。
8.3. 安全加固
- 異地登錄的安全風(fēng)險(xiǎn)管控嵌洼;
- 登錄設(shè)備的更換風(fēng)險(xiǎn);
- 限制用戶當(dāng)日密碼登錄次數(shù)封恰,防止攻擊者定向破解該用戶的密碼麻养;
- 引入高性能設(shè)備防止DDoS攻擊;
8.4. 分布式系統(tǒng)
- 入口負(fù)載均衡诺舔,比如引入Nginx鳖昌;
- 增加熔斷、限流低飒、降級(jí)等分布式管控能力许昨;
- 分庫(kù)分表;
- 異步消費(fèi)設(shè)計(jì):登錄注冊(cè)等操作后褥赊,異步記錄用戶操作日志糕档,通知下游發(fā)積分或者紅包等;
- 靈活多樣的監(jiān)控告警:業(yè)務(wù)層拌喉、系統(tǒng)層速那、機(jī)器性能等。