nodejs ,koa-bodyparser,koa-router,koa-static, JWT 编写的一个可权限控制的后端(ES6 style)以及展示用户登录、注册的前端

该文章演示了一个使用Koa.js框架构建的应用,包括处理登录和注册功能,采用JSONWebTokens(JWT)进行用户身份验证,并通过中间件实现授权。用户数据存储在内存中,登录成功后生成并返回JWT,客户端保存用于后续请求的授权。

1、安装组件

npm install koa koa-bodyparser koa-router koa-static koa-views jsonwebtoken
  1. 项目结构

project/
├── public/
│   ├── index.html
│   └── dashboard.html
└── App.mjs

2、App.mjs

import Koa from 'koa';
import koaBody from 'koa-bodyparser';
import koaRouter from 'koa-router';
import koaStatic from 'koa-static';
import jwt from 'jsonwebtoken';
import fs from 'fs';
import path from 'path';

const app = new Koa();
const router = new koaRouter();

app.use(koaBody());
app.use(koaStatic('public'));
/*
  模拟用户密码数据库
*/
const users = [
  {
    email: 'user1@example.com',
    password: 'password1',
    role: 'user',
  },
  {
    email: 'user2@example.com',
    password: 'password2',
    role: 'user',
  },
  {
    email: 'admin@example.com',
    password: 'adminpassword',
    role: 'admin',
  },
];

function generateToken(user) {
  const token = jwt.sign({ email: user.email, role: user.role }, 'secret_key', {
    expiresIn: '1h',
  });
  return token;
}

function authenticate(email, password) {
  const user = users.find((user) => user.email === email && user.password === password);
  return user;
}

router.post('/api/login', async (ctx) => {
  const { email, password } = ctx.request.body;
  const user = authenticate(email, password);
  if (!user) {
    ctx.status = 401;
    ctx.body = { message: 'Invalid login credentials' };
    return;
  }
  const token = generateToken(user);
  ctx.body = { token };
});

router.post('/api/register', async (ctx) => {
  const { email, password } = ctx.request.body;
  const user = { email, password, role: 'user' };
  users.push(user);
  const token = generateToken(user);
  ctx.body = { token };
});

function authMiddleware() {
  return async (ctx, next) => {
    const authHeader = ctx.request.headers.authorization;
    if (!authHeader) {
      ctx.status = 401;
      ctx.body = { message: 'Authorization header missing' };
      return;
    }
    const [bearer, token] = authHeader.split(' ');
    if (bearer !== 'Bearer' || !token) {
      ctx.status = 401;
      ctx.body = { message: 'Invalid Authorization header' };
      return;
    }
    try {
      const decoded = jwt.verify(token, 'secret_key');
      ctx.state.user = decoded;
      await next();
    } catch (err) {
      ctx.status = 401;
      ctx.body = { message: 'Invalid or expired token' };
    }
  };
}

router.get('/api/user', authMiddleware(), async (ctx) => {
  ctx.body = ctx.state.user;
});

router.get('/', async (ctx) => {
  ctx.redirect('/index.html');
});

app.use(router.routes());
app.use(router.allowedMethods());

const port = process.env.PORT || 3000;
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});
  1. index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Example App</title>
  </head>
  <body>
    <h1>Welcome to Example App</h1>
    <div>
      <h2>Login</h2>
      <form id="login-form">
        <div>
          <label for="email">Email:</label>
          <input type="email" name="email" id="email" />
        </div>
        <div>
          <label for="password">Password:</label>
          <input type="password" name="password" id="password" />
        </div>
        <div>
          <button type="submit">Login</button>
        </div>
      </form>
    </div>
    <div>
      <h2>Register</h2>
      <form id="register-form">
        <div>
          <label for="email">Email:</label>
          <input type="email" name="email" id="register-email" />
        </div>
        <div>
          <label for="password">Password:</label>
          <input type="password" name="password" id="register-password" />
        </div>
        <div>
          <button type="submit">Register</button>
        </div>
      </form>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
      $(document).ready(() => {
        $('#login-form').submit((event) => {
          event.preventDefault();
          const email = $('#email').val();
          const password = $('#password').val();
          $.post('/api/login', { email, password })
            .done((response) => {
              localStorage.setItem('token', response.token);
              window.location.href = '/dashboard.html';
            })
            .fail((xhr, status, error) => {
              alert(xhr.responseJSON.message);
            });
        });

        $('#register-form').submit((event) => {
          event.preventDefault();
          const email = $('#register-email').val();
          const password = $('#register-password').val();
          $.post('/api/register', { email, password })
            .done((response) => {
              localStorage.setItem('token', response.token);
              window.location.href = '/dashboard.html';
            })
            .fail((xhr, status, error) => {
              alert(xhr.responseJSON.message);
            });
        });
      });
    </script>
  </body>
</html>
  1. dashboard.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Dashboard</title>
  </head>
  <body>
    <h1>Welcome to the Dashboard</h1>
    <p>You are logged in!</p>

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
      $(document).ready(() => {
        const token = localStorage.getItem('token');
        if (!token) {
          window.location.href = '/index.html';
        } else {
          $.ajax({
            url: '/api/user',
            method: 'GET',
            headers: { Authorization: `Bearer ${token}` },
          })
            .done((response) => {
              $('h1').append(`, ${response.email}!`);
            })
            .fail((xhr, status, error) => {
              alert(xhr.responseJSON.message);
              window.location.href = '/index.html';
            });
        }
      });
    </script>
  </body>
</html>

node.exe App.mjs 启动服务

在浏览器中访问 http://localhost:3000/ 查看应用程序。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值