- 具备注册登录功能
- 一个⽤户只能在一个设备上登录,切换终端登录时,其他已登录的终端会被踢出
- 用户长时间未使用功能,需要重新登录,而经常使用则会延长登录状态
客户端下载链接:MyLogin
- 客户端用Android实现,采用Djinni实现跨平台开发,业务逻辑在C++层实现
- 服务端用C++实现,缓存使用Redis,数据库使用MySQL
- 使用GPRC进行远程通信,生成非对称密钥,使用SSL进行数据传输加密
- 使用Bazel构建后台项目,以及依赖管理
- 使用Docker部署项目,并使用Docker Compose编排容器
客户端 | 服务端 |
接口名 | 接口方法 |
---|---|
用户登录 | requestUserLogin |
用户注册 | requestUserSign |
退出登录 | requestLogout |
检查用户在线状态 | checkConnect |
刷新用户Token | refreshToken |
requestUserLogin
参数名 | 必选 | 类型 | 说明 |
---|---|---|---|
account | true | string | 用户账号 |
password | true | string | 用户密码 |
参数名 | 类型 | 说明 |
---|---|---|
token | string | 用户Token |
refresh_token | string | 用来刷新Token |
{
"code": 0,
"msg": "",
"data": {
"token":"",
"refresh_token":""
}
}
requestUserSign
参数名 | 必选 | 类型 | 说明 |
---|---|---|---|
account | true | string | 用户账号 |
password | true | string | 用户密码 |
参数名 | 类型 | 说明 |
---|---|---|
token | string | 用户Token |
refresh_token | string | 用来刷新Token |
{
"code": 0,
"msg": "",
"data": {
"token":"",
"refresh_token":""
}
}
requestLogout
参数名 | 必选 | 类型 | 说明 |
---|---|---|---|
token | true | string | 用户Token |
无
{
"code": 0,
"msg": "",
"data": ""
}
checkConnect
参数名 | 必选 | 类型 | 说明 |
---|---|---|---|
token | true | string | 用户Token |
无
{
"code": 0,
"msg": "",
"data": ""
}
refreshToken
参数名 | 必选 | 类型 | 说明 |
---|---|---|---|
token | true | string | 用户Token |
refresh_token | true | string | 用来刷新Token |
参数名 | 类型 | 说明 |
---|---|---|
token | string | 用户Token |
refresh_token | string | 用来刷新Token |
{
"code": 0,
"msg": "",
"data": {
"token":"",
"refresh_token":""
}
}
公共错误码范围 0 ~ 99
错误代码 | 错误类型 | 描述 |
---|---|---|
0 | 请求成功 | |
1 | 请求参数非法 | 后台接口接收到的参数不合法,如账号里包含非法字符,密码不够位数 |
2 | 返回数据非法 | 返回的数据中存在敏感字符 |
业务逻辑错误,7位数字
组成规则:[模块][接口][具体错误]
如:1xxxzzz 账号模块,2xxxzzz 卡包模块, ......
错误代码 | 错误类型 | 描述 | 备注 |
---|---|---|---|
1001xxx | 用户登录接口相关 | ||
1001001 | 账号不存在 | 根据接口传来的账号,查询数据库没有找到对应的账号信息 | |
1001002 | 密码初始化失败 | 对接口传来的密码,进行加密过程失败 | |
1001003 | 用户密码错误 | 对接口传来的密码,与数据库中该用户的密码匹对失败 | |
1001004 | 用户Token更新失败 | 根据账号与UID生成的Token,更新到Redis失败 | |
1002xxx | 用户注册接口相关 | ||
1002001 | 账号已经存在 | 根据接口传来的账号,查询数据库发现该账号已经存在 | |
1002002 | 密码初始化失败 | 对接口传来的密码,进行加密过程失败 | |
1002003 | 账号创建失败 | 根据账号信息新增账号到数据库失败 | |
1002004 | 账号信息获取失败 | 根据账号查询数据库对应账号信息失败 | |
1002005 | 账号会话信息创建失败 | 根据账号与UID生成的Token,更新到Redis失败 | |
1003xxx | 用户退出登录接口相关 | ||
1003001 | Token格式错误 | 接口传来的Token格式不正确,属于非法Token | |
1003002 | 账号会话信息更新失败 | 更新Token信息到Redis失败 | |
1003003 | 账号Token已失效 | 用户Token失效(过期) | |
1004xxx | 检查在线状态接口 | ||
1004001 | Token格式错误 | 接口传来的Token格式不正确,属于非法Token | |
1004002 | Token已经失效 | 用户Token失效(过期),需要客户端执行刷新Token操作 | |
1004003 | Token校验失败 | 根据Token与数据库中Token进行比对,判断该设备不在线 | |
1005xxx | 刷新用户Token接口 | ||
1005001 | Token格式错误 | 接口传来的Token格式不正确,属于非法Token | |
1005002 | RefreshToken格式错误 | 接口传来的RefreshToken格式不正确 | |
1005003 | RefreshToken不对应 | 根据UID从Redis取出RefreshToken进行比对不相等 | |
1005004 | RefreshToken已失效 | 用户RefreshToken失效(过期),需要重新登录 | |
1005005 | 账号会话信息创建失败 | 根据账号与UID生成的Token,更新到Redis失败 | |
1005006 | Token信息有误 | 接口传来的Token与RefreshToken中的UID不一致 |
接口使用SSL加密认证,通过OpenSSL生成对应的密钥文件,客户端保存公钥,在Channel中加上公钥信息,服务器端通过私钥认证接口合法性,保证传输安全
- 生成16位由数字、大小写字母组成的随机盐
- 通过SHA256消息摘要算法对账号进行计算,获得摘要信息
- 将2中获得的摘要信息作为盐,与随机盐、密码拼接,获得加密前信息
- 将3中获得的信息,进行SHA256消息摘要计算,获得加密后的64位,用16进制表示的密码字符串
- 通过用户传来的账号和密码,用“注册流程”加密规则获得加密后密码字符串
- 与数据库中用户的密码字符串比对,一致则登录成功,反之则登录失败。
-
加密前Token由用户信息和当前登录信息组成
加密前Token = [用户账号]:[用户UID]:[随机6位字符串]:[登录时间戳(秒)]:[过期时间戳(秒)]
例如:"13533192331:2:172649:1552031125:1557301525"
-
将加密前Token进行对称密钥算法AES计算,获得加密后Token
Token = AES(加密前Token)
日志文件按日期分割,例如 2019-3-5.log
消息类型 | 含义 |
---|---|
INFO | 相关INFO信息可以让运维保留下来,可用来分析,比如接口访问信息 |
DEBUG | 相关DEBUG信息提供给开发来用调试程序 |
WARN | 相关WARN信息提示程序没有按照预期的运行,但不会影响到整体正常运行,需要被FIX |
ERROR | 相关ERROR信息提示程序出现了严重错误,影响到系统的正常运行,必须被FIX |
分三个等级
- 初级密码
- 总共6-18位
- 由任意数字或字母组成
- 中级密码(当前)
- 总共6-18位
- 至少1个数字字符
- 至少1个小写字母
- 至少1个大写字母
- 其他可以是任意字符
- 高级密码
- 总共6-18位
- 至少1个数字字符
- 至少1个小写字母
- 至少1个大写字母
- 至少1个特殊字符
- 其他可以是任意字符
-
入参校验
根据参数的业务类型规定数据的格式,进行校验,不匹配则返回错误码1,防止SQL注入。
-
反参校验
返回数据前对数据进行敏感字符过滤,存在则返回错误码2,防止XSS攻击返回非法语句,SSRF攻击返回系统信息等。
在执行SQL语句前,对传入的参数进行敏感字符过滤,确保参数内容符合预期,防止SQL注入。
使用存储过程进行参数化查询和数据的插入,防止SQL注入。
使用MySQL作为数据库
表名 | 字段 | 类型 | 备注 |
---|---|---|---|
user_account | |||
ID | INTEGER PRIMARY KEY | 用户UID | |
ACCOUNT | CHAR | 用户账号 | |
PASSWORD | CHAR | 用户密码 | |
PWD_SALT | CHAR | 参与密码初始化的随机盐 |
使用存储过程封装常用数据库操作
名称 | 入参 | 反参 | 用途 |
---|---|---|---|
queryaccount | 用户账号 | [用户UID,用户账号,用户密码,密码随机盐] | 根据用户账号(手机号),获取用户信息,不存在则UID返回-1 |
insertaccount | [用户账号,用户密码,密码随机盐] | 用户UID | 插入一条用户账号信息到数据库,失败则UID返回-1,成功则返回用户UID数据的主键ID |
{
"SERVER_IP_PORT": "0.0.0.0:50051", //服务-IP:端口
"REDIS_IP": "47.92.xxx.xx", //Redis-IP
"REDIS_POST": 6379, //Redis-端口
"MYSQL_HOST": "47.92.xxx.xx", //MySQL-IP
"MYSQL_POST": 3306, //MySQL-端口
"MYSQL_CHARSET": "UTF8", //MySQL-编码
"MYSQL_USER": "melon", //MySQL-登入用户名
"MYSQL_PASSWORD": "xxxxxxxxxx", //MySQL-登入密码
"MYSQL_DB_NAME": "user_center", //MySQL-登入库名
"SSL_PATH_KEY": "source/pem/server.key", //SSL-KEY
"SSL_PATH_CERT": "source/pem/server.crt", //SSL-密钥
"TOKEN_AES_KEY": "$L&^E*Usd9k!Ld4%", //参与Token加密AES的密钥
"IS_CONSOLE_DEBUG_INFO": true, //决定是否打印和记录DEBUG信息,生产环境下可关闭
"TOKEN_TIMEOUT_DAY": 15, //Token的有效时间(天)
"REFRESH_TOKEN_TIMEOUT_DAY": 30 //RefreshToken的有效时间(天)
}
模块 | 版本号 | 备注 | |
---|---|---|---|
Android端 | |||
Android Studio | 3.2.1 | Android IDE | |
Gradle | 4.6 | ||
Android Plugin Version | 3.2.1 | ||
GRPC-C++ | 1.18.0 | ||
Protobuffer | 3.6.1 | ||
后端 | |||
Ubuntu | 18.04-x86_64 | ||
Bazel | 0.20.0 | ||
GRPC-C++ | 1.18.0 | ||
Docker | 18.09.1 | Community | |
Docker Compose | 1.23.2 | ||
系统 | |||
ufw | 0.35 | Ubuntu 防火墙 |
命令 | 备注 |
---|---|
bazel build //source:account_server | 构建目标文件 |
cp ./bazel-bin/source/account_server docker-src/ | 将构建成功的执行文件复制到本地docker目录 |
docker build -t grpcserver:1.0 . | 在根目录,根据Dockerfile编写的规则,生成服务镜像 |
docker-compose up grpcserver | 在根目录,根据docker-compose.yml编写的规则,启动并管理容器 |