网站后台系统设计

文档修改历史

版本号 修订人 摘要 日期
1.0 钟大 创建文档 6.12
1.1 钟大 完成文档 6.13

1. 概述

1.1. 术语

术语 描述
SpringBoot 简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置
MyBatis 基于Java的DB持久层框架
JWT Json web token, 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景
CSRF Cross Site Request Forgery,跨站请求伪造
XSS Cross Site Scripting,跨站脚本漏洞

1.2. 需求背景

用Java完成一个网站的后台,实现注册、登录、鉴权、登出的功能。

1.3. 目标

1.3.1. 商业目标

  • 3个月拉新用户XX,一年拉新用户YY,MAU ZZ;(运营数据待定)
  • 构建账号体系,可以围绕用户账号做运营活动,收集用户数据,比如手机用户基本信息、行为习惯,为同类型用户提供更个性、定制化的内容;
  • 有利于公司后期多条产品线的构建,老带新等产品整合,实现用户一个账号登录,多平台使用,节约用户使用成本,有利于商业变现。

1.3.2. 系统目标

  • 支持商业目标的用户容量,通过分布式系统来负载均衡和动态扩容能力;
  • 注册和登录方式可横向扩展,比如快速支撑手机号码、邮箱、第三方平台登录等方式;
  • 后台系统的安全性考虑,比如防密码暴力破解、防拖库、不要明文传输存储用户密码等。

1.4. 相关文档

2. 业务流程和架构分析

2.1. 整体业务流程

  • 如下图,本期实现了账密 注册、登录、鉴权、登出 4大基础功能;
  • 忘记密码和重置密码本次未实现,后一个迭代考虑加上该功能;
  • 手机号码、邮箱、第三方平台登录本期也未实现,但是代码和DB表结构支撑快速扩展实现,待PD提需求。
整体业务流程

2.2. 整体业务架构

  • 用户账密注册、登录、鉴权、登出等行为是账号体系的功能反应,本文构建基础的账号体系,支持多种账号方式及第三方平台的登录方式。
  • 要素包括用户基本信息、行为习惯等;
  • 我们经常接触到的账号组合方式可能如下几种,本次实现用户名&密码的注册方式,但是代码和DB表结构支持快速扩展。
    • 用户名&密码
    • 手机&密码|验证码
    • 邮箱&密码|验证码
    • 第三方平台账号(微信、QQ、微博等)
    • 游客登录
    • 后台分配固定权限账号登录(后台系统常见)
整体业务架构

2.3. 登录注册常见的漏洞和攻击分析

本次需求分析了常见的漏洞攻击,将在下面的“4. 业务用例分析”一节重点解决。

2.3.1. 常见漏洞

  • 账户密码明文传输
  • 验证码重复使用
  • 任意用户注册
  • 登录账号泄露
  • 弱账号和密码

2.3.2. 上述漏洞容易引发的攻击方式

  • 撞库
  • 中间人劫持账户密码
  • 密码定向破解
  • 批量注册账户
  • 弱密码
  • 验证码爆破和绕过

2.3.3. 漏洞解决方案

  • 账户密码明文传输 —— 密码在前端通过公钥加密传输,服务端用私钥解码后消费;
  • 验证码重复使用 —— 每个验证码使用后,在redis删除,防止重复使用;
  • 任意用户注册 —— 每次注册都要验证图片验证码,后续采用手机号码和邮箱注册,解决方案更彻底;
  • 弱账号和密码 —— 限制账号和密码长度,密码必须大小写+数字。

2.4. 用例

用例图

3. 系统架构和领域模型

3.1. 系统整体架构

  • 本次只使用mysql和redis来实现账号密码注册和登录,后续第三方平台登录可以接入微信和QQ。
  • 比如 AccountController 依赖 AccountService,AccountService再依赖数据库Mapper、Redis封装类、util类等。
系统架构图.png

3.2. 关键技术及第三方框架依赖

3.2.1. 开发框架

  • 引入SpringBoot搭建Java Web框架,简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。
  • MyBatis基于Java的DB持久层框架,封装底层Mapper访问DB。

3.2.2. 数据库技术

  • MySQL,通过关系数据库持久化存储用户信息、账户密码等。

3.2.3. 缓存技术

  • Redis缓存图片验证码,防止黑客暴力破解密码。同时缓存登出token,解决token过期时效性问题。

3.2.4. BCrypt密码加密

