一个带有用户系统的应用最基本登录方式是站内账号登录,但这种方式往往不能满足我们的需求。现在的应用基本都有站内账号、email、手机和一堆第三方登录,那么如果需要支持这么多种登录方式,或者还有银行卡登录、身份证登录等等更多的登录方式,我们的数据表应该怎么设计才更合理呢?

需求分析

实现多种登录方式,并且除了站内账号登录方式以外的登录方式,都能够进行绑定和解绑或者更换绑定。

如果按照传统的数据表设计,我们用户表会存储用户的账号和密码等授权相关的字段,类似下面:

id

username

password

nickname

sex

...

1

2

3

4

5

6

id

username

password

nickname

sex

...

但是如果登录方式非常多的情况下,这种数据表结构不再适用。那么应该怎么设计呢?在查阅了一些资料后,本渣渣终于有了一个自我感觉很合理的设计方式。

首先,一个用户不管有多少种登录方式,用户还是只有那一个用户,但登录方式却有多种。这就形成了一对多的关系:一个用户对应多个登录方式。

所以,我们就可以把用户表拆分成2张表,一张表存储用户基本的数据,另一张表存储登录授权相关的数据。我们可以向下面这样设计:

users

id

nickname

sex

age

email

mobile

status

...

1

2

3

4

5

6

7

8

id

nickname

sex

age

email

mobile

status

...

user_auths

id # 自增id

user_id # users表对应的id

identity_type # 身份类型(站内username 邮箱email 手机mobile 或者第三方的qq weibo weixin等等)

identifier # 身份唯一标识(存储唯一标识,比如账号、邮箱、手机号、第三方获取的唯一标识等)

credential # 授权凭证(比如密码 第三方登录的token等)

verified # 是否已经验证(存储 1、0 来区分是否已经验证通过)

1

2

3

4

5

6

id#自增id

user_id#users表对应的id

identity_type#身份类型(站内username邮箱email手机mobile或者第三方的qqweiboweixin等等)

identifier#身份唯一标识(存储唯一标识,比如账号、邮箱、手机号、第三方获取的唯一标识等)

credential#授权凭证(比如密码第三方登录的token等)

verified#是否已经验证(存储1、0来区分是否已经验证通过)

这样我们创建一个用户,首先需要创建一条 users 表的用户基础信息记录和一条或者多条 user_auths 表的授权记录。注意修改密码时也需要同时修改多条 user_auths 记录,保证需要密码的登录方式凭证需要同步更新。而第三方的授权凭证和需要密码的授权凭证则不需要同步。

代码实现

这里我使用 laravel 来实现简单的用户注册、登录、修改密码等操作,仅供参考。

首先创建2张数据表,结构如下:

users

public function up()

