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

一个带有用户系统的应用最基本登录方式是站内账号登录,但这种方式往往不能满足我们的需求。现在的应用基本都有站内账号、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 字段即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/530010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将Go语言开发的Web程序部署到K8S

搭建K8S基础环境 如果已经有K8S环境的同学可以跳过,如果没有,推荐你看看我的《Ubuntu22加Minikue搭建K8S环境》,课程目录如下: Ubuntu22安装Vscode 下载:https://code.visualstudio.com/Download 安装命令&#…

python 扫描仪_基于Opencv和Python的多选扫描仪

首先,我检测到图像右侧的20个黑框,然后将x和宽度添加到列表中:image cv2.imread(args["image"])gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)(_, thresh) cv2.threshold(gray, 220, 255,cv2.THRESH_BINARY)kernel cv2.getStr…

mysql dmz_MySQL 中LIMIT的使用详解

MySQL的Limit子句Limit子句可以被用于强制 SELECT 语句返回指定的记录数。Limit接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。//初始记录行的偏移量…

python编程入门到实践笔记习题_Python编程从入门到实践笔记——列表简介

python编程从入门到实践笔记——列表简介#codingutf-8#列表——我的理解等于c语言和java中的数组bicycles ["trek","cannondale","readline","specialized"]print(bicycles)#列表索引从0开始print(bicycles[0].title())#访问列表元素…

informatica mysql odbc_Informatica 配置mysql community odbc连接

Informatica linux 版本内置的DataDirect 驱动支持各种数据库例如oracle、sybase、postgreSQL、Greenplum、mysql等等但是mysql 只支持企业版本,如果我们使用的是community 社区版本便不能使用自带的DataDirect方式了,那我们就需要手动配置其他odbc连接。…

mysql分表 动态扩容_数据库hash分表后的扩容方案

postgres的hash分表不停机扩容方案原来我们hash分表之后,数据扩容采用的是rehash,这样迁移全部的数据,比较麻烦。本次扩容利用hash环原理,并在此基础上做一些适应性的改动。首先假定哈希环的范围为0-1023,总共1024的数…

php mysql长连接聊天室_PHP之探索MySQL 长连接、连接池

PHP连接MysqL的方式,用的多的是MysqL扩展、MysqLi扩展、pdo_MysqL扩展,是官方提供的。PHP的运行机制是页面执行完会释放所有该PHP进程中的所有资源的,如果有多个并发访问本地的测试页面 http://127.0.0.1/1.php 根据PHP跟web服务器的不同,会开…

python 读取地震道头数据_python地震数据可视化详解

本文实例为大家分享了python地震数据可视化的具体代码,供大家参考,具体内容如下准备工作:在windows10下安装python3.7,下载参考源码到本地。1. demo绘图测试demo绘图指令cmd> python seisplot.py --demo问题1)缺少依赖包File &…

在MySQL查询山东省男生信息_MySQL-查询

来一波英语单词解释(意思)create 创建show 显示database 数据库use 使用select 选择table 表from 来自…distinct 消除重复行as 同样地(用于其别名)where 范围like 模糊查询rlike 正则查询In 范围查询not in 不非连续的范围之内between ... and …表示…

java 导入world数据_java读取world文件,把world文件中的内容,原样输出到页面上。...

POI,处理可以。样式在Java代码中添加就可以。给了一个例子这个是Excel的。package cn.com.my.common;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.sql.Connection;import java.sql.ResultSet…

java程序员 css_Java程序员从笨鸟到菜鸟之(十七)CSS基础积累总结(下)

七.组织元素(span和div)span和div元素用于组织和结构化文档,并经常联合class和id属性一起使用。在这一课中,我们将进一步探究span和div的用法,因为这两个HTML元素对于CSS是很重要的。用span组织元素用div组织元素用span组织元素span元素可以说…

redlock java_Redlock分布式锁

这篇文章主要是对 Redis 官方网站刊登的 Distributed locks with Redis 部分内容的总结和翻译。什么是 RedLockRedis 官方站这篇文章提出了一种权威的基于 Redis 实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。它可以保证以下特性&#xff1…

java 两个数组交叉_java – 如何交叉两个没有重复的排序整数数组?

这个问题本质上减少到一个连接操作,然后是一个过滤器操作(删除重复,只保留内部匹配).由于输入都已经排序,所以可以通过O(O(size(a)size(b))的merge join来有效地实现连接.过滤器操作将为O(n),因为连接的输出被排序,并且要删除重复项,所有您需要做的是检查每个元素是否与之​​前…

java retentionpolicy_Java注解之如何利用RetentionPolicy.SOURCE生存周期

上一篇文章简单讲了下Java注解的学习之元注解说明,学习了Java注解是如何定义的,怎么使用的,但是并没有介绍Java的注解是怎么起作用的,像Spring Boot里面的那些注解,到底是怎么让程序这样子运行起来的?特别是…

在java程序中定义的类有两种成员_java试题 急需答案 谢谢!!!

三、填空(每小题2分,共10分)1.在Applet中,创建一个具有10行45列的多行文本区对象ta的语句为:2.创建一个标识有“关闭”字样的标签对象gb的语句为。3.方法是一种仅有方法头,没...三、填空(每小题…

java 同步 变量,在java中的对象上同步,然后更改同步的变量的值

I came across a code like thissynchronized(obj) {obj new Object();}Something does not feel right about this , I am unable to explain, Is this piece of code OK or there is something really wrong in it, please point it out.Thanks解决方案Its probably not wha…

java set泛型_Java 集合二 泛型、Set相关

泛型1、在定义一个类的方法时,因为不确定返回值类型,所以用一个符号代替,这个符号就是泛型eg:ArrayList list new ArrayList();2、泛型的好处:1、提高了数据的安全性,将运行时的问题提前暴露在编译阶段2、避免了强转的…

java annotation 实现_在Java中如何实现自己的annotation

1. 先定义annotation2. 使用annotation例子:import java.lang.annotation.*;import java.lang.reflect.Method;Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)interface Test {String info() default "";}class Annotated {Test(info &q…

登录界面拦截java_java拦截通过url访问页面,必须通过登录页面访问目标页面

在web.xml中配置过滤:LoginFiltercom.verification.action.LoginFilterLoginFiltery/form/dealParse.do/* 拦截所有请求/.do 拦截以“.do”结尾的请求/index.jsp 拦截指定的jsp/artery/form/* 拦截该目录下的所有请求等等拦截器,拦截请求类&#xf…

python textwrap_[Python标准库]textwrap——格式化文本段落

textwrap——格式化文本段落作用:通过调整换行符在段落中出现的位置来格式化文本。 Python 版本:2.5 及以后版本 需要美观打印时,可以用 textwrap 模块来格式化要输出的文本。这个模块允许通过编程提供类似段落自动换行或填充…