Bcrypt加密原理:

  • 加密时,对于同一个密码,每次生成的hash是不同的,但是hash中包含了salt(hash产生过程:先随机生成salt,salt跟password进行hash)
  • 校验时,从hash中取出salt,salt跟password进行hash,得到的结果跟数据库中提取的的hash进行比对返回Boolean类型:true/false

Bcrypt与MD5进行对比:

  • 首先MD5加密后存储为32位,Bcrypt为60位。
  • 相对来说BCrypt比MD5更安全,但是加密更慢。(简单MD5加密后密码一样,数据库当中不同用户存储了相同的密码值,那么当知道其中一个用户的密码后就可以窥探出相同密码的用户,BCrypt加密每次生成的密文是不一样的。如:10dH/eH7tuzPCkaFSfm44QXePkTxbfuhguLwC.hPmZ2Sp81bcdWbL1W 即使得到了原文密码,破译其他用户密码的机率是很小的)。
  • Bcrypt加密有利也有弊,优点是安全性较高,弊端就是如果存储量比较大,性能消耗也是非常大的。但是用户登录注册并不是一个高并发的接口,所以影响并不会特别大。

3.2.5. JWT实现鉴权

相比Session 的以下缺点,JWT(Json Web Token)有很大优势,本次鉴权方案采用JWT。

3.2.5.1. Session的缺点
    1. cookie + session 在跨域场景表现不好。
    1. 如果是分布式部署, 需要做多机共享 session 机制。
    1. 基于 cookie 的机制很容易被 CSRF。
    1. 查询 session 信息可能会有数据库查询操作
3.2.5.2. JWT 工作原理
  • 浏览器端通过 POST 请求传递用户名和密码给服务端, 服务端校验后, 如果成功将用户 id 等其他信息作为 jwt 的有效载荷和头部进行 base64 编码之后形成了一个 jwt, 这段 jwt 就像以点分割的乱码字符串。
  • 然后后端将这个字符串作为登录成功的返回结果和 200 状态码返回给前端, 前端将其保存在 localstorage 和 sessionstorage 中, 每次请求都把这个 jwt 字符串作为 http 头里的 Authorization 加上 Bearer 和 jwt 字符串, 发送给后端, 后端检查是否存在和有效性(例如检查签名正确与否, 令牌过期与否等), 验证通过后后端使用 jwt 中包含的用户信息, 这段用户信息保存在 jwt 的有效载荷中, 验证解密后就拿到用户信息, 进行其他业务逻辑, 返回业务结果。
  • 退出登录的时候就删除这段 jwt字符串即可。
JWT 工作原理
3.2.5.3. JWT vs. Session

可拓展性

session 多以redis,数据库等保存在服务器中, 水平拓展方案下需要创建一个独立中心的存储。所以 jwt 要比 session 要好一点, jwt 可以无缝接入拓展, 因为基于token 令牌的验证是无状态的, 所以不需要在 session 中存储用户信息。

安全性

对于XSS跨站脚本攻击. js 可以修改 JWT, 因为 JWT 通常放在 localstorage 或 cookie 中, js 可以修改这些变量, 所以也可以修改 JWT, 此时就会出现 xss 攻击, 比如坏人把代码注入页面中.
如何防范呢?可以通过签名, 加密两种方式. 还有不要把敏感信息放在 jwt 中, 防止密钥泄露导致信息泄露。

RESTful API 方面

RESTful 架构要求程序应该是无状态的。因此 session 这种有状态的认证方式, 显然违反了 RESTful 的基本限制。

性能

jwt 性能不太好。因为在客户端向服务端发出请求的时候, 可能有大量的用户信息在 jwt 中, 那么每个 http 请求都产生大量的开销, 而 session 只用少量的开销, 因为 session-id 比较小. jwt 是json, 而且包含了完整的信息, 所以可能是它的好几倍大. jwt 是空间换时间,完整信息都在字符串里, 不需要查询。而 session的缺点是每一个请求都要在服务器上查找 session, 因为拿到的是 session-id, 没有完整的信息, 要用 id 查完整信息. 所以要消耗性能。

有效期

无状态是JWT的特点,JWT是一次性的。想修改里面的内容,比如token续期,就必须签发一个新的JWT。最简单的一种方式是每次请求刷新JWT,即每个HTTP请求都返回一个新的JWT。这个方法不仅暴力不优雅,而且每次请求都要做JWT的加密解密,会带来性能问题。另一种方法是在redis中单独为每个JWT设置过期时间,每次访问时刷新JWT的过期时间,本文采用方法二。

