赞
踩
路由组件(生成token)
const Router = require('@koa/router') const jwt = require('jsonwebtoken'); const router = new Router() const mockDbUserInfo = [ { nickname: 'xxxliu', username: 'Tom', password: 123456, icon: 'url1' }, { nickname: 'xxx', username: 'John', password: 123456, icon: 'url2' }, ] const secretKey = 'xxxliu key'; router.post('/api/home', async ctx => { ctx.response.body = { msg: '主页', code: 'success' } }) router.post('/api/login', async ctx => { try { const { username, password } = ctx.request.body; //mock查db操作 const { nickname, password: pwd, icon } = mockDbUserInfo.find(item => item.username === username) || {}; if (!nickname) { ctx.response.body = { msg: "不存在该用户", code: "failed", }; return; } if (pwd !== password) { ctx.response.body = { msg: "密码输入错误", code: "error", }; return; } // 构建 JWT 头部 const header = { alg: 'HS256', // 签名算法,例如使用 HMAC SHA-256 typ: 'JWT', // Token 的类型 }; const payload = { username }; const options = { expiresIn: '1h', // 设置 JWT 的过期时间 header, // 将 header 选项包含在 options 中 }; //生成token const token = jwt.sign(payload, secretKey, options); ctx.response.body = { nickname, icon, code: "success", msg: "登录成功", }; ctx.cookies.set( 'myToken', token, { maxAge: 1 * 60 * 60 * 1000, httpOnly: false } ) } catch (error) { ctx.response.body = error; } }) module.exports = router;
token解析
const jwt = require('jsonwebtoken'); const secretKey = 'xxxliu key'; const verifyToken = async (ctx, next) => { try { const { url } = ctx.request; //走登录页不鉴权 const requestUrl = url.split("?")[0]; const noVerifyList = ["/api/login"]; const noVerify = noVerifyList.includes(requestUrl); if (noVerify) { await next(); } else { //拿到请求头的参数 const authorization = ctx.request.header["authorization"]; const username = ctx.request.body["username"]; if (authorization.startsWith('Bearer ')) { const token = authorization.slice(7); const { exp, username: userName } = jwt.verify(token, secretKey) || {}; if (userName !== username) { ctx.response.body = { code: "error", msg: "无效的token, 请重新登录", }; } else if (exp * 1000 > Date.now()) { ctx.response.body = { code: "error", msg: "登录信息已过期, 请重新登录", }; } else { await next(); } } else { ctx.response.body = { code: "error", msg: "无效的token, 请重新登录", }; } } } catch (err) { if (err.name == "TokenExpiredError") { ctx.body = { code: "error", msg: "token已过期, 请重新登录", }; } else if (err.name == "JsonWebTokenError") { ctx.body = { code: "error", msg: "无效的token, 请重新登录", }; } } }; module.exports = verifyToken;
注册中间件
const Koa = require('koa') const path = require('path') const sendfile = require('koa-sendfile') const static = require('koa-static') const bodyParser = require("koa-bodyparser") const router = require('./server/api-router.js') const assets = require('./server/assets-router') const verifyToken = require('./server/verifyToken.js'); const app = new Koa() // static app.use(static(path.resolve(__dirname, 'public'))) //body app.use(bodyParser()); //midware auth app.use(verifyToken); // api app.use(router.routes()).use(router.allowedMethods()) // assets app.use(assets.routes()).use(assets.allowedMethods()) // 404 app.use(async (ctx, next) => { await next() if (ctx.status === 404) { await sendfile(ctx, path.resolve(__dirname, 'public/index.html')) } }) app.listen(3000, () => { console.log(`> http://127.0.0.1:3000`) })
前端请求(登录+打开主页)
import { useState, useEffect } from 'react' import getTokenFromCookie from './tools' function App() { const [response, setResponse] = useState({}) const token = getTokenFromCookie(); useEffect(() => { fetch('/api/login', { method: 'post', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ username: 'Tom', password: 123456 }) }) .then(res => res.json()) .then(res => { setResponse(res); }) // fetch('/api/home', { // method: 'post', // headers: { // 'Content-Type': 'application/json', // 'Authorization': `Bearer ${token}`, // }, // body: JSON.stringify({ // username: 'Tom', // password: 123456 // }) // }) // .then(res => res.json()) // .then(res => { // setResponse(res); // }) }, []) const { nickname, icon, msg } = response || {} return ( <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}> { nickname ? <div> <h1>icon: {icon}</h1> <h1>nickname: {nickname}</h1> <h1>msg: {msg}</h1> </div> : <h1>msg: {msg}</h1> } </div> ) } export default App
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。