网站后台系统设计

2021-06-13  本文已影响0人  大头8086

文档修改历史

版本号 修订人 摘要 日期
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. 商业目标

1.3.2. 系统目标

1.4. 相关文档

2. 业务流程和架构分析

2.1. 整体业务流程

整体业务流程

2.2. 整体业务架构

整体业务架构

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

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

2.3.1. 常见漏洞

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

2.3.3. 漏洞解决方案

2.4. 用例

用例图

3. 系统架构和领域模型

3.1. 系统整体架构

系统架构图.png

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

3.2.1. 开发框架

3.2.2. 数据库技术

3.2.3. 缓存技术

3.2.4. BCrypt密码加密

Bcrypt加密原理:

Bcrypt与MD5进行对比:

3.2.5. JWT实现鉴权

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

3.2.5.1. Session的缺点
3.2.5.2. 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. 系统依赖关系

应用系统依赖关系图.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 K-V模型

3.4.3. 代码结构UML

登录注册登出UML

3.4.4. 状态机

不涉及

4. 业务用例分析

4.1. 账密注册

4.1.1. 用例描述

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

4.1.2. 接口说明

4.1.2.1. 获取图片验证码接口说明
4.1.2.2. 获取公钥接口说明

输入参数:
无入参

输出参数:

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

错误码:

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

响应结果样例:

{
    "code": 200,
    "message": "获取公钥成功",
    "data": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf5nUMwxDdYiVtfpYCfkNzxuh5ASHoRLpCQjdWoZmnpaBOK9pwPT3knhLGXYTLMUZNRMgvn81dzJz4HCgNVH1m8tYtvhdwOUSw4V82LcpwYVE2mnWPkrmf3uSy+kzxWZnZjgSptk7YmaHQByfgv+ZyJKPKkmGLXclrhmxqsl5alQIDAQAB"
}
4.1.2.3. 账密注册接口说明

输入参数:

参数名称 类型 是否必传 说明
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. 外部依赖接口

4.1.4. 业务流程说明

4.1.4.1. 交互时序图
账密注册时序图

4.1.5. 非功能点分析

4.1.5.1. 幂等性设计
4.1.5.2. 安全性分析

SQL注入攻击

数据库权限配置

公私钥保存好

4.2. 账密登录

4.2.1. 用例描述

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

4.2.2. 接口说明

4.2.2.1. 获取图片验证码接口说明
4.2.2.2. 获取公钥接口说明
4.2.2.3. 账密登录接口说明

输入参数:

参数名称 类型 是否必传 说明
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. 外部依赖接口

4.2.4. 业务流程说明

4.2.4.1. 交互时序图
账密登录时序图

4.2.5. 非功能点分析

4.2.5.1. 幂等性设计
4.2.5.2. 安全性分析

4.3. 鉴权

4.3.1. 用例描述

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

4.3.2. 接口说明

无对外API

4.3.3. 外部依赖接口

4.3.4. 业务流程说明

4.3.4.1. JWT组成

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

例如使用名为 jwt-token 的cookie来存储 JWT 例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOlsiMTIzNDU2IiwiMDI5ZDdlNDItZDAxYy00ZjQ4LTk3ZTgtNGE2NzNhZDQ4M2Y4Il0sImV4cCI6MTYyMzYzOTU5NSwiaWF0IjoxNjIzNTUzMTk1fQ.gG7QRGPCnOY0pyRbHPcULwXY0duh2YdIR-HHMFDbM1Q

使用.分割值可以得到三部分组成元素,按照顺序分别为:

header:

{"typ":"JWT","alg":"HS256"}

payload:

{
    "aud": ["123456", "067013d1-9e6e-4fd8-a713-2c2972743541"],
    "exp": 1623639879,
    "iat": 1623553479
}

signature:

4.3.4.2. 交互时序图
鉴权时序图

4.3.5. 非功能点分析

4.3.5.1. 幂等性设计
4.3.5.2. 安全性分析

4.4. 退出登录

4.4.1. 用例描述

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

4.4.2. 接口说明

4.4.2.1. 登出接口说明

输入参数:
无入参

输出参数:

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

响应结果样例:

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

错误码:

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

4.4.3. 外部依赖接口

4.4.4. 业务流程说明

4.4.4.1. 交互时序图
退出登录时序图

4.4.5. 非功能点分析

4.4.5.1. 幂等性设计
4.4.5.2. 安全性分析

5. 配置类专项分析

5.1. 秘钥配置

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. 安全加固

8.4. 分布式系统

上一篇 下一篇

猜你喜欢

热点阅读