3.3. 系统依赖关系

  • 前端实现注册、登录、会员中心等页面功能,同时调用后端API进行功能交互逻辑;
  • 后台系统采用SpringBoot+Mybatis框架实现注册、登录等逻辑处理;
  • MySQL的关系数据库存储用户的账号、密码、用户信息;
  • Redis保存图片验证码和校验,同时保存失效后的JWT token。
应用系统依赖关系图.png

3.4. 领域模型

3.4.1. MySQL数据库模型

MySQL数据库模型

表DDL如下,user表维护用户基本信息,local_auth支持账密、手机号、邮箱验证码等多种本地实现的登录方式,oauth支持微信、QQ、微博等开放平台的第三方登录。

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类型,用户名、手机号码、邮箱',
  `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 '第三方平台id',
  `oauth_type` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '第三方平台标识(qq、wechat、weibo)',
  `access_token` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '第三方获取的access_token,校验使用',
  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模型

  • 账密登录的图片验证码校验通过Redis 的key-value的String实现,支持set/get/del/getExpire等。
Redis K-V模型

3.4.3. 代码结构UML

  • 展示层的Controller 通过聚合引入业务层的Service服务;
  • 业务层登录和注册的验证码和密码校验通过接口 VerificationService 来继承 账密、手机号、邮箱地址等多种方式的快速扩展。
登录注册登出UML

3.4.4. 状态机

不涉及

4. 业务用例分析

4.1. 账密注册

4.1.1. 用例描述

支持账号和密码注册,使用图片验证码防止批量注册。

4.1.2. 接口说明

4.1.2.1. 获取图片验证码接口说明
  • 获取图片验证码API: /api/code/genImageCode, Class/Method: VerificationCodeController#genImageCode
  • 不能鉴权,GET请求返回Content-Type: image/jpeg,前端直接展示图片。
4.1.2.2. 获取公钥接口说明
  • 获取公钥API: /api/account/getPublicKey, Class/Method: AccountController#getPublicKey

输入参数:
无入参

输出参数:

参数名称 类型 是否必传 说明
code Integer 结果处理码,200成功,其他code表示多种失败场景
message String 结果码的文案描述
data String 公钥字符串

错误码:

code错误码 对客文案 解释
550 系统异常 系统处理未知异常

响应结果样例:

{
    "code": 200,
    "message": "获取公钥成功",
    "data": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf5nUMwxDdYiVtfpYCfkNzxuh5ASHoRLpCQjdWoZmnpaBOK9pwPT3knhLGXYTLMUZNRMgvn81dzJz4HCgNVH1m8tYtvhdwOUSw4V82LcpwYVE2mnWPkrmf3uSy+kzxWZnZjgSptk7YmaHQByfgv+ZyJKPKkmGLXclrhmxqsl5alQIDAQAB"
}
4.1.2.3. 账密注册接口说明
  • 注册API: /api/account/register , Class/Method:AccountController#register

输入参数:

参数名称 类型 是否必传 说明
loginId String 登录账号,可能是账号、手机号、邮箱地址
loginType String 登录类型,username/mobile/email
password String 公钥加密的用户密码
verificationCode String 图片码 or 短信验证码 or 邮箱验证码

入参样例

{
    "loginId": "aa123456",
    "loginType": "username",
    "password": "ewD+yhHSWpmqfbh/zbLKGEu70rJWdF8Fo6c4roajzUUhk8DqKmcrAhzh+NokKMUw3TZhN4ejo7huXFsIUuUy5D6AY1SrqkFTh5mmsQjyJ3UsCAuQZSYwNWVpwjbl/XZDbq1BMeyA0nD1hIHcZY5dqEqOqu3P4bN5WdZa1nMLM64=",
    "verificationCode": "GSMB"
}

输出参数:

参数名称 类型 是否必传 说明
code Integer 结果处理码,200成功,其他code表示多种失败场景
message String 结果码的文案描述
data UserResponseDTO 公钥字符串

UserResponseDTO

参数名称 类型 是否必传 说明
userId Long user id
nickname String 用户昵称
token String 鉴权token

响应结果样例:

{"code":200,"data":{"nickname":"wechat_abc123456","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiNiIsImYzZDgwNDYxLTRlMDItNDRjMy05NzhlLWM3ZDQ4MGVhNGViMSJdLCJleHAiOjE2MjM2NTY1MzIsImlhdCI6MTYyMzU3MDEzMn0.mYo0Tk9Pd0E4xYP0SOWKAbuugg2ao5pmIbvCOx-ewQM","userId":6},"message":"登录成功"} 

错误码:

code错误码 对客文案 解释
550 系统异常 系统处理未知异常
452 用户已存在,不能注册 数据库已存在该loginId
551 注册失败 插入user入库失败

4.1.3. 外部依赖接口

  • MySQL: insert/update/select
  • Redis:set/get/del

4.1.4. 业务流程说明

4.1.4.1. 交互时序图
  • 前端获取服务端的公钥加密 账户密码,防止明文传输被窃取,更加安全办法再启用HTTPS防止窃听和篡改加密的密码。
  • 用户提交账密时,前端账号长度和密码长度,要求密码必须大小写+数字,后端二次校验。
  • 不能明文密码入库,防止密码泄露,通过加盐hash后入库,再次防止彩虹表等方式的暴力破解。
  • 提交注册的账号密码需要输入验证码,防止机器批量注册账号或者破解密码。
账密注册时序图

4.1.5. 非功能点分析

4.1.5.1. 幂等性设计
  • 1、第一次校验 图片验证码成功,删除redis验证码,如果重复提交验证码,系统报错,防止重复注册;
  • 2、注册账号在数据库的表是唯一键,不能重复插入,初步达到幂等效果。
  • 3、更好的方案是token机制,防止页面重复提交,但是囿于工作量,本文暂未实现。
4.1.5.2. 安全性分析

SQL注入攻击

  • SQL注入会造成拖库,原因绝大多数是直接使用输入参数拼接SQL语句造成,防范方法:使用预编译语句(PreparedStatement):可以加速SQL执行,也可以防止SQL注入。

数据库权限配置

  • 防止越权入侵服务器,同时也要安装入侵检测系统,监控告警。

公私钥保存好

  • 防止密码加密的公私钥泄露导致用户密码被破解泄露。

4.2. 账密登录

4.2.1. 用例描述

支持账号和密码登录,使用图片验证码防止登录密码的暴力破解。

4.2.2. 接口说明

4.2.2.1. 获取图片验证码接口说明
  • 同 4.1.2.1
4.2.2.2. 获取公钥接口说明
  • 同4.1.2.2
4.2.2.3. 账密登录接口说明
  • 注册API: /api/account/login, Class/Method:AccountController#login

输入参数:

参数名称 类型 是否必传 说明
loginId String 登录账号,可能是账号、手机号、邮箱地址
loginType String 登录类型,username/mobile/email
password String 公钥加密的用户密码
verificationCode String 图片码 or 短信验证码 or 邮箱验证码

入参样例

{
    "loginId": "aa123456",
    "loginType": "username",
    "password": "ewD+yhHSWpmqfbh/zbLKGEu70rJWdF8Fo6c4roajzUUhk8DqKmcrAhzh+NokKMUw3TZhN4ejo7huXFsIUuUy5D6AY1SrqkFTh5mmsQjyJ3UsCAuQZSYwNWVpwjbl/XZDbq1BMeyA0nD1hIHcZY5dqEqOqu3P4bN5WdZa1nMLM64=",
    "verificationCode": "GSMB"
}

输出参数:

参数名称 类型 是否必传 说明
code Integer 结果处理码,200成功,其他code表示多种失败场景
message String 结果码的文案描述
data UserResponseDTO 公钥字符串

UserResponseDTO

参数名称 类型 是否必传 说明
userId Long user id
nickname String 用户昵称
token String 鉴权token

响应结果样例:

{"code":200,"data":{"nickname":"wechat_abc123456","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiNiIsImYzZDgwNDYxLTRlMDItNDRjMy05NzhlLWM3ZDQ4MGVhNGViMSJdLCJleHAiOjE2MjM2NTY1MzIsImlhdCI6MTYyMzU3MDEzMn0.mYo0Tk9Pd0E4xYP0SOWKAbuugg2ao5pmIbvCOx-ewQM","userId":6},"message":"登录成功"} 

错误码:

code错误码 对客文案 解释
550 系统异常 系统处理未知异常
452 用户已存在,不能注册 数据库已存在该loginId
551 注册失败 插入user入库失败

4.2.3. 外部依赖接口

  • MySQL: insert/update/select
  • Redis:set/get/del

4.2.4. 业务流程说明

4.2.4.1. 交互时序图
  • 前端获取服务端的公钥加密 账户密码,防止明文传输被窃取,更加安全办法再启用HTTPS防止窃听和篡改加密的密码。
  • 用户提交账密时,前端账号长度和密码长度,要求密码必须大小写+数字,后端二次校验。
  • 不能明文密码入库,防止密码泄露,通过加盐hash后入库,再次防止彩虹表等方式的暴力破解。
  • 提交登录的账号密码需要输入验证码,防止机器批量注册账号或者破解密码。
账密登录时序图

4.2.5. 非功能点分析

4.2.5.1. 幂等性设计
  • 登录接口主要是校验密码和验证码,无幂等性风险。
4.2.5.2. 安全性分析
  • 同 4.2.5.1. 的安全性分析。

4.3. 鉴权

4.3.1. 用例描述

没有在Controller方法打注解@PassToken,一律鉴权jwt token。

4.3.2. 接口说明

无对外API

4.3.3. 外部依赖接口

  • MySQL: insert/update/select
  • Redis:set/get/del

4.3.4. 业务流程说明

4.3.4.1. JWT组成

JWT 由三部分组成,分别是 header(头部),payload(载荷),signature(签证) 这三部分以小数点连接起来。

例如使用名为 jwt-token 的cookie来存储 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
  • 服务端根据header和payload计算出 signature,和要校验的JWT中的 signature 部分进行对比,如果 signature 部分相等则是一个有效的 JWT。
4.3.4.2. 交互时序图
  • 使用SpringBoot拦截器技术 AuthenticationInterceptor 来实现;
  • 在redis中单独为每个JWT设置过期时间,每次访问时如果过期时间短于30分钟,刷新JWT的过期时间。
鉴权时序图

4.3.5. 非功能点分析

4.3.5.1. 幂等性设计
  • 不涉及
4.3.5.2. 安全性分析
  • 不要把敏感信息放在 jwt 中。
  • 防止密钥泄露导致信息泄露。

4.4. 退出登录

4.4.1. 用例描述

支持用户主动登出账号,系统清除登录态token。

4.4.2. 接口说明

4.4.2.1. 登出接口说明
  • 登出API: /api/account/logout, Class/Method:AccountController#logout

输入参数:
无入参

输出参数:

参数名称 类型 是否必传 说明
code Integer 结果处理码,200成功,其他code表示多种失败场景
message String 结果码的文案描述

响应结果样例:

{"code":200,"message":"登出成功"} 

错误码:

code错误码 对客文案 解释
550 系统异常 系统处理未知异常

4.4.3. 外部依赖接口

  • Redis:set/get/del

4.4.4. 业务流程说明

4.4.4.1. 交互时序图
  • 登出用户需要到redis清除userId保存的jwt token。
退出登录时序图

4.4.5. 非功能点分析

4.4.5.1. 幂等性设计
  • 退出登录会清除redis的key,支持幂等性。
4.4.5.2. 安全性分析
  • 登出用户需要到redis清除userId保存的jwt token。

5. 配置类专项分析

5.1. 秘钥配置

  • 用户密码的加密公私钥需要安全保存,比如阿里采用KMI系统保存;
  • JWT Token生成的秘钥也要安全保存。

5.2. 数据库链接配置

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数据库索引(默认为0)
    host: 127.0.0.1  # Redis服务器地址
    port: 6379         # Redis服务器连接端口
    password:    # Redis服务器连接密码(默认为空)
    max-wait: 30000    # 连接池最大阻塞等待时间(使用负值表示没有限制)
    max-active: 100   # 连接池最大连接数(使用负值表示没有限制)
    max-idle: 20     # 连接池中的最大空闲连接
    min-idle: 0     # 连接池中的最小空闲连接
    timeout: 5000  # 连接超时时间(毫秒)

6. 兼容性分析

全部新增功能,无兼容性风险。

7. 对周边系统影响分析

新增功能,无原有功能影响。

8. 后续计划

8.1. 运营

  • 注册送红包,定期登录送积分,拉新促活。
  • 开放给更多公司的业务线使用,作为通用的注册登录平台,用户一个账号,多个业务使用。

8.2. 功能增强

  • 支持主动修改密码 和 忘记密码的充值密码;
  • 手机号码、邮箱、第三方平台登录。

8.3. 安全加固

  • 异地登录的安全风险管控;
  • 登录设备的更换风险;
  • 限制用户当日密码登录次数,防止攻击者定向破解该用户的密码;
  • 引入高性能设备防止DDoS攻击;

8.4. 分布式系统

  • 入口负载均衡,比如引入Nginx;
  • 增加熔断、限流、降级等分布式管控能力;
  • 分库分表;
  • 异步消费设计:登录注册等操作后,异步记录用户操作日志,通知下游发积分或者红包等;
  • 灵活多样的监控告警:业务层、系统层、机器性能等。

推荐阅读更多精彩内容