这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天
React 16.8+的生命周期分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段。
fs模块
读文件
<script>
const fs = require('fs')
fs.readFile('./node1.txt', 'utf8', function(err, dataStr) {
if(err){
return console.log('读取文件失败'+err.message)
}
console.log('成功'+dataStr)
})
</script>
写文件
<script>
const fs = require('fs')
fs.writeFile('./node1.txt', '内容', function(err, dataStr) {
if(err){
return console.log('读取文件失败'+err.message)
}
console.log('成功'+dataStr)
})
</script>
- _ _dirname表示当前文件的目录
- 处理路径问题的时候 一定要用__dirname拼接地址
-
fs.writeFile(__dirname'./node1.txt', '内容', function(err, dataStr) {
path模块
- 是node官方提供的 用来处理路径的模块,它提供了一系列的方法和属性 用来满足用户对路径的处理需求
//path.join()用来将多个路径片段拼接成一个完整的路径字符串
//path.basename()方法,用来从路径字符串中 讲文件名解析出来
//path.extname()可以获取路径中的拓展名部分
- 先导入
-
const path = requir('path')
时钟案例
const fs = require('fs')
const path = require('path')
const regstyle = /<style>[\s\S]*</style>/
const regscript = /<script>[\s\S]*</script>/
fs.readFile(path.join(__dirname, './时钟案例.html'), 'utf8', function(err, dataStr) {
if (err) return console.log('读取失败' + err.message)
//读取成功后 分别调对应的三个方法,拆出css html js
resolveCSS(dataStr)
resolveJS(dataStr)
resolvehtml(dataStr)
})
function resolveCSS(htmlStr) {
const r1 = regstyle.exec(htmlStr)
const newcss = r1[0].replace('<style>', '').replace('</style>', '')
fs.writeFile(path.join(__dirname, './时钟案例.css'), newcss, function(err) {
if (err) return console.log('写入css样式失败!' + err.message)
console.log('写入样式文件成功!')
})
}
function resolveJS(htmlStr) {
const r2 = regscript.exec(htmlStr)
const newjs = r2[0].replace('<script>', '').replace('</script>', '')
fs.writeFile(path.join(__dirname, './时钟案例.js'), newjs, function(err) {
if (err) return console.log('写入js样式失败!' + err.message)
console.log('写入样式文件成功!')
})
}
function resolvehtml(htmlStr) {
htmlStr.replace(regstyle, '<link>')
}
http模块
- 导入http模块
- 创建web服务器实例
- 为服务器实例绑定request事件,监听客户端的请求
- 启动服务器
const http = require('http')
//创建wub服务器实例
const server = http.createServer()
//为服务器实例绑定request事件,监听客户端的请求
server.on('request', function(req, res) {
console.log('someone visit our web server')
})
//启动服务器
server.listen(8080, function() {
console.log('server running at http://127.0.0.1:8080')
})
req请求对象
- 只要服务器接受到了客户端的请求,就会调用通过server.on()为服务器绑定的request事件处理函数,如果想在事件处理函数中,访问与客户端相关的数据或者属性 可以:
-
req.url是客户端请求的url地址 req.method是客户端的method请求类型 -
const http = require('http') const server = http.createServer() server.on('request', (req) => { const url = req.url const method = req.method const str = `your request url; is${url},and request method is ${method}` console.log(str) }) server.listen(8080, () => { console.log('server running at http://127.0.0.1:8080') })
res响应对象
const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
const url = req.url
const method = req.method
const str = `your request url; is${url},and request method is ${method}`
console.log(str)
//调用res.end()向客户端响应一些内容
res.end(str)
})
server.listen(8080, () => {
console.log('server running at http://127.0.0.1:8080')
})
解决res返回中文时乱码的问题
const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
const url = req.url
const method = req.method
const str = `请求的地址是${url},请求的方式是 ${method}`
//调用res.setHeader()方法,设置Content-type响应头,解决中文乱码问题
res.setHeader('Content-Type', 'text/html; charset=utf-8')
//调用res.end()向客户端响应一些内容
res.end(str)
})
server.listen(8080, () => {
console.log('server running at http://127.0.0.1:8080')
})
根据不同的url返回不同内容
-
获取请求的url地址
-
设置默认响应内容
-
判断用户请求是什么
-
设置content-type响应头
-
使用res.end()
let url=.... if(url===){ }====
模块化
概念
- 是模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程,对于整个系统来说,模块是可组合 分解和更换的单元
好处
- 提高代码的复用性
- 提高了代码的可维护性
- 可以实现按需加载
node.js
- 内置模块 fs path http
- 自定义模块 用户创建的js
-
const custom=require(./custom.js) - 第三方模块 由第三方开发出来的模块 并非官方提供出来的
向外共享模块作用域中的成员
module
- 在每个.js模块中都有一个module对象,他里面存储了和当前模块有关的信息
- 在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用
- module.exports
- 在自定义模块中
-
module.exports.username='zs' module.exports.sayhello=function(){ console.log('hello') } 以上为自定义模块 一下调用 const m=require('./自定义模块') console.log(m) - exports跟module.exports一样
npm与包
包
-
第三方模块又叫包
-
/* npm i moment@版本号 版本号2.23.0 第一位是大版本 第二位是功能版本 第三位是bug修复 */ -
/* npm init -y 在执行命令处的目录中,快速新建package.json文件 项目文件名必须是英文 不能出现空格 配置文件中的dependencies节点 专门记录npm */一次性安装所有的包
/*npm i 读取package.json文件 然后安装所有包 *//* 卸载包 npm uninstall */
devDependencies节点
- 如果某些包只在项目开发阶段会用到,在项目上线之后不会用到
-
/*npm i 包名 -D*/ - 如果都需要用到 把这些包记录到dependencies节点中
nrm工具
- 更加方便的切换下包的镜像源 利用nrm提供的终端命令,可以快速查看和切换下包的镜像源
-
/* npm i nrm -g nrm ls 查看所有镜像源 nrm use taobao */
项目包
- 那些被安装在项目的node_modules目录中的包 都是项目包
- 分为两类开发依赖包 记录到Devdependencies中的 只在开发期间会用到
- 核心依赖包 被记录到dependencies节点中的包 在开发期间和项目上线后都会用到
i5ting_toc
/*i5ting_toc-f 要转换的md文件路径 -O
把md文件转换为html
*/
开发属于自己的包
function dateFormat(dtStr) {
const dt = new Date(dtStr)
const y = padzero(dt.getUTCFullYear())
const m = padzero(dt.getMonth() + 1)
const n = padzero(dt.getDate())
const hh = padzero(dt.getHours())
const mm = padzero(dt.getMinutes())
const ss = padzero(dt.getSeconds())
return `${y}-${m}-${n} ${hh}:${mm}:${ss}`
}
function padzero(n) {
return n > 9 ? n : '0' + n
}
//定义转移html字符的函数
function htmlEscape(htmlstr) {
return htmlstr.replace(/<|>"|&/g, (match) => {
switch (match) {
case '<':
return '<'
case '>':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
//定义还原html字符的函数
function htmlunEscape(htmlstr) {
return htmlstr.replace(/<"|&/g, (match) => {
switch (match) {
case '<':
return '<'
case '>;':
return '>'
case '"':
return '"'
case '&':
return '&'
}
})
}
module.exports = {
dateFormat,
htmlEscape,
htmlunEscape
}
讲不同功能进行模块化拆分
- 将格式化时间的功能,拆分
- 将处理字符串的功能 拆分
- 在index.js中 导入两个模块 得到需要向外共享的方法
- 在index.js中 使用module.exports 把对应的方法共享出去
- 在发布包的时候 要登录到官方地址 不可以镜像
-
/*登录 npm login*/ -
/*发布 npm publish* 删除 npm unpublish 包名 --force 只可以删除72小时之内的 /
模块的加载机制
- 优先从缓存中加载,模块在第一次加载后会被缓存 这也意味着多次调用require()不会导致模块的代码被执行多次
- 内置模块优先机制最高
express
认识express
- web框架
- 本质是npm的一个第三方包
- 安装 npm i express
const express = require('express')
const app = express()
app.listen(8080, () => {
console.log('express server running at http://127.0.0.1:8080')
})
请求get post
app.get('./user', (req, res) => {
res.send({ name: 'sss', age: 20 })
})
app.post('./user', (req, res) => {
res.send({ name: 'sss', age: 20 })
})
获取url携带的查询参数
req.query
获取动态参数
req.params
app.get('./user:id', (req, res) => {
console.log(req.params)
res.send(req.params)
})
:id是动态参数
托管静态资源
/*express.static()通过他 我们可以非常方便的创建一个静态资源服务器
app.use(express.static('public'))
就可以访问public中所有文件了*/
const express = require('express')
const app = express()
app.use(express.static('../前端/node.js—资料/day3/素材/clock'))
app.listen(9090, () => {
console.log('express server running at http://127.0.0.1:9090')
})
Express路由
在Express中 路由指客户端的请求与服务器处理函数之间的映射关系 分为三部分 请求的类型 请求的url地址 处理函数
const express = require('express')
const app = express()
app.get('/', (req, res) => {
res.send('hello word')
})
app.post('/', (req, res) => {
res.send('post request')
})
app.listen(8080, () => {
console.log('http://127.0.0.1:8080')
})
模块化管理
-
为了方便管理 不建议将路由直接挂载到app上,而是推荐将路由由抽离为单独的模块
- 创建路由模块对应的.js文件
- 调用express.router()函数创建路由对象
- 向路由对象上挂载具体的路由
- 使用module.exports向外共享路由对象
- 使用app.use()函数注册路由模块
//这是路由模块 const express = require('express') //创建路由对象 const router = express.Router() //挂载具体的路由 router.get('/user/list', (req, res) => { res.send('get your list') }) router.post('/user/list', (req, res) => { res.send('add your list') }) //向外导出路由对象 module.exports = router ========================================================================== //注册路由模块的方法 ========================================================================== const express = require('express') const app = express() //导入路由 const router = require('./router') //注册路由模块 app.use(router) app.listen(8080, () => { console.log('http://127.0.0.1:8080') }) -
为路由挂载统一的访问前缀
app.use('地址' router)
Express的中间件
- 中间环节
- 当一个请求到达express的服务器之后 可以连续调用多个中间件 从而对这次请求进行预处理
- 中间件处理函数 function函数中必须有 req res next函数 路由处理函数只有req res
- next函数的作业 他表示把流转关系转交给下一个中间件或路由
const express = require('express')
const app = express()
//定义一个最简单的中间件函数
const mw = function(req, res, next) {
console.log('最简单的中间件')
next()
}
app.listen(8080, () => {
console.log('http://127.0.0.1:8080')
})
全局中间件
将中间件注册为全局
app.ues(function(req, res, next) {
console.log('最简单的中间件')
next()
})
定义多个全局中间件(按顺序调用)
app.ues(function(req, res, next) {
console.log('最简单的中间件1')
next()
})
app.ues(function(req, res, next) {
console.log('最简单的中间件2')
next()
})
app.get('./', (req, res) => {
res.send('home page')
})
局部中间件
const m1=function(req, res, next) {
console.log('最简单的中间件1')
next()
}
const m2=function(req, res, next) {
console.log('最简单的中间件2')
next()
}
app.get('./',m1,m2, (req, res) => {
res.send('home page')
})
作用
-
多个中间件之间 共享同一份req和res 基于这样的特性 我们可以在上游的中间件中 统一为req或者res对象添加自定义的属性或方法 供下游的中间件或者路由使用
const express = require('express') const app = express() //定义一个最简单的中间件函数 const mw = function(req, res, next) { const time = Date.now() //为req对象挂载自定义属性 从而把时间共享给后面的所有路由 req.startTime = time next() } app.get('./', (req, res) => { res.send('home page' + req.startTime) }) app.listen(8080, () => { console.log('http://127.0.0.1:8080') })
注意
- 一定要在路由之前注册
- 客户端发送过来的请求,可以连续调用多个中间件处理
- 执行完中间件的业务代码之后,不要忘记调用next()函数
- 为了防止代码逻辑混乱 调用next()函数后不要再写额外的代码
- 连续调用多个中间件时,多个中间件之间,共享req和res对象
中间件的分类
应用级别
- 通过app.use() app.get() 绑定到app实例上的中间件 叫做应用级别的中间件
路由级别
- 绑定到express.router()实例上的中间件 叫做路由级别的中间件
错误级别
- 专门来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
- function处理函数中,必须有4个形参 形参顺序从前到后,分别是(err,req,res,next)
- 所有错误级别的中间件都要保存在路由之后*(与其他级别的中间件不同)*
Express内置
- express.static 快速托管静态资源
- express.json 解析json格式的请求体数据
-
const express = require('express') const app = express() app.use(express.json()) app.post('./', (req, res) => { console.log(req.body) res.send('ok') }) - express.urlencoded 解析url-ecoded格式的请求体数据
-
app.use(express.urlencoded({ extended: false })) app.post('./user', (req, res) => { console.log(req.body) res.send('ok') })
第三方
- body-parser 解析请求体数据
-
const express = require('express') const app = express() //导入 const parser = require('body-parser') //使用app.use()注册中间件 app.use(parser.urlencoded({ extended: false })) app.post('./', (req, res) => { //如果没有配置任何解析表单数据的中间件,则req.body 默认等于undefined res.send(req.body) }) app.listen(8080, function { console.log('http://127.0.0.1:8080') })
自定义中间件
-
定义中间件
-
监听req的data事件
const express = require('express') const app = express() app.use((req, res, next) => { //定义zhongj件具体的业务逻辑 //定义一个str字符串 专门用来存储客户端方送过来的请求体数据 let str = '' //监听req的data事件 req.on('data', (chunk) => { str += chunk }) }) -
监听req的end事件
-
使用querysring模块解析请求体数据
-
querystring 专门用来处理查询字符串的 通过这个模块提供的parse()函数 可以轻松把查询字符串解析成对象的格式
const express = require('express') const app = express() const qs = require('URLSearchParams') app.use((req, res, next) => { //定义zhongj件具体的业务逻辑 //定义一个str字符串 专门用来存储客户端方送过来的请求体数据 let str = '' //监听req的data事件 req.on('data', (chunk) => { str += chunk }) req.on('end', () => { //console.log(str) //todo把字符串格式的请求体数据 解析成对象格式 const body = qs.parse(str) console.log(body) }) })
-
-
将解析出来的数据对象挂载为req.body
const express = require('express') const app = express() const qs = require('URLSearchParams') app.use((req, res, next) => { //定义zhongj件具体的业务逻辑 //定义一个str字符串 专门用来存储客户端方送过来的请求体数据 let str = '' //监听req的data事件 req.on('data', (chunk) => { str += chunk }) req.on('end', () => { //console.log(str) //todo把字符串格式的请求体数据 解析成对象格式 const body = qs.parse(str) req.body = body next() }) }) app.post('./user', (req, res) => { res.send(req.body) }) -
将自定义中间件封装为模块
使用express写接口
get/post
const { urlencoded } = require('body-parser')
const express = require('express')
const app = express()
//配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
const router = require('./apiRouter')
//把路由模块 注册到app上
app.use('/api', router)
nodemon
- 能够监听项目文件的变动,当代吗被修改后 nodemon会自动帮我们重启项目
cors解决接口跨域问题
- jsonp也可以解决 但是jsonp无法解决post 只可以get
方法:
- 运行npm i cors
- const cors=require(‘cors’)
- app.use(cors())
<body>
<button id="btnget">GET</button>
<button id="btnpost">post</button>
<script>
$(function() {
$('#btnget').on('click', function() {
$.ajax({
type: 'get',
url: 'http://127.0.0.1/api/get',
data: {
name: 'zs',
age: 20
},
seccess: function(res) {
console.log(res)
},
})
})
$('#btnpost').on('click', function() {
$.ajax({
type: 'post',
url: 'http://127.0.0.1/api/post',
data: {
bookname: 'zs',
name: 20
},
seccess: function(res) {
console.log(res)
}
})
})
})
</script>
</body>
const { urlencoded } = require('body-parser')
const express = require('express')
const app = express()
//配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
const cors = require('cors')
app.use(cors())
const router = require('./apiRouter')
//把路由模块 注册到app上
app.use('/api', router)
cors响应头
res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD')
cors请求的分类
-
简单请求
- 请求方式是get post head
- 客户端与服务器之间只会发生一次请求
-
预检请求
- 请求方式是三个之外的
- 请求头中包含自定义头部字段
- 向服务器发送了application/json格式的数据
- 客户端与服务器之间发生两次请求
jsonp接口
概念
- 浏览器端通过script标签的src属性 请求服务器上的数据 同时 服务器返回一个函数的调用 这种请求数据的方式叫做jsonp
特点
- jsonp不属于真正的Ajax请求 因为没有使用xmlhttprequest对象
- jsonp仅支持get请求 不支持post put delete等请求
步骤
- 获取客户端发送过来的回调函数的名字
- 得到要通过jsonp形式发送给客户端的数据
- 根据前两部得到的数据 拼接出一个函数调用的字符串
- 坝上一步拼接得到的字符串 响应给客户端的 script标签进行解析执行
app.get('/api/jsonp', (req, res) => {
//定义JSONP接口具体的实现过程
const funcName = req.query.callback
const data = { name: 'zd', age: 22 }
const scriptstr = `${funcName}(${JSON.stringify(data)})`
res.send(scriptstr)
})
前后端的身份认证
分类
基于服务器渲染的web开发模式
- 服务器发送给客户端的html的页面 实在服务器通过字符串的拼接 动态生成的 因此 客户端不需要使用Ajax这样的技术额外请求页面的数据
- 优点:前端耗时少 有利于SEO
- 缺点 占用服务器端资源 不利于前后端分离 开发效率低
前后端分离的web开发模式
- 后端提供接口 前端用Ajax调用接口
- 优点:开发体验好 用户体验好 减轻了服务器端的渲染压力
- 缺点:不利用网站的SEO 可以利用Vue提供的ssr技术能够很好的解决SEO问题
如何选择
企业级的网站 主要功能是展示而没有复杂的交互 并且需要良好的SEO 则这时我们就需要使用服务器端的渲染
后台管理项目 交互性比较强 不需要考虑SEO 那么就可以使用前后端分离的开发模式
身份认证
又称身份验证 鉴权 是指通过一定的手段 完成对用户身份的确认
session认证机制
http协议的无状态性 是指客户端的每次http请求都是独立的 连续多个请求之间没有直接的关系 服务器不会主动保留每次http请求的状态
如何突破http的无状态的限制 --------------------------- cookie
cookie
是储存在用户浏览器中的一段不超过4kb的字符串 由一个名称 一个值 和其他几个用于控制cookie有效期 安全性 使用范围的可选属性组成
-
客户端第一次请求服务器的时候 服务器通过响应头的形式,想客户端发送一个身份认证的cookie 客户端会自动将cookie保存在浏览器中 随后 在客户端浏览器每次请求服务器的时候 浏览器会自动将身份认证相关的cookie 通过请求头的形式 发送给服务器 服务器即可验证客户端的身份
-
cookie不具备安全性 不用拿cookie写隐私信息
-
提高身份认证的安全性
session认证机制
在express中使用session认证
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// TODO_01:请配置 Session 中间件
const session = require('express-session')
app.use(
session({
secret: 'itheima',
resave: false,
saveUninitialized: true,
})
)
// 托管静态页面
app.use(express.static('./pages'))
// 解析 POST 提交过来的表单数据
app.use(express.urlencoded({ extended: false }))
// 登录的 API 接口
app.post('/api/login', (req, res) => {
// 判断用户提交的登录信息是否正确
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登录失败' })
}
// TODO_02:请将登录成功后的用户信息,保存到 Session 中
// 注意:只有成功配置了 express-session 这个中间件之后,才能够通过 req 点出来 session 这个属性
req.session.user = req.body // 用户的信息
req.session.islogin = true // 用户的登录状态
res.send({ status: 0, msg: '登录成功' })
})
// 获取用户姓名的接口
app.get('/api/username', (req, res) => {
// TODO_03:请从 Session 中获取用户的名称,响应给客户端
if (!req.session.islogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({
status: 0,
msg: 'success',
username: req.session.user.username,
})
})
// 退出登录的接口
app.post('/api/logout', (req, res) => {
// TODO_04:清空 Session 信息
req.session.destroy()
res.send({
status: 0,
msg: '退出登录成功',
})
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1:80')
})
JWT认证机制
session机制要配合cookie才能实现 由于cookie默认不支持跨域访问 所以 当涉及到前端跨域请求后端接口的时候 需要做很多额外的配置 才能实现跨域session认证
- 当前端请求后端接口不存在跨域问题的时候 推荐使用session 身份
- JSON Web Token
- 通常由三部分组成 分别是 header payload(有效荷载) signature(签字)
- payload是真正的加密信息 header signature是保证安全性的
- 客户端收到服务器返回的jwt之后 通常会将它储存在localstorage或者sessionstorage中
- 推荐的做法是把jwt放在http请求头的authorization字段中
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')
// 允许跨域资源共享
const cors = require('cors')
app.use(cors())
// 解析 post 表单数据的中间件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
// TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey
const secretKey = 'itheima No1 ^_^'
// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^/api//] }))
// 登录接口
app.post('/api/login', function (req, res) {
// 将 req.body 请求体中的数据,转存为 userinfo 常量
const userinfo = req.body
// 登录失败
if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
return res.send({
status: 400,
message: '登录失败!',
})
}
// 登录成功
// TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
// 参数1:用户的信息对象
// 参数2:加密的秘钥
// 参数3:配置对象,可以配置当前 token 的有效期
// 记住:千万不要把密码加密到 token 字符中
const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
res.send({
status: 200,
message: '登录成功!',
token: tokenStr, // 要发送给客户端的 token 字符串
})
})
// 这是一个有权限的 API 接口
app.get('/admin/getinfo', function (req, res) {
// TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
console.log(req.user)
res.send({
status: 200,
message: '获取用户信息成功!',
data: req.user, // 要发送给客户端的用户信息
})
})
// TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res, next) => {
// 这次错误是由 token 解析失败导致的
if (err.name === 'UnauthorizedError') {
return res.send({
status: 401,
message: '无效的token',
})
}
res.send({
status: 500,
message: '未知的错误',
})
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(8888, function () {
console.log('Express server running at http://127.0.0.1:8888')
})
- 定义secret秘钥 用于加密 解密
在登录成功后生成jwt字符串
- 调用jsonwebtoken 包提供sign()方法 将用户的信息加密成jwt字符串 响应给客户端
-
jwt.sign()生成jwt字符串 三个参数分别是 用户信息对象 加密秘钥 配置对象