文章目录

  • 1. 功能列表
  • 2. 多种模式的token方案登录流程
  • 3. token方案解析
  • 4. db设计


1. 功能列表

1) 支持多种登录模式,如账号密码登录、微信登录、支付宝登录等。
2) 支持单设备在线,因为在每次登录后会重新刷新token ,该用户存在redis里的token也会被刷新掉,更换设备后,先check_token,token不相等就意味着失效,然后前端返回登录页面。

2. 多种模式的token方案登录流程

redis存储bety Redis存储token每次都一样吗_redis存储bety

3. token方案解析

  1. 将 时间戳+ 用户id 用md5的方式加密得到token作为key 存放到 redis服务器里。value为 用户id或者其他信息, 后续会使用该信息,比如check_token的时候,根据token获取用户信息。
2) 用户每次登录后会产生新的token,并设置失效时间存放到redis里。访问之前需要进行Checktoken,如果前端包含了此token,那么就返回True,不需要再次登录, 如果不包含此token,那么就返回False,前端就需要重新登录以获取新的token,然后前端就可以用新的token来访问系统,只需要在请求头里携带token就可以访问到系统,对于需要验证的请求那么就可以加上auth,通过比对token,相等就通过,不同就拦截。另外django获取请求头的方式前面需要添加:

HTTP_ ,比如我传的请求头是 user-token, 那么在获取的时候我们应该用HTTP_USER_TOKEN。 django会自动将请求头用"_" 进行拼接。

def auth(self, request):
    user_token = request.META.get("HTTP_USER_TOKEN")
    if user_token is None:
        raise AuthException
    conn = redis_util.get_redis_connection()
    if not conn.exists(user_token):
        raise AuthException
  1. 在登录前,前端一定要Check_token,如果通过此token能够获取到用户信息,那么就直接登录成功,如果不能返回验证错误AuthException, 那么表示账号在其他地方登录被挤下线或者是登录已经失效, 这是无状态的登录, 另外如果根据返回的用户信息中包含了没有验证身份,那么提示用户进行手机号绑定身份验证。如果是账号密码登录,那么就不需要再进行身份验证,因为账号就是手机号。
  2. check_token验证无效的token后,如果使用三方微信或支付宝登录,我们需要判断是否是首次登录,如果是首次登录,那么返回首次登录标志,前端跳转至用户完善信息页面,如果不是首次登录,那么页面就直接进入到系统首页。

    需要注意的是:
  3. 三方登录用户(微信、支付宝或其他),首次登录需要绑定手机号才能进行后续操作。
  4. 如果没有绑定手机号退出了,下次进来得提示该用户绑定手机号才能进行后续操作。
  5. 三方登录用户绑定手机号,如果未绑定手机号就退出,那么就需要提醒再次绑定,需要验证验证码是否正确。解决方法: 可以在填写手机验证后,在登录表里将validation 值设置为1, 那么再获取用户信息(check_token)的时候将值进行返回,这样前端就不会再去跳转到验证页面,如果返回的为0,表示没有验证手机号,前端跳转至验证页面。
  6. 如果绑定的用户手机号已经存在于user表,那么就先检查是否绑定其他账号,如果绑定了,那么就 返回该账号已经绑定。
    5. 如果没有绑定其他账号就将该手机号对应的user表的id更新到login表。
    6. 如果该手机号没注册,那么就相当于先注册,然后再绑定。
    7. 生成token和刷新token的方法如下
import time
import base64
import hmac
from school_edu_app.redis import redis_util
import hashlib
from HttpResult import AuthException
from school_edu_app.models import SysUser, SysLogin
from Constant import token_exp_time


def md5(key):
    md5_key = hashlib.md5()
    md5_key.update(key.encode())
    return md5_key.hexdigest()


# 根据手机号和角色清除token
def del_token(key, role):
    conn = redis_util.get_redis_connection()
    user = SysUser.objects.filter(phone=key, role=role).values("id").first()
    login = SysLogin.objects.filter(user__id=user["id"])
    for i in login:
        access_token = i.access_token
        if conn.exists(access_token):
            conn.delete(access_token)
        conn.close()
        i.access_token = ""
        i.save()


# 每次登录会生成新的token, 根据用户手机号或user_id生成token,同时设置失效时间存放到redis里
def generate_token(key):
    conn = redis_util.get_redis_connection()
    ts_str = str(time.time()) + "-" + str(key)
    token = md5(ts_str)
    conn.setex(token, token_exp_time, key)
    conn.close()
    return token