{

Schema::create('users', function (Blueprint $table) {

$table->increments('id');

$table->string('nickname', 30)->default('宝宝')->comment('昵称');

$table->string('say')->nullable()->comment('心情寄语');

$table->string('avatar', 50)->default('uploads/user/avatar.jpg')->comment('头像');

$table->string('mobile', 11)->nullable()->comment('手机号码');

$table->string('email', 50)->nullable()->comment('邮箱');

$table->tinyInteger('sex')->default(0)->comment('性别 0女 1男');

$table->tinyInteger('status')->default(1)->comment('状态 1可用 0 不可用');

$table->tinyInteger('is_admin')->default(0)->comment('是否是管理员');

$table->tinyInteger('qq_binding')->default(0)->comment('QQ登录是否绑定');

$table->tinyInteger('weixin_binding')->default(0)->comment('微信登录是否绑定');

$table->tinyInteger('weibo_binding')->default(0)->comment('微博登录是否绑定');

$table->tinyInteger('email_binding')->default(0)->comment('邮箱登录是否绑定');

$table->tinyInteger('phone_binding')->default(0)->comment('手机登录是否绑定');

$table->timestamps();

});

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

publicfunctionup()

{

Schema::create('users',function(Blueprint$table){

$table->increments('id');

$table->string('nickname',30)->default('宝宝')->comment('昵称');

$table->string('say')->nullable()->comment('心情寄语');

$table->string('avatar',50)->default('uploads/user/avatar.jpg')->comment('头像');

$table->string('mobile',11)->nullable()->comment('手机号码');

$table->string('email',50)->nullable()->comment('邮箱');

$table->tinyInteger('sex')->default(0)->comment('性别 0女 1男');

$table->tinyInteger('status')->default(1)->comment('状态 1可用 0 不可用');

$table->tinyInteger('is_admin')->default(0)->comment('是否是管理员');

$table->tinyInteger('qq_binding')->default(0)->comment('QQ登录是否绑定');

$table->tinyInteger('weixin_binding')->default(0)->comment('微信登录是否绑定');

$table->tinyInteger('weibo_binding')->default(0)->comment('微博登录是否绑定');

$table->tinyInteger('email_binding')->default(0)->comment('邮箱登录是否绑定');

$table->tinyInteger('phone_binding')->default(0)->comment('手机登录是否绑定');

$table->timestamps();

});

}

user_auths

public function up()

{

Schema::create('user_auths', function (Blueprint $table) {

$table->increments('id');

$table->integer('user_id')->index()->comment('用户id');

$table->string('identity_type')->comment('登录类型(手机号phone 邮箱email 用户名username)或第三方应用名称(微信weixin 微博weibo 腾讯QQqq等)');

$table->string('identifier')->unique()->index()->comment('标识(手机号 邮箱 用户名或第三方应用的唯一标识)');

$table->string('credential')->nullable()->comment('密码凭证(站内的保存密码,站外的不保存或保存token)');

$table->tinyInteger('verified')->default(0)->comment('是否已经验证');

$table->timestamps();

});

}

1

2

3

4

5

6

7

8

9

10

11

12

publicfunctionup()

{

Schema::create('user_auths',function(Blueprint$table){

$table->increments('id');

$table->integer('user_id')->index()->comment('用户id');

$table->string('identity_type')->comment('登录类型(手机号phone 邮箱email 用户名username)或第三方应用名称(微信weixin 微博weibo 腾讯QQqq等)');

$table->string('identifier')->unique()->index()->comment('标识(手机号 邮箱 用户名或第三方应用的唯一标识)');

$table->string('credential')->nullable()->comment('密码凭证(站内的保存密码,站外的不保存或保存token)');

$table->tinyInteger('verified')->default(0)->comment('是否已经验证');

$table->timestamps();

});

}

实现注册功能,创建站内账号,一个用户 + 一个站内账号登录授权。

public function register(Request $request)

{

// 已经登录则直接跳转

if (Session::has('user')) {

return redirect()->route('admin.index');

}

if ($request->method() === 'GET') {

return view('admin.user.register');

}

// 验证表单

$validator = Validator::make($request->all(), [

'identifier' => ['required', 'between:6,16', 'unique:user_auths'],

'credential' => ['required', 'between:6,16', 'confirmed'],

], [

'identifier.required' => '用户名为必填项',

'identifier.unique' => '用户名已经存在',

'identifier.between' => '用户名长度必须是6-16',

'credential.required' => '密码为必填项',

'credential.between' => '密码长度必须是6-16',

'credential.confirmed' => '两次输入的密码不一致',

]);

if ($validator->fails()) {

return back()->withErrors($validator);

}

// 创建用户

$user = new User();

$user->save();

// 创建授权

$userAuth = new UserAuth();

$userAuth->user_id = $user->id;

$userAuth->identity_type = 'username';

$userAuth->identifier = $request->identifier;

$userAuth->credential = bcrypt($request->credential);

$userAuth->save();

return redirect()->route('admin.login');

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

publicfunctionregister(Request$request)

{

// 已经登录则直接跳转

if(Session::has('user')){

returnredirect()->route('admin.index');

}

if($request->method()==='GET'){

returnview('admin.user.register');

}

// 验证表单

$validator=Validator::make($request->all(),[

'identifier'=>['required','between:6,16','unique:user_auths'],

'credential'=>['required','between:6,16','confirmed'],

],[

'identifier.required'=>'用户名为必填项',

'identifier.unique'=>'用户名已经存在',

'identifier.between'=>'用户名长度必须是6-16',

'credential.required'=>'密码为必填项',

'credential.between'=>'密码长度必须是6-16',

'credential.confirmed'=>'两次输入的密码不一致',

]);

if($validator->fails()){

returnback()->withErrors($validator);

}

// 创建用户

$user=newUser();

$user->save();

// 创建授权

$userAuth=newUserAuth();

$userAuth->user_id=$user->id;

$userAuth->identity_type='username';

$userAuth->identifier=$request->identifier;

$userAuth->credential=bcrypt($request->credential);

$userAuth->save();

returnredirect()->route('admin.login');

}

实现登录,站内账号、邮箱、手机号码登录方式。

public function login(Request $request)

{

// 已经登录则直接跳转

if (Session::has('user')) {

return redirect()->route('admin.index');

}

if ($request->method() === 'GET') {

return view('admin.user.login');

}

// 验证表单

$validator = Validator::make($request->all(), [

'identifier' => ['required', 'exists:user_auths'],

'credential' => ['required', 'between:6,16'],

], [

'identifier.exists' => '用户不存在',

'identifier.required' => '用户名为必填项',

'credential.required' => '密码为必填项',

'credential.between' => '密码长度必须是6-16',

]);

if ($validator->fails()) {

return back()->withErrors($validator);

}

// 查询授权记录 - 查询3种登录方式的授权记录

$userAuth = UserAuth::where('identifier' , $request->identifier)

->whereIn('identity_type', ['username', 'phone', 'email'])

->first();

if (isset($userAuth) && Hash::check($request->credential, $userAuth->credential)) {

// 查询用户表

$user = User::find($userAuth->user_id);

if ($user->status == 0) {

return back()->with('errors', '用户已经被禁用');

}

if ($user->is_admin == 0) {

return back()->with('errors', '普通用户禁止登陆后台');

}

Session::put('user', $user);

return redirect()->route('admin.index');

} else {

return back()->with('errors', '管理员密码错误');

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

publicfunctionlogin(Request$request)

{

// 已经登录则直接跳转

if(Session::has('user')){

returnredirect()->route('admin.index');

}

if($request->method()==='GET'){

returnview('admin.user.login');

}

// 验证表单

$validator=Validator::make($request->all(),[

'identifier'=>['required','exists:user_auths'],

'credential'=>['required','between:6,16'],

],[

'identifier.exists'=>'用户不存在',

'identifier.required'=>'用户名为必填项',

'credential.required'=>'密码为必填项',

'credential.between'=>'密码长度必须是6-16',

]);

if($validator->fails()){

returnback()->withErrors($validator);

}

// 查询授权记录 - 查询3种登录方式的授权记录

$userAuth=UserAuth::where('identifier',$request->identifier)

->whereIn('identity_type',['username','phone','email'])

->first();

if(isset($userAuth)&&Hash::check($request->credential,$userAuth->credential)){

// 查询用户表

$user=User::find($userAuth->user_id);

if($user->status==0){

returnback()->with('errors','用户已经被禁用');

}

if($user->is_admin==0){

returnback()->with('errors','普通用户禁止登陆后台');

}

Session::put('user',$user);

returnredirect()->route('admin.index');

}else{

returnback()->with('errors','管理员密码错误');

}

}

实现修改密码,站内登录、邮箱登录、手机登录同步修改。

public function modifyPassword(Request $request)

{

if ($request->method() === 'GET') {

return view('admin.user.modify');

}

// 验证输入字段

$validator = Validator::make($request->all(), [

'credential' => 'required|between:6,20|confirmed',

], [

'credential.required' => '新密码不能为空!',

'credential.between' => '新密码必须在6-20位之间',

'credential.confirmed' => '新密码和确认密码不一致',

]);

if ($validator->fails()) {

return back()->withErrors($validator);

}

// 判断当前Session里的用户是否还有效

$user = Session::get('user');

if (! isset($user)) {

return redirect()->route('admin.login')->with('errors', '登录超时');

}

// 查询用户权限表,修改密码

$userAuths = UserAuth::where('user_id', $user->id)

->whereIn('identity_type', ['username', 'email', 'phone'])

->get();

if (count($userAuths) && Hash::check($request->credential_o, $userAuths[0]->credential)) {

UserAuth::where('user_id', $user->id)

->whereIn('identity_type', ['username', 'email', 'phone'])

->update(['credential' => bcrypt($request->credential)]);

return back()->with('errors', '修改密码成功!');

}

return back()->with('errors', '原密码错误!');

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

publicfunctionmodifyPassword(Request$request)

{

if($request->method()==='GET'){

returnview('admin.user.modify');

}

// 验证输入字段

$validator=Validator::make($request->all(),[

'credential'=>'required|between:6,20|confirmed',

],[

'credential.required'=>'新密码不能为空!',

'credential.between'=>'新密码必须在6-20位之间',

'credential.confirmed'=>'新密码和确认密码不一致',

]);

if($validator->fails()){

returnback()->withErrors($validator);

}

// 判断当前Session里的用户是否还有效

$user=Session::get('user');

if(!isset($user)){

returnredirect()->route('admin.login')->with('errors','登录超时');

}

// 查询用户权限表,修改密码

$userAuths=UserAuth::where('user_id',$user->id)

->whereIn('identity_type',['username','email','phone'])

->get();

if(count($userAuths)&&Hash::check($request->credential_o,$userAuths[0]->credential)){

UserAuth::where('user_id',$user->id)

->whereIn('identity_type',['username','email','phone'])

->update(['credential'=>bcrypt($request->credential)]);

returnback()->with('errors','修改密码成功!');

}

returnback()->with('errors','原密码错误!');

}

例子中路由相关代码直接无视!如果后期需要新增或删除登录方式,只需要新增或删除 user_auths 表中的记录。如果是判断邮箱、手机是否已经验证,也只是操作 user_auths 表中的 verified 字段即可。

mysql表的设计几种方式_支持多种登录方式的数据表设计 | 六阿哥博客相关推荐

  1. webstorm安装eslint插件_在WebStorm中使用ESLint开发Vue项目 | 六阿哥博客

    对于习惯使用 IntelliJ IDEA 的玩家,使用 WebStorm 来编写前端代码比使用 Visual Studio Code 更得劲,于是开始折腾... 我使用的 WebStorm 版本是 2 ...

  2. wordpress启动_如何通过7个简单步骤正确地启动WordPress博客(2020)

    wordpress启动 Do you want to start a WordPress blog the right way? We know that starting a blog can be ...

  3. 个人博客管理系统_教程 | 一文搭建你的第一个免费专属博客

    点击蓝字关注我 本文将详细介绍利用Github+hexo搭建一个免费.简洁的个人博客,从获取域名到菜单栏.搜索框.评论分享这些必要功能的配置,给自己一个个性化的内容分享平台. -▼- 我建了一个QQ学 ...

  4. 云呐|固定资产盘点中,支持多种盘点方式(资产清查盘点)

    高效盘点是条形码扫描和RFID技术固定资产管理系统的主要特点,但结合用户咨询,云呐发现许多人不知道如何根据条形码或RFID标签实现固定资产管理系统的资产盘点. 处理固定资产管理系统的流程可以协助不了解 ...

  5. 你有没有遇到要实现多种登录方式的场景丫 一起来看看咯 Spring Security 实现多种登录方式,如常规方式外的邮件、手机验证码登录

    你好丫,我是博主宁在春,一起加油吧!!! 不知道, 你在用Spring Security的时候,有没有想过,用它实现多种登录方式勒,这次我的小伙伴就给我提了一些登录方面的需求,需要在原有账号密码登录的 ...

  6. 宝宝起名神器小程序源码_支持多种流量主模式

    2022年马上到了,还不知道怎么给虎宝宝取名字么? 那么这款小程序源码就可以帮到你了,这款小程序支持输入姓氏自动起名. 不满意还可以点击换一换来找到满意的,支持起两个字或者三个字的名字. 另外也给该款 ...

  7. 新动态视频壁纸微信小程序源码_支持多种分类短视频-也有静态壁纸

    这是一款主打动态视频壁纸的一款微信小程序源码,当然啦,里面也是有静态壁纸的. 其实这款小程序也可以说是短视频小程序都可以,该款小程序全采集,另外支持多种流量主!! 下载链接: 新动态视频壁纸微信小程序 ...

  8. mysql多表联查的几种方法_多表联查的几种方式

    有如下两张表 mysql> select * from teacher; +------+-----------+ | t_id | t_name | +------+-----------+ ...

  9. mysql多表查询有几种方法_多表查询有几种方式

    多表查询有3种方式,分别是:1.传统方式,包括左外连接查询,右外连接查询 ,完全外链接查询:2.子查询方式,包括单行查询,多行查询:3.聚合查询方式,包括求和,平均查询,记录总数. 多表查询有3种方式 ...

最新文章

  1. webpack 中的加载器简介||webpack 中加载器的基本使用——1. 打包处理 css 文件 2. 打包处理 less 文件 3.打包处理 scss 文件
  2. mysql删除了密码怎样恢复_window 下如何恢复被删除的mysql root账户及密码(mysql 8.0.17)...
  3. 单样本学习与孪生网络
  4. 仿京东左侧菜单弹出html代码,相仿京东左侧菜单
  5. 使用Maven Jetty插件
  6. java实现递归层次遍历_Java实现二叉树的前序、中序、后序、层序遍历(递归方法)...
  7. oracle userenv(sid),ORACLE USERENV函数
  8. NLP学习—12.Seq2Seq模型与Attention机制
  9. 阿里java工具包_阿里开源的Java诊断工具Arthas(阿尔萨斯)
  10. 业务重点-实现一个简单的手机号码验证
  11. download.js实现下载的基本用法
  12. selenium超详解
  13. 随机森林-科比生涯数据集分析与预测
  14. 用python编写word自动编写离职报告
  15. 5.stm32L476在freeRTOS下使用低功耗
  16. Angular最佳实践之$http-麻雀虽小 五脏俱全
  17. Ignition Gazebo Fortress LTS试用
  18. MySQL 中的 “My” 是什么意思?
  19. PHP将图片和文字合成到一张背景图上
  20. 关键字 - restrict

热门文章

  1. linux 搭建/重启 java项目 复制文件命令
  2. MyBatis相关整理
  3. Tcp三次握手连接梳理
  4. Django知乎问答精选(第一期)
  5. indoor是什么意思_indoor是什么意思中文翻译
  6. 【洛谷 1348】Couple number
  7. java计算机毕业设计人才公寓管理系统源码+数据库+系统+lw文档+mybatis+运行部署
  8. 如何将chatGpt接入企业微信
  9. 学习笔记5--车用地图与导航技术(下)
  10. 阿里云短信验证(代码)demo完整教程