Realize user login verification based on thinkjs


1. Database creation

用Navicat创建一个数据库“db”,在创建一张存储用户登录信息的表,命名为“demo-admin”,
表的设计如下(表直接拿“hioshop”开源项目中的admin表):

hioshop-server

id username password password_salt last_login_ip last_login_time is_delete
14 hiolabs 4af5d2d0e6c2bd03dca28c884afdf0ee HIOLABS

2. Create a thinkjs project

thinkjs new demo-server//创建一个thinkjs项目

3. Configure the project database

找到项目‘src/config/adapter.js’文件,打开这个文件,修改代码如下:
mysql: {
    
    
    handle: mysql,//mysql数据库
    database: 'db',//数据库名
    prefix: 'demo_',//数据表前缀,如果一个数据库里有多个项目,那项目之间的数据表可以通过前缀来区分
    encoding: 'utf8mb4',//数据库编码方式
    host: '127.0.0.1',//主机ip
    port: '3306',//端口号
    user: 'root',//用户名
    password: '123123123',//密码
    dateStrings: true
  }

4. Implementation of user login verification

//创建一个"auth.js"的controller控制器
thinkjs controller auth
//创建一个"token.js"的Service对象,用来定义token验证的相关方法
thinkjs service token
假设客户端向服务端发送一个post请求携带用户名和密码的请求‘/auth/login’接口,服务端对当前
请求进行验证,验证通过则往下继续执行,相关验证代码如下:
//"auth.js"
const Base = require('./base.js');

module.exports = class extends Base {
    
    
	async loginAction(){
    
    //登录验证
		//获取前端提交过来的用户名,并定义一个常量存储它
		const username = this.post('username');
		//获取前端提交过来的密码,并定义一个常量存储它
		const password = this.post('password');
		//对用户名进行验证
		const admin = await this.model('admin').where({
    
    username:username}).find();
		if(think.isEmpty(admin)){
    
    
			return this.fail(401,'用户名不正确');
		}
		//对用户密码进行验证
		if(think.md5(password + '' + admin.password_salt) !== admin.password){
    
    
			return this.fail(401,'用户密码错误');
		}
		//用户名和密码都正确的话,更新当前用户的登录时间和登录ip到记录对应的数据表相应字段中
		await this.model('admin').where({
    
    id:admin.id}).update({
    
    
			last_login_time:parseInt(Date.now()/1000),
			last_login_ip:this.ctx.ip
		});
		//实例化一个tokenservice对象,来调用create()方法创建一个token
		const TokenService = this.service('token');
		const token = await TokenService.create({
    
    
			id:admin.id//payload
		});
		//判断token是否创建成功
		if(think.isEmpty(token)){
    
    
			return this.fail('用户登录失败');
		}
		//令牌创建成功,返回给客户端"uerInfo"和"token"给前端
		const userInfo = {
    
    
			id:admin.id,
			username:admin.username
		};
		return this.success({
    
    
			token:token,
			userInfo:userInfo
		});
	}
};
//"token.js"
const jwt = require('jsonwebtoken');//JWT
const secret = '&w@ueJnXy!Gj0J@qVmMPR^9ip00EP0^Qy5Se#lzwStExJ3Zw4j02NVLm^btjBO8x'

module.exports = class extends think.Service {
    
    
	async create(userId){
    
    //创建令牌
		const token = jwt.sign(userId,secret);
		return token;
	}

	async parse(){
    
    //令牌验签
		if(think.isEmpty(think.token)){
    
    //如果token为空
			return null;
		}
		else{
    
    
			try{
    
    
				return jwt.verify(think.token,secret).id;
			}catch(err){
    
    
				return null;
			}
		}
	}
};
通过以上操作后端接口‘auth/login’将token返回给客户端,假设客户端获取到服务端响应的token,
把token值存储在header请求头'Authorization'中,便于每次请求路由时携带token发送到服务端

接着实现对每次请求的token验证是否合法的实现,代码如下:
//base.js
module.exports = class extends think.Controller {
    
    
	async _before(){
    
    //_before()方法在访问每条路由前都会先执行此函数
		//从客户端请求头header中获取发送过来的token,如果没有获取到给think.token赋空值
		think.token = this.ctx.header['Authorization'] || '';
		//声明一个Token实例来调用token.js里的parse()方法来对token进行验签,通过则返回用户ID,失败返回null
		const Token = this.service('token');
		think.userId = await Token.parse();
		if(this.ctx.controller !== 'auth'){
    
    //如果当前路由不是'/auth'且
			if(think.isEmpty(think.userId)){
    
    //如果用户ID为空
				return this.fail('用户未登录');
			}
		}
	}
};

Guess you like

Origin blog.csdn.net/weixin_52134577/article/details/109584858