# 获取用户时,刷新token时效,延长token使用时间
def get_user_by_token(token):
    conn = redis_util.get_redis_connection()
    user_id = conn.get(token)
    if user_id is not None:
        user_id = user_id.decode("utf-8")
    else:
        raise AuthException("登录失效!")
    # 刷新token时效
    conn.setex(token, token_exp_time, user_id)
    conn.close()
    return user_id

测试接口: http://localhost:8006/app/check_token/10459db2ab26c0e975f9a461efbfae65

redis存储bety Redis存储token每次都一样吗_支付宝微信登录_02


返回结果:

1. 在登录前前端一定要check_token。

2.如果check_token有返回用户信息,表示token有效,如果返回的data为null,那么表示该token无效。

3. 如果验证了手机号,那么validation为True,没有验证手机号就返回False,前端根据此标志是否进行三方新用户手机号验证绑定。

{
    "code": 0,
    "message": "OK",
    "data": {
        "id": 2,
        "description": null,
        "enabled": true,
        "name": "张三",
        "mail": null,
        "gender": null,
        "age": null,
        "role": 2,
        "phone": "15394737178",
        "validation": true
    },
    "elapsed": 324
}

4. db设计

后台只需要两个表,一个sys_user表,一个sys_login表, login表通过user_id与user表进行关联。
sys_user:

/*
 Navicat Premium Data Transfer

 Source Server Type    : MySQL
 Source Server Version : 50733
 Source Schema         : my_school

 Target Server Type    : MySQL
 Target Server Version : 50733
 File Encoding         : 65001

 Date: 01/03/2021 17:55:07
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `description` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `enabled` tinyint(1) NOT NULL,
  `create_datetime` datetime(6) NULL,
  `update_datetime` datetime(6) NULL,
  `delete_datetime` datetime(6) NULL DEFAULT NULL,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `mail` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `gender` int(11) NULL DEFAULT NULL,
  `age` int(11) NULL DEFAULT NULL,
  `role` int(11) NOT NULL,
  `phone` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `pwd` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `birthday` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `last_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `phone`(`phone`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user`(`id`, `description`, `enabled`, `create_datetime`, `update_datetime`, `delete_datetime`, `name`, `mail`, `gender`, `age`, `role`, `phone`, `pwd`, `birthday`, `last_name`) VALUES (1, NULL, 1, '2022-04-07 09:07:49.000000', NULL, NULL, 'zhangsan', '123456146@qq.com', 1, 23, 1, '', 'ioaseydf12312lsajfda', NULL, NULL);

sys_login:
identifier: 为三方应用的唯一序列串,授权后生成,后台只需要接收即可。
access_token: 为后续访问系统的token, 在redis里也有一个备份。

/*
 Navicat Premium Data Transfer
 Source Server Type    : MySQL
 Source Server Version : 50733
 Target Server Type    : MySQL
 Target Server Version : 50733
 File Encoding         : 65001

 Date: 01/03/2021 17:55:16
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_login
-- ----------------------------
DROP TABLE IF EXISTS `sys_login`;
CREATE TABLE `sys_login`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `description` varchar(5000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `enabled` tinyint(1) NOT NULL,
  `create_datetime` datetime(6) NULL,
  `update_datetime` datetime(6) NULL,
  `delete_datetime` datetime(6) NULL DEFAULT NULL,
  `login_type` int(11) NOT NULL,
  `identifier` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `validation` tinyint(1) NOT NULL,
  `user_id` int(11) NULL DEFAULT NULL,
  `access_token` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `sys_login_user_id_9bda9a31_fk_sys_user_id`(`user_id`) USING BTREE,
  CONSTRAINT `sys_login_user_id_9bda9a31_fk_sys_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_login
-- ----------------------------
INSERT INTO `sys_login` VALUES (3, '', 1, '2021-02-26 16:21:57.491866', '2021-03-01 17:08:04.934626', NULL, 0, '', 1, 2, 'd52c2018b21fd83658b777d5d08479a5');
INSERT INTO `sys_login` VALUES (4, '', 1, '2021-02-26 16:23:29.131366', '2021-02-26 17:16:45.380937', NULL, 1, 'wx123456', 1, 1, '6f7f63eb6bdb01a5030abf8fed5f74fb');
INSERT INTO `sys_login` VALUES (5, '', 1, '2021-02-26 17:32:17.306361', '2021-03-01 17:06:15.010139', NULL, 0, '', 1, 1, 'b6470404c2813672c9ae037d89d44526');
INSERT INTO `sys_login` VALUES (6, '', 1, '2021-03-01 09:01:56.651269', '2021-03-01 10:24:07.342097', NULL, 3, 'zdb123456', 1, 2, '');

SET FOREIGN_KEY_CHECKS = 1;