常见的前后端鉴权方式
- Session-Cookie
- Token 验证(包括 JWT,SSO)
- OAuth2.0(开放授权)
JWT
JWT(JSON Web Token)是一种开放的标准,用于在网络应用间传递信息的一种方式。它是一种基于JSON的安全令牌,用于在客户端和服务端之间传输信息。
基于token的鉴权机制是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。每个请求都是独立的,所有的认证信息包含在每个请求的请求头参数中,这就为应用的扩展提供了便利。
JWT由三部分组成,它们通过点(.)进行分隔:
- Header(头部):包含了令牌的类型和使用的加密算法等信息。通常采用Base64编码表示。
- Payload(负载):包含了身份验证和授权等信息,如用户ID、角色、权限等。也可以自定义其他相关信息。同样采用Base64编码表示。
- Signature(签名):使用指定的密钥对头部和负载进行签名,以确保令牌的完整性和真实性。
JWT的工作流程如下:
- 用户通过提供有效的凭证(例如用户名和密码)来请求服务端进行身份验证
- 服务端验证凭证,并生成一个JWT作为响应。JWT包含了用户的身份信息和其他必要的数据
- 服务端将JWT发送给客户端
- 客户端在后续的请求中,将JWT放入请求的头部或其他适当的位置
- 服务端在接收到请求时,验证JWT的签名以确保其完整性和真实性。如果验证通过,服务端使用JWT中的信息进行授权和身份验证。
Token 和 JWT
相同
- 都是访问资源的令牌
- 都可以记录用户的信息
- 都是使服务端无状态化
- 都是只有验证成功后,客户端才能访问服务端上受保护的资源
区别
- Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
- JWT: 将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
安装使用的依赖
-
passport:passport是一个流行的用于身份验证和授权的Node.js库,该依赖库中有很多身份验证的方式,JWT只是其中之一。
-
passport-jwt:Passport-JWT是Passport库的一个插件,用于支持使用JSON Web Token (JWT) 进行身份验证和授权
-
jsonwebtoken:生成token的库
js
npm i passport
npm i passport-jwt
npm i jsonwebtoken
passport
passport.js是Nodejs中的一个做登录验证的中间件,极其灵活和模块化,并且可与Express、Sails等Web框架无缝集成。passport功能单一,只能做登录验证,但非常强大,支持本地账号验证和第三方账号登录验证(OAuth和OpenID等),支持大多数Web网站和服务。
策略Strategy
策略是passport中最重要的概念。passport模块本身不能做认证,所有的认证方法都以策略模式封装为插件,需要某种认证时将该认证的passport插件添加到项目中即可。
策略模式是一种设计模式,它将算法和对象分离开来,通过加载不同的算法来实现不同的行为,适用于相关类的成员相同但行为但不同的场景。比如在passport中,认证所需的字段都是用户名、邮箱、密码等,但认证方法是不同的。依据策略模式,passport支持了众多的验证方案,包括Basic、Digest、OAuth(1.0,和2.0的三种实现)、Bearer等。
基本用法
local本地验证
本地验证默认使用用户名和密码来进行验证。在做验证之前,首先需要对策略进行配置,官方的示例如下:
js
let passport = require('passport');
let LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: '用户名不存在.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: '密码不匹配.' });
}
return done(null, user);
});
}
));
其中的User.findOne()
是MongoDB风格的语法,意思是从数据库的User集合中查询一条数据,第一个参数是查询条件,后面是callback回调函数,一般在callback回调函数中进行后续操作。这里的逻辑很简单,依次检查username
、password
,如果出错执行done(err)
则返回错误信息,如果通过执行done(null,user)
则返回user信息。上面的代码user.validPassword(password)
方法用于验证密码是否正确,这并不是passport添加的,而是需要用户自定义。
配置策略
passport本地验证默认使用用户名和密码来验证,但实际上也可以用邮箱来验证,通过配置策略对象实现。
passport在策略配置里提供了options参数,用来设置要验证的字段名称,使用方法如下:
js
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'passwd'
},
function(username, password, done) {
// ...
}
));
注意,这里要验证的字段名称应该是客户端传入的参数,即req.body.xxx,而不是user数据库中的字段名称。
验证回调
passport本身不处理验证,验证方法在策略配置的回调函数里由用户自行设置,它又称为验证回调。验证回调需要返回验证结果,这是由done()
来完成的。
在passport.use()里面,done()有三种用法:
- 当发生系统级异常时,返回done(err),这里是数据库查询出错,一般用next(err),但这里用done(err),两者的效果相同,都是返回error信息;
- 当验证不通过时,返回done(null, false, message),这里的message是可选的,可通过express-flash调用;
- 当验证通过时,返回done(null, user)。
将策略配置对象options作为LocalStrategy第一个参数传入,第二个参数是验证回调。
session序列化与反序列化
验证用户提交的凭证是否正确,是与session中储存的对象进行对比,所以涉及到从session中存取数据,需要做session对象序列化与反序列化。实现代码如下:
js
// 将user.id序列化到session中,即sessionID,同时它将作为凭证存储在用户cookie中
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// 从session反序列化,参数为用户提交的sessionID,若存在则从数据库中查询user并存储到req.user中
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
session序列化与反序列化的执行顺序可以放在passport.use()
的前面或后面,但需要在app.use(passport.initialize())
之前。
Authenticate验证
js
app.post('/login',
passport.authenticate('local',
{ successRedirect: '/',
failureRedirect: '/login',
failureFlash: true }),
function(req, res) {
// 验证成功则调用此回调函数
res.redirect('/users/' + req.user.username);
});
这里的passport.authenticate()
就是中间件,若通过就进入后面的回调函数,并且给res加上res.user,若不通过则默认返回401错误。
authenticate()方法有3个参数,第一是name
,即验证策略的名称,第二个是options
,包括下列属性:
- session:Boolean,设置是否需要session,默认为true
- successRedirect:String,设置当验证成功时的跳转链接
- failureRedirect:String,设置当验证失败时的跳转链接
- failureFlash:Boolean或者String,设置为Boolean时,express-flash将调用use()里设置的message。设置为String时将直接调用这里的信息。
- successFlash:Boolean或者String,使用方法同上。
第三个参数是callback。注意如果使用了callback,那么验证之后建立session和发出响应都应该由这个callback来做,passport中间件之后不应该再有其他中间件或callback。以下是代码:
js
var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
//User模型需自己实现
var User = require('../models/User');
var bcrypt = require('bcrypt');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: '用户名不存在.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: '密码不匹配.' });
}
return done(null, user);
});
}
));
app.use(session({secret: "need change"}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.post('/login', passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) {
req.flash('errors', { msg: info.message });
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) return next(err);
req.flash('success', { msg: '登录成功!' });
res.redirect('/');
});
})(req, res, next)
);
OAuth验证
OAuth标准分为两个版本,1.0版和2.0版,两者被使用的都很广泛,passport通过passport-oauth为两者提供支持,使用下面的命令可以安装。
js
npm install passport-oauth
OAuth验证流程
OAuth1.0和2.0的使用流程都差不多,一般来说如下:
- 为自己的app去第三方服务商处申请标识和令牌appkey和secret;
- 在自己的app里添加按钮或链接,将用户引导至服务商的授权页,用户在这里选择授权给自己的app;
- 授权成功后跳转回自己的app,同时还传递回access_token和一些用户资料。
到这里首次验证流程就完成了,之后只要拿access_token去就可以做登录验证或者其他事了。
OAuth1.0
要使用passport OAuth1.0验证需要先引入然后进行配置
js
let passport = require('passport');
let OAuthStrategy = require('passport-oauth').OAuthStrategy;
// 配置
passport.use('provider', new OAuthStrategy({
requestTokenURL: 'https://www.provider.com/oauth/request_token',
accessTokenURL: 'https://www.provider.com/oauth/access_token',
userAuthorizationURL: 'https://www.provider.com/oauth/authorize',
consumerKey: '123-456-789',
consumerSecret: 'shhh-its-a-secret'
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(token, tokenSecret, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
});
}
));
这里比通用流程多的一点就是,自己的App需要先访问第三方服务,获取request token,这个request token是未授权的,等用户授权之后,可以拿这个request token去换取access token。在passport中不必管这些细节,找到第三服务的文档找到对应的URL添上即可。在此之前还得申请key和secret。
use
方法的回调接受四个参数,token就是access token,和tokenSecret一起好好保存。profile则是用户在第三方服务上的一些公开资料,它的模型如下,不过返回的资料不一定全面,在使用前需要验证是否存在。
js
class UserProfile{
// 用户进行身份验证的提供者(facebook、twitter等)
provider:string
// 用户的唯一标识符,由服务提供者生成
id:string
// 该用户的名称,用于显示
displayName:string
name:object{
familyName:string, // 用户的姓
givenName:string, // 用户的名
middleName:string // 用户的中间名
}
emails:srting[][
value, // 实际的电子邮件地址
type // 电子邮件地址的类型(家庭、工作等)]
photos:string[][
url // 图片的地址]
}
OAuth1.0的路由常见写法如下:
js
app.get('/auth/provider', passport.authenticate('provider'));
app.get('/auth/provider/callback', passport.authenticate('provider',
{
successRedirect: '/',
ailureRedirect: '/login'
})
);
OAuth1.0主要是一些比较早提供第三方登录功能的网站使用,现在的网站大部分使用OAuth2.0
OAuth2.0
OAuth2.0的验证不需要request_token,但比1.0多了scope和refresh token,具体的配置方法:
js
let passport = require('passport')
let OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
passport.use('provider', new OAuth2Strategy({
authorizationURL: 'https://www.provider.com/oauth2/authorize',
tokenURL: 'https://www.provider.com/oauth2/token',
clientID: '123-456-789',
clientSecret: 'shhh-its-a-secret'
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
});
}
));
refreshToken是重新获取access token的方法,因为access token是有使用期限的,到期了必须让用户重新授权才行,现在有了refresh token,可以让应用定期的用它去更新access token,这样第三方服务就可以一直绑定了。不过这个方法并不是每个服务商都提供,注意看服务商的文档。
下面是路由,OAuth2.0也有一点不同:
js
app.get('/auth/provider',passport.authenticate('provider', { scope: 'email' }));
app.get('/auth/provider/callback', passport.authenticate('provider',
{
successRedirect: '/',
failureRedirect: '/login'
})
);
scope是权限范围,需要在服务商处事先申请,可参考微博的scope文档。它可以只有一项,也可以有多项,当为多项时以数组形式表示。
scope 是 OAuth2.0 授权机制中 authorize 接口的一个参数。通过 scope 参数,平台可以提供更多的能力给开发者,同时用户也可以有选择的授权或者不授权不同的能力给第三方,即保障了用户对于隐私保护的需求,也提升了用户使用第三方应用的体验。
passport-jwt用法
配置策略
js
import { Strategy, ExtractJwt } from 'passport-jwt'
// new Strategy(options, verify)
let opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'secret';
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
new Strategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
// passport错误信息,false不通过验证
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
})
- options:第一个参数是一个对象,包含用于控制如何从请求中提取令牌或者被验证的选项的对象文本
- verify:第二个参数是一个验证token的回调函数
options配置对象属性如下:
- secretOrKey:包含加密( 对称) 或者 public 编码密钥( 非对称)的字符串或者缓冲区,用于验证令牌的签名。除非提供 secretOrKeyProvider,否则需要
- secretOrKeyProvider:是一个回调函数,格式为
function secretOrKeyProvider(request, rawJwtToken, done)
应该为给定密钥和请求调用一个密码或者调用 done函数并传入public 密钥( 非对称)。 done 以function done(err, secret)
格式接受参数。 除非提供 secretOrKey,否则需要 - jwtFromRequest:字符串或者null,是请求中提取的JWT。从请求中提取 JWT的方法详情如下。
- issuer: 如果定义了令牌颁发者,将根据这个值验证
- audience: 如果定义了,将根据该值验证令牌
- algorithms: 带允许算法名称的字符串列表, 例如:HS256和HS384
- ignoreExpiration: 如果 true 不验证令牌的到期时间
- passReqToCallback: 如果 true,请求将被传递到第二个参数即验证回调函数
- jsonWebTokenOptions:
passport-jwt
将使用jsonwebtoken
验证令牌
verify验证回调函数的参数如下:
- jwt_payload:是包含解码的JWT负载的对象文字
- done:用于控制鉴权的成功或者失败,回调函数接受两个参数,第一个参数是错误信息
从请求中提取 JWT 的方法
passport-jwt
的ExtractJwt类中提供了许多提取器工厂函数。
ExtractJwt.fromHeader(header_name)
:创建一个新的提取器,它在给定的http头中查找 JWTExtractJwt.fromBodyField(field_name)
:创建一个新的提取器,它在给定的主体字段中查找 JWT,必须配置了主体解析器才能使用该方法ExtractJwt.fromUrlQueryParameter(param_name)
:创建一个新的提取器,它在给定的URL查询参数中查找 JWTExtractJwt.fromAuthHeaderWithScheme(auth_scheme)
:为在授权标头中查找JWT创建一个新的提取器,期望该方案匹配 auth_scheme。ExtractJwt.fromAuthHeaderAsBearerToken()
:创建一个新的提取器,该提取器在请求头在中查找授权字段的tokenExtractJwt.fromExtractors([array of extractor functions])
:使用提供的提取器的array 创建一个新的提取器,每个提取器都按顺序尝试,直到返回一个标记。
jsonwebtoken
生成token方法
js
// jwt.sign(保存的信息, 口令, 参数)
import jsonwebtoken from 'jsonwebtoken';
jsonwebtoken.sign(data, secret, { expiresIn: '7d' });
- data:第一个参数是保存的信息
- secret:第二个参数是加密口令,加密的时候混入信息使用,解密的时候还要这个口令
- 参数对象:第三个参数是一个对象,expiresIn表示过期时间,单位为秒;可以使用7d表示7天,1h表示1小时
解码token方法
js
// jwt.verify(要解析的 token, 口令, 回调函数)
import jsonwebtoken from 'jsonwebtoken';
jsonwebtoken.verify(token, secret, (err, data) => {
if (err && err.message === 'invalid token') return res.send({ message: '无效 token', code:0})
if (err && err.message === 'jwt expired') return res.send({ message: 'token 失效', code: 0})
next()
})
- token: 必须是一个指定的 token
- 口令: 必须是加密时候的口令
- 回调函数: 接收结果
项目架构
- jwt目录用于封装token的初始化、生成和验证等方法
- types目录的index.d.ts是全局声明
.d.ts文件是ts用来声明变量,模块,type,interface等的。
.d.ts后缀的ts文件声明这些东西和在纯ts文件声明这些东西的区别如下:
在.d.ts声明变量或者模块等东西之后,在其他地方可以不用import导入这些东西就可以直接用,用时且有语法提示。 但是也不是说创建了.d.ts文件,里面声明的东西就能生效了,毕竟归根到底也是.ts文件,需要预编译,所以需要在tsconfig.json文件里面的include数组里面添加这个文件。include数组里面可以不用写.d.ts文件的绝对路径,可以通过glob通配符,匹配这个文件所在的文件夹或者是"祖宗级别"文件夹。
支持的glob通配符有:
*
:匹配0个或多个字符(不包括目录分隔符)?
:匹配一个任意字符(不包括目录分隔符)**
:递归匹配任意子目录
使用files
配置 files
属性用来指定要编译的文件,可以配置一个数组列表,里面包含指定文件的相对或绝对路径,编译器在编译的时候只会编译包含在files
中列出的文件,如果不指定,则取决于有没有设置include
选项。如果没有include
选项,则默认会编译根目录以及所有子目录中的文件。files
列出的路径必须是指定文件,而不是某个文件夹。
include
属性用来指定要编译要包含的路径列表,和files
的区别在于,这里的路径可以是文件夹,也可以是文件,可以使用相对和绝对路径。
使用ts-node运行ts文件时,会默认忽略
.d.ts
的后缀文件编译,会报错找不到引用类型。解决方案是使用ts-node --files xx.ts
代码实现
js
// src/jwt/index.ts
import { injectable } from 'inversify';
import passport from 'passport';
import jsonwebtoken from 'jsonwebtoken';
import { Strategy, ExtractJwt } from 'passport-jwt'
@injectable()
export class JWT {
private secret = 'goldenstar&gloria-queens'; // 加密口令
private jwtOptions = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), // 从请求中提取JWT的token
secretOrKey: this.secret // 口令
};
constructor() {
this.strategy();
}
// 中间件,中间件用于jwt验证
static middleware() {
return passport.authenticate('jwt', { session: false })
}
// 初始化jwt
public strategy() {
// 使用passport-jwt,它是passport一个使用jsonwebtoken的一种插件
const strategy = new Strategy(this.jwtOptions, (payload, done) => {
// payload是负载,done用于控制鉴权的成功或者失败
done(null, payload)
});
// 在passport中注册使用passport-jwt插件
passport.use(strategy);
}
// 生成token的方法
public createToken(data: object) {
// 接受三个参数,第一个是Payload负载,是自定义的相关信息,可以包含身份验证和授权等信息,如用户ID、角色、权限等。
// 第二个参数是私钥secret
// 第三个参数是设置token的过期时间,1h表示1小时,7d表示7天
return jsonwebtoken.sign(data, this.secret, { expiresIn: '7d' });
}
// 将passport和express进行关联,初始化passport
public init() {
return passport.initialize();
}
}
js
// main.ts
// 作为装饰器的基础,支持反射,放在最顶层
import 'reflect-metadata';
// 引入搭建服务端的express的类,InversifyExpressServer可以快速的创建express应用程序
import { InversifyExpressServer } from 'inversify-express-utils';
// 配置inversify容器,实现依赖注入
import { Container } from 'inversify';
// 引入express
import express from 'express';
// 引入prisma ORM框架
import { PrismaClient } from '@prisma/client';
// 引入User模块的controller
import { UserController } from './src/user/controller';
// 引入User模块的services
import { UserService } from './src/user/service';
// 引入封装好的数据库PrismaDatabase
import { PrismaDatabase } from './src/db';
// 引入封装好的JWT用于鉴权的类
import { JWT } from './src/jwt';
// 创建容器对象,这个对象会自动创建项目所需的对象
const container = new Container();
// 在容器对象中绑定封装的prisma工厂类,注入的实际是工厂创建器这个函数,容器对象会执行注入的函数,返回工厂函数
container.bind<PrismaClient>('PrismaClient').toFactory(() => {
// 工厂创建器函数,不需要使用context上下文对象
return () => {
// 工厂函数返回PrismaClient的实例化对象
return new PrismaClient(); // 创建prisma实例化对象
}
});
// 具体类型绑定自身,将依赖类注入到容器中,在容器中会创建依赖类的实例对象并存储在容器的实例对象中
// 将User模块的controller和User模块的services添加到容器中,就可以实现services和controller之间的依赖注入,controller依赖于services
container.bind<UserController>(UserController).toSelf();
container.bind<UserService>(UserService).toSelf();
container.bind<PrismaDatabase>(PrismaDatabase).toSelf();
container.bind<JWT>(JWT).toSelf();
// 创建 Express 服务器,需要接受一个容器对象
const sever = new InversifyExpressServer(container);
// 在.setConfig方法中进行express的中间件注册
sever.setConfig(app => {
// 注册express中间件,以便接受和解析前端请求时传入的json数据
app.use(express.json());
// 将passport注册为express的中间件,如此express和passport进行了关联
// 从容器实例对象中读取JWT类的实例对象,并执行JWT类的init方法,初始化passport并集成到express中
app.use(container.get(JWT).init())
});
// 所有注册的控制器和中间件添加到express应用程序并返回应用程序实例
const app = sever.build();
// 启动服务
app.listen(8089, () => {
console.log('8089 is running');
})
js
// src/user/controller.ts
// 引入装饰器将类设置注册为路由控制器
import { controller, httpGet, httpPost as Post } from 'inversify-express-utils'
// 引入inject装饰器,将controller层标注为依赖类,其参数是被依赖的services层
import { inject } from 'inversify'
// 引入services层
import { UserService } from './service';
// 引入类,用于变量的类型定义
import type { Request, Response } from 'express'
import { JWT } from '../jwt';
@controller('/user') //路由
export class UserController {
// 属性的修饰符是private readonly,属性名是userService,属性类型是UserService
private readonly userService: UserService;
private readonly jwt: JWT;
constructor(@inject(UserService) userService: UserService, @inject(JWT) jwt: JWT) {
// User类的构造函数中实例化依赖的services层的UserService类
this.userService = userService;
// 依赖声明
this.jwt = jwt;
}
// get请求接口,获取所有用户的信息,传入JWT.middleware()表示该接口使用传入的中间件
// 该接口就会去验证是否携带token,没有携带token就返回401
// 装饰器的第一个参数是路径,第二个参数是该路径需要用到的中间件
@httpGet('/userInfo',JWT.middleware())
public async getUserInfo(req: Request, res: Response) {
let result = await this.userService.getUserList();
res.send({
code: 200,
msg: '查询成功',
data: result
})
}
// post请求接口,添加一条用户信息
@Post('/create')
public async createUser(req: Request, res: Response) {
// req用于接受前端出入的参数
const info = req.body;
let result = await this.userService.addUser(info)
// res用于给前端返回数据
res.send(result);
}
// 登录接口
@Post('/login')
public async userLogin(req: Request, res: Response){
let result = await this.userService.loginHandle(req.body);
if(result.code == 200){
res.send({
success:true,
msg:'登陆成功',
token:this.jwt.createToken(result.user) // 生成token
});
}else if(result.code == 400){
res.send({
success:true,
msg:'用户不存在'
});
}else if(result.code == 500) {
res.send({
success:true,
msg:'密码错误'
});
}
}
}
哪个接口需要token验证就把jwt验证的中间件往那个接口加就可以了
js
// src/user/service.ts
// 引入注入装饰器,作用是将修饰的类变成一个可以注入类的可依赖类
import { injectable, inject } from 'inversify'
import { UserDto } from './user.dto'
import { plainToClass } from 'class-transformer' //dto验证
import { validate } from 'class-validator' //dto验证
import { PrismaDatabase } from '../db'
@injectable()
export class UserService {
private readonly prismaDataBase: PrismaDatabase;
constructor(@inject(PrismaDatabase) prismaDataBase: PrismaDatabase ) {
// 依赖声明
this.prismaDataBase = prismaDataBase;
}
public async getUserList() {
// 逻辑处理后的数据,将处理后的结果返回
return await this.prismaDataBase.prisma.user.findMany()
}
public async addUser(userData: UserDto) {
// userData接受前端传入的参数
const user = plainToClass(UserDto, userData);
const errors = await validate(user);
if (errors.length != 0) {
// 错误信息数组有值,验证未通过
return errors
} else {
// 逻辑处理后的数据
let result = await this.prismaDataBase.prisma.user.create({
data: user
});
// 将处理后的结果返回
return result
}
}
public async loginHandle(userInfo:UserLogin){
const passworld = 'admin123';
if(userInfo.passworld == passworld){
let result = await this.prismaDataBase.prisma.user.findMany({
where:{
name:userInfo.name
}
});
if(result.length != 0){
return {
code:200,
user:result[0]
};
}else{
return {
code:400
};
}
}else {
return {
code:500
};
}
}
}
js
// types/index.d.ts
type UserLogin = {
name:string;
passworld:string
}
request.http
测试接口:
js
GET http://localhost:8089/user/userInfo HTTP/1.1
token验证失败展示:(请求接口时未携带token)
js
POST http://localhost:8089/user/login HTTP/1.1
Content-Type: application/json
{
"name":"gloria",
"passworld":"admin123"
}
密码错误展示:
用户名错误展示:
获取token展示:
Bearer后面需要添加一个空格,然后是token
js
GET http://localhost:8089/user/userInfo HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6Imdsb3JpYSIsImVtYWlsIjoiaW5mb0BnbmF0aW9uLmhrIiwiaWF0IjoxNzE2MzQxNDgyLCJleHAiOjE3MTY5NDYyODJ9.r0Z3G9-qajx67T8pp97K68-vKX5rfQc73sSCjGofPfU
token验证成功展示:(请求接口时携带token)
从token中获取保存的信息
当接口需要token验证并添加了jwt验证的中间件时,就可以直接通过req.user
获取用户信息。
需要先安装passport的ts声明模块,这样才会有类型提示
js
yarn add @types/passport
该User
接口可进行类型扩展
扩充后有类型提示
js
GET http://localhost:8089/user/userInfo HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6Imdsb3JpYSIsImVtYWlsIjoiaW5mb0BnbmF0aW9uLmhrIiwiaWF0IjoxNzE2MzQxNDgyLCJleHAiOjE3MTY5NDYyODJ9.r0Z3G9-qajx67T8pp97K68-vKX5rfQc73sSCjGofPfU