写系列文章最容易出现的状况是写到一半,而后写不下去了。一个因素是人变懒了;一个因素是这几天在整理用户模块这部分的知识,发现有太多的内容要写,而深刻Google以后又发现好多内容是本身未知的,写着写着就不知道该如何写了。linux
常见的和用户模块相关的操做有:注册登陆、信息修改、退出登陆ios
一个系统中,用户模块是最基础也是最重要的。若是只考虑一种登陆方式,那用户表能够用一张
users
表搞定一切。可是现实状况是登陆方式有不少种,好比:帐号密码登陆
,微信、QQ、微博登陆
,手机号、邮箱登陆
。并且一个用户能够使用帐号密码登陆
以后再关联微信、QQ、微博
,以后能够直接使用微信、QQ、微博
登陆;那么如何保证设计用户表
将变得相当重要。数据库
参考:axios
经过以上部分博文的介绍,咱们能够将用户表设计为:用户基础表:user_base
,用户受权表:user_auth
,用户扩展表:user_extends
。小程序
用户基础表:uid、user_role、user_name、avatar、password、signature、birthday、gender等segmentfault
用户受权表:id、uid、identity_type、identifier、certificate等微信小程序
用户扩展表:id、uid、device_name、device_id、vendor、client_name、client_version等api
以上的用户表的设计能够参考: 用户系统设计与实现服务器
ps:我这个项目的用户表设计当时没有考虑那么多,只考虑了
微信小程序端
的设计,先给本身留个坑之后再将项目完善吧。先介绍目前的实现方式。微信
我目前的实现步骤以下:
在app/models
包下建立user.js
:用户表的相关字段以及用户表的操做
User.init({ id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, nickname: Sequelize.STRING, email: { type: Sequelize.STRING(128), // 添加惟一(unique)约束后插入重复值会报错 unique: true }, password: { type: Sequelize.STRING, set(val) { // 使用 bcryptjs 库对密码加密处理 const salt = bcrypt.genSaltSync(10) const psw = bcrypt.hashSync(val, salt) // 设置 password 的值 this.setDataValue('password', psw) } }, openid: { type: Sequelize.STRING(64), unique: true }, gender: Sequelize.INTEGER, balance: Sequelize.INTEGER, avatar_url: Sequelize.STRING, city: Sequelize.STRING, country: Sequelize.STRING, province: Sequelize.STRING }, { sequelize, tableName: 'users' }) module.exports = { User }
微信小程序端不须要注册,直接使用微信受权登陆便可。
这里的注册是指用户在Web、App 端
使用邮箱、用户名、密码完成注册。
在app/api/v1
包下建立user.js
:
/** * 用户名密码注册 */ router.post('/register', async (ctx) => { const v = await new RegisterValidator().validate(ctx) // 令牌获取 颁布令牌 const user = { email: v.get('body.email'), password: v.get('body.password2'), nickname: v.get('body.nickname') } // 使用 Sequlize 保存到数据库 await User.create(user) success() }) module.exports = router
调用:
http://192.168.*.***:3000/v1/user/register
,在表单中填写邮箱、用户名、密码,而后将结果传递给api
便可
使用帐号(邮箱)密码登陆
/** * 邮箱登陆:用户不存在会提示帐户不存在,用户存在则验证用户信息,登陆成功以后返回 token * @param {帐号} account * @param {密码} secret */ async function emailLogin(account, secret) { const user = await User.verifyEmailPassword(account, secret) return token = generateToken(user.id, Auth.USER) }
微信小程序使用token
登陆
在app/service
包下建立wx.js
:获取登陆的token
class WXManager { /** * 小程序登陆 * @param {小程序中传递的 code} code * @param {小程序中公开的用户信息} userInfo */ static async codeToToken(code, userInfo) { const url = util.format(global.config.wx.loginUrl, global.config.wx.appId, global.config.wx.appSecret, code) // 调用微信提供的接口 const result = await axios.get(url) if (result.status !== 200) { throw new global.errs.AuthFailed('openid获取失败') } // 微信中最终判断是经过 errcode 判断 const errcode = result.data.errcode const errmsg = result.data.errmsg if (errcode){ throw new global.errs.AuthFailed('openid获取失败:'+errmsg) } // openid // 档案 user uid openid 长 // openid // 用户是否存在,例如 token 过时的状况 let user = await User.getUserByOpenid(result.data.openid) if(!user){ user = await User.registerByOpenid(result.data.openid, userInfo) } return generateToken(user.id, Auth.USER) } } module.exports = { WXManager }
(1)帐号密码登陆调用:
http://192.168.*.***:3000/v1/token
,在表单中填写邮箱、密码,而后将结果传递给api
便可;(2)小程序登陆调用:先进行小程序登陆,而后调用http://192.168.*.***:3000/v1/token
,将小程序提供的code
发送给api
便可
无论是帐号密码登陆
仍是微信小程序登陆
,都返回了token
主要是用于API 鉴权、登陆状态保持
在core
包下建立util.js
:生成token
的工具类
/** * 使用 JWT 生成 token * @param {用户 id} uid * @param {用户权限} scope */ const generateToken = function (uid, scope) { const secretKey = global.config.security.secretKey const expiresIn = global.config.security.expiresIn // 在 token 中写入 uid、scope 数据 const token = jwt.sign({ uid, scope }, secretKey, { expiresIn: expiresIn }) return token } module.exports = { generateToken, }
关于
JWT
的介绍能够查看我以前写的文章:全栈项目|小书架|服务器开发-JWT 详解,或者这篇文章:JWT 超详细分析
用户信息的修改须要调用
update
信息便可,若是是修改密码
还须要将本地的登陆状态
清除,从新登陆。
具体的修改信息,后续接口实现了再补上。
这篇文章介绍的不错:How to log out when using JWT
文中描述的主要方式有下面几种:
token
token
维护黑名单的方式有人认为会致使黑名单列表过长,这里能够经过判断token
是否过时,过时则自动删除。
本项目只是从客户端删除token
的方式实现退出登陆。
其余的处理方式参考:
咨询请加微信:轻撩便可。