完整源码在github,下载server和react部分

最近在做一个JSPatch后台管理系统。起初只是自己内部用,后来买了阿里云的免费服务器,用docker+jenkins完成了一个自动化部署,于是就想把这个小东西放到公网里。所以现在开始回来完善一下登录注册的功能,当然这个不是最终解决方案,只是帮助大家了解Json Web Token的基本流程和koa-jwt的基本用法。

一.Json Web Token介绍

由于http协议的无状态性,有些时候我们需要保存一些状态,比如用户的登录信息等。目前主要用到的一种方式是session + cookie。这种方式我之前也有过实现,但是只适用于浏览器端而不适用于原生应用。另外一种方式就是我们今天要讲到的Json Web Token。

1.组成

JWT主要由三部分组成

Header:base64编码的json object,包含token类型和使用的加密算法。一个Header行如下

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

Payload:base64编码的json object,包含一些自定义信息(用户唯一标识),和一些jwt预留的标识。常用的有iss(签发者),exp(过期时间戳),sub(面向的用户),aud(接收方),iat(签发时间)等。jwt不会强制要求你使用预留标识,一个简单Payload行如下。

{"uid": 42245, "exp": Date.now()+10*60*1000}

Signature: 根据Header,PayloadA 和一个密钥(只有服务端知道),并利用Header中指定的加密算法,生成的一个签名串。

2.输出形式

JWT格式的输出是以.分隔的三段Base64编码,与SAML等基于XML的标准相比,JWT在HTTP和HTML环境中更容易传递。一个标准的JWT输出大概是这样子的

JWT输出示例

3.鉴权流程

这里以koa-jwt为例。服务端调用koa-jwt的sign方法生成token,通过api请求下发到客户端。客户端存入本地(h5就是localstorage,cookie,sessionstorage等,iOS的话就是NSUserDefault)。客户端在下一次请求的时候在header里面带上token,服务端koa-jwt检测到token,先进行校验,校验成功以后用decode方法解密,并直接赋予this.state.user。以下是一个流程图。

JWT流程

二.具体使用

看了上面的介绍,大家也许还一头雾水?没关系,下面我们在实战中具体来操作。先来看一下我们具体要实现的功能。首先我们需要有一个注册的页面

注册页

注册完成以后,我们需要验证用户的邮箱

验证邮箱页

验证完成以后,跳转到主页面

主页

最后我们还有一个登录页面,如果登录之后没验证邮箱的,也跳转到认证邮箱页面,否则跳转到主页面

登录

前端大概就是这么几个页面,很简单,大家可以用React把这几个页面画出来,然后用React Router完成相关路由设定。页面部分的代码我就不在贴出,待源码整理完成以后会提供大家参考,本文的重点是JWT。

下面看一下server部分的,除了之前讲解koa上传图片用到的基本库以外,本文还需要用到koa-jwt和nodemailer两个库,首先用npm install xxx --save安装jwt和nodemailer。

先来看一下我的一个整体的项目结构

工程目录

项目的路由由于引入koa-frouter,所以是根据文件路径来决定的。我设置的是routers下面的文件。model用于生成mongodb的Schema,lib下主要是提供一些mongo的增删改查函数。

然后,打开app.js

app.js

我们引入koa-jwt并且加入到中间件级联中。这里的secret就是我们之前所讲的JWT用来生成Signature的密钥。由于我们的登录,注册这几个路由不需要用到JWT验证,所以我们把相关的几个路由忽略掉。

首先我们先来写一下注册的接口,在routers文件夹下创建register.js,然后在model下创建User.js,我们先来生成一个UserSchma。

User.js

这里主要看activeKey和isActive。activeKey我这里是用邮箱的md5(也可以用别的随机字符串,保证唯一性),isActive是表明用户验证过没有。我这里认证邮箱的思路大概是这样:首先用户注册,注册成功后把用户邮箱md5以后存到activeKey里面(email是不能重复的),用koa-jwt生成token,然后用nodemailer往注册邮箱发一封邮件,邮件包含一个链接。链接打开后会访问验证邮箱页面,并且。把activeKey放到链接的查询参数里面(query),在认证邮箱页面请求服务端认证接口,如果参数key和数据库里面保存的key相同,就说明认证成功,把数据库里的isActive改为true。

在lib目录下,创建User.js

User.js

这里就简单封装了我们要用到的几个函数。然后在routers下创建register.js。

config.scheme.js

register.js

引入lib下的User.js,nodemailer和koa-jwt。在这里我用到了koa-scheme这个库,所以对post请求上来的body数据合法性检测就放到了config下的config.schema.js里面。主要是邮箱合法性,以及生成activeKey的一些操作。在register.js里面我们就无需再写这些繁琐的数据校验。在register的post函数中,我们首先检验邮箱是否已存在,若不存在,入库,然后生成token。这里我们用koa-jwt提供的sign。

在之前我们有讲过JWT包含三部分。Header一般情况我们并不需要指定。我们给koa-jwt的sign方法传递了三个参数:

第一个是Payload,也就是用户信息(要注意payload不要传整个文档,Payload需要的是唯一且不变的数据,否则当Payload改变的时候需要重新下发token)。这里我们用文档的id,目的是唯一标识用户,并且不可修改但是它默认是ObjectId类型的,我们使用toString方法让其转化为字符串。

第二个参数是密钥,也就是你生成Signature时所用到的加密密钥。要注意这里必须和创建jwt的时候传入的secret一致,因为服务端需要用创建时的secret来解密。

第三个参数则是设置一个token的过期时间,这里我们设置的是1天。

这样我们就生成了一个token,这个token我们下发到浏览器端,然后存到localstorage里面。

前端Register.js

然后我们使用nodemailer给指定邮箱发邮件。这里我们默认发送到我的一个网易邮箱。

register.js

注意mailOptions里面的html,这里的html就是在邮件内容里面显示出来的html。这里我们用一个a标签包一个超链接,链接到前端验证邮箱的页面,并带上查询参数verifyKey。收到邮件后,根据React Router点击会跳转到VerifyEmail组件

前端VerifyEmail.js

在组件挂载完毕之后,我们从loaction的query里取verifyKey参数,如果存在,证明是点击邮箱跳转过来的,这样我们就调用一下网络请求的函数访问服务端verifyemail接口。

前端Category.js

前端network.js

网络请求的函数我写进了Component的扩展里面,方便调用。Category里面,我们对10002和10001错误进行了统一处理。一个是说明用户未验证邮箱,一个则是说明用户未登录。

另外,在network里面我用了这种返回Promise的方法,也是避免回调函数嵌套。注意network里面,我们会从localstorage里面取一次token。如果token存在,证明服务端授权过。所以我们在请求的header里面增加Authorization字段,并且用Bearer + 空格 + token的形式把服务端生成并返回给我们的token传入。

当你的请求头里包含Authorization,并且koa-jwt没有unless的情况下,koa-jwt就会对这一次API访问进行鉴权。如果成功,会把token解密后的数据放到ctx.state.user中,在koa里我们的上下文就是this。当然这个user的名称是可以更换的,具体的请在github查看koa-jwt的usage。

现在由于我们是点击邮箱跳转过来的,所以verifyKey参数也是有的,再加上之前注册服务端返回并存到localstorage的token,所以我们在VerifyEmail里面直接请求verifyemail接口。在服务端routers文件夹下我们创建verifyemail.js

verifyemail.js

这个逻辑就很简单了,拿到前端传过来的verifyKey,再拿到jwt解密出来的文档id,然后查询出来id所对应的doc,判断两个key是否相等。相等的话就说明认证成功,然后更新文档的isActive字段,并返回成功给前端。前端判断如果code为0的话,就直接跳转到主页。

到这里我们已经完成了一个简短的邮箱验证,但是还有一些细节需要处理。

接下来我们看一下登录,在routers下创建login.js。

login.js

和注册的逻辑基本一样,只不过我们需要验证一下密码。在这里存在的一个逻辑就是,如果注册完没有立即去验证邮箱,也是能登录进来的。在这种情况下,我们需要在登录成功之后跳转到认证邮箱的页面。在这里我们实现的逻辑是登录成功后默认跳转到主页,主页会调用getapplist接口。然后我们对所有请求添加一个中间件进行处理。打开app.js。

app.js

添加如上代码,这里添加一个中间件。除login,register,verifyemail三个接口之外,所有接口都会验证User文档的isActive是否为true,若为false,则直接返回code==10002。若不为则交给下面的中间件继续处理。

前端的话,就要对所有网络请求进行统一的处理,这也就是我们为什么要封装网络请求的原因。(请看上文的Category.js)

所以当我们登录并未验证的用户,getapplist请求会被该中间件拦截并返回code==10002。前端对返回code==10002会跳转到认证邮箱页。同理未登录的用户会返回code==10001。

三.总结

关于JWT Token过期问题

1.如果在payload里面设置了exp属性,则你可以将你的token存到localstorage里面,当到达过期时间以后,你再用这个token去请求API,服务器会抛出Thrown error if the token is expired错误。

Thrown error if the token is expired.

Error object:

- name: 'TokenExpiredError'

- message: 'jwt expired'

- expiredAt: [ExpDate]

2.对于没有设置exp的童鞋,你可以存到cookie里面并设置过期时间。或者在localstorage里面再增加一条创建时间的字段。两种方式都可以看个人的喜好。

The End

至此我们已经用React+nodemailer+koa-jwt实现了一个简单的登录注册及邮箱验证功能。如果你正在为原声移动应用或者SPA编写API的话,JWT会是一个非常不错的选择。有一点需要记住的是: 在浏览器中使用JWT你需要将它存在LocalStorage或者SessionStorage中,但这可能会导致XSS攻击。关于XSS攻击的话,网上有详细的文章来说明,这里就不赘述。

源码待我整理完毕后会上传到github。

好了,打个小广告。小弟和几个朋友一起弄一个技术公众号,主要专注于React全栈相关技术。感兴趣的朋友可以关注,一起交流学习。公众号另外一位小编可是资深React和Node开发者哦!

如果你也喜欢全栈,请关注React全栈开发吧!!

React + nodemailer + koa-jwt 实现登录注册邮箱验证相关推荐

  1. 原生JavaScript实现登录注册前端验证

    原生JavaScript实现登录注册前端验证 功能:实现一个简单的前端登录页面的验证, 废话不多说,直接上代码... 登录窗口代码段... //html <div class="log ...

  2. Destoon如何去除登录的邮箱验证?

    Destoon如何去除登录的邮箱验证? 主要修改下面的函数即可,/module/member/member.class.php function is_member($member) {global ...

  3. JavaMail实现注册邮箱验证案例

    原文链接:https://www.jianshu.com/p/8f8d7a46888f 在日常生活中,我们在一个网站中注册一个账户时,往往在提交个人信息后,网站还要我们通过手机或邮件来验证,邮件的话大 ...

  4. 干货!flask登录注册token验证接口开发详解

    今天给大家献上登录注册接口开发,是基于token验证的.咱们闲言少叙,进入正题! 首先看一下数据库模型: #pip install passlib from passlib.apps import c ...

  5. 基于jwt的登录/注册功能实现

    # views.py # 登录视图 class LoginView(ViewSet):# 登录@action(methods=['POST'], detail=False)def login(self ...

  6. jwt判断token是否过期_4spring-security5整合jwt做登录、权限验证,全网最全!!!可用...

    github源码: https://github.com/gyb123456/spring-security5-jwt,最烦那些写文档只截图一半还不给源码的人,要不你就截全图,要不就给源码! 前言: ...

  7. h5 php登录注册页面验证,H5制作一个注册页面的代码实例

    HTML5写的注册页面,正在学习html5的朋友可以参考下 代码如下: register.html function play(){ document .getElementById("me ...

  8. phpcmsV9 邮箱注册:邮箱验证(不改代码、含演示截图) - 配置篇

    phpcmsV9 邮箱注册:邮箱验证(不改代码.含演示截图) - 全程指导 方法一.(网传) · 配置教程 第一步:修改登陆的验证JS 第二步: 修改登录文件 方法二.真机实操 · 教程 [推荐] 1 ...

  9. 注册验证之邮箱验证(SpringBoot框架)

    一.首先先加入邮箱依赖,看下面链接! JAVA实现邮件发送(SPRING BOOT 框架) 二.大概思路及实现 1.数据库加一个验证字段 2.先让用户进行注册 前端实现:(正常的注册,只是邮箱必填,会 ...

最新文章

  1. 如何在内核里面查找某些结构体或者宏的定义
  2. bzoj 1058: [ZJOI2007]报表统计
  3. 面试官:重写 equals 时为什么一定要重写 hashCode?
  4. vSAN6.2 性能服务
  5. PTA L2-006 树的遍历-二叉树的后序遍历+中序遍历,输出层序遍历 团体程序设计天梯赛-练习集...
  6. SK海力士与电装四巨头论半导体供给
  7. 负载均衡获得真实源IP的6种方法 【转】
  8. 条件判断_判断疑似陨石应具备什么条件下,才能判断陨石真伪
  9. 初始化问题(其中含有盲区,{}和()的区别)
  10. 【利用WPS功能破解密码】笔记
  11. Web渗透测试-实战 方法 思路 总结
  12. qcap 教程_高通平台抓取ramdump及使用qcap解析,ramdumpqcap
  13. Spring boot应用【tailf】服务启动停止管理脚本
  14. 一些CS领域、互联网领域的名词解释,作为知识补充
  15. HTML菜单中有关selected=true和setAttribute(“selected“,“selected“)的异同以及selected设置无法生效的问题解析
  16. C++ for循环效率优化
  17. 幂等和非幂等的关系与区别
  18. Booth乘法器设计
  19. 反编译工具Virtuous Ten Studio使用
  20. 拓扑排序与关键路径(AOV网和AOE网)

热门文章

  1. 杭州阿里总部五面全过程:一面技术面+二面(项目+技术)+三面(项目经理面)+四面(地区技术负责人面)
  2. 多线程十二种设计模式详解
  3. Dev-c++的小游戏代码(可直接复制)
  4. 半导体材料的霍尔效应测试简介
  5. 天祥电子proteus原理图c51+avr+pic
  6. 手机话筒破音测试软件,如何解决手机话筒有杂音的问题?
  7. springBoot配置文件密码加密
  8. IMU(惯性测量单元)学习
  9. matlab 求样本离差阵,样本的离差、标准差、方差、偏度、(多图)
  10. Python爬虫出现cannot use a string pattern on a bytes-like object