在next.js的13.2.1版本中使用中间件,实现禁止特定ip访问网址所有页面

在实现实现禁止特定ip访问网址所有页面时,有两种方式,一种是针对单个页面,另一种是针对整个网站

在pages/api中创建文件使用,针对单个页面,也可以应用于所有页面

之前是在pages/api下创建的中间件去实现的,但是使用pages/api中的中间件需要手动调用,也就是说如果全部页面都需要使用的话,那么就要在所有页面中去调用,页面刷新会重新加载getIp文件,也就是说会重置cachedData、cacheTime这些变量,不刷新的话就不会每次只会执行handler函数内部的代码,所以可以使用缓存,每次刷新后,就会重新请求一次,不刷新且满足条件的,就会使用缓存。
pages/api/getIp.js

// ipBlockMiddleware.js
 
 
 
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import axios from 'axios'
let cachedData = null;
let cacheTime = null;
/**
* 获取地区信息
* @param {*} req
* @param {*} res
*/
export default async function handler(req, res, next) {
  const blockedIPs = ['82.116.192.22']; // 被禁止的IP列表:塞浦路斯
  // 设置缓存有效期为10分钟
  const TEN_MINUTES = 10 * 60 * 1000;
  try {
    // 检查缓存是否有效
  if (cachedData && cacheTime && Date.now() - cacheTime < TEN_MINUTES) {

  } else {
    // 获取ip信息
    const response = await axios.get('https://api.ipdatacloud.com/v2/query?key=xxx',{ timeout: 5000 })
    // 更新缓存
    cachedData = response.data.data.location
    cacheTime = Date.now();
  }
    
  if (blockedIPs.includes(cachedData?.ip)) {
      res.statusCode = 403
      res.end('Forbidden');
    } else {
      next && next();
    }
  } catch (error) {
    console.log(error);
  }
}

如果要在首页中使用getIp,就需要在index.js页面中的getServerSideProps使用,如果对网站所有页面都需要使用的话,那工程就有点大。所有这种方法只适用于单个页面的时候。
注意:** getServerSideProps 只能在组件中使用,像_app.js是不能使用的**


import getIp from './api/getIp'
export async function getServerSideProps({ req, res, locale }) {
  await getIp(req, res, () => {
  console.log(1111111);
   }); // 使用中间件
  return {
    props: {
          },
  };
}
应用于所有页面

在_app.js文件中,使用getInitialProps方法可行,这是个老版本的方法,这样当页面切换就会触发getInitialProps


import getIp from '@/pages/api/getIp'
const App = ({ Component, pageProps }) => {
.....
}
App.getInitialProps = async (context) => {
  console.log('_app.js');
  await getIp(context.ctx.req, context.ctx.res)
  return {}
}

在src目录下创建一个middleware.js文件实现全局使用中间件

middleware
首先要在src目录下创建一个middleware.js文件
在middleware中不能使用axios发送请求,需要使用fetch发送请求,fetch请求会返回一个方法p.json()是一个promise,返回的才是数据
在middleware中是全局使用的,编写代码后不用手动引用,任何资源请求都会触发middleware里的代码,所以会很频繁的触发,如果里面有接口请求的话,而pages/api中的则需要到具体的页面中的getServerSideProps去手动调用
middleware.js

import { NextResponse, NextRequest } from 'next/server'
// import axios from 'axios'
export const middleware = async (request, response) => {
  
  const blockedIPs = ['127.0.0.1', '192.168.0.1', '113.76.109.114', '183.9.77.26', '113.76.109.131']; // 被禁止的IP列表
  try {
    const p = await fetch('https://api.ipdatacloud.com/v2/query?key=ccc')
    const d = await p.json()
  if (blockedIPs.includes(d.data.location.ip)) {
  // 符合条件则禁止页面访问,通过NextResponse.json在页面显示Forbidden字样
      return NextResponse.json('Forbidden', {
            status: 403
          })
    } else {
    // 正常就不做处理,直接下一步
      return NextResponse.next()
    }
  } catch (error) {
    console.log(error);
  }
};

注意:

  1. 不能在middleware函数上使用async/await 否则npm run build 的时候会报错如下错误:
    ./node_modules/@babel/runtime/regenerator/index.js
    Dynamic Code Evaluation (e. g. ‘eval’, ‘new Function’, ‘WebAssembly.compile’) not allowed in Edge Runtime
    Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation
  2. middleware中不能使用axios发送请求,需要使用fetch发送请求

上述代码打包会报错,以下代码才是正确的方式,不在middleware使用async/await,可以通过在middleware内部生成一个函数使用async/await:

import { NextResponse, NextRequest } from 'next/server'
// import axios from 'axios'
console.log(1111);
export const middleware = (request, response) => {

  // 这种方法不行,即使符合被禁用的条件还是能访问,因为没有在同步代码中手动return一个结果
  // const blockedIPs = ['CY']; // 被禁止的IP列表:塞浦路斯
  // try {
  //   // 这里不能使用axios发起请求,所以使用fetch
  //   fetch('https://api.ipdatacloud.com/v2/query?key=ccccc').then(res => {
  //     return res.json()
  //   }).then(res => {
  //     console.log('kkkkkkkk');
  //       const country_code = res.data?.location?.country_code
  // if (!blockedIPs.includes(country_code)) {
  //   console.log(6666666);
  //     return NextResponse.json('Forbidden', {
  //           status: 403
  //         })
  //   } else {
  //     return NextResponse.next()
  //   }
  //   })
  
  // } catch (error) {
  //   console.log(error);
  // }
  // 这种方法可以,因为在最后面手动return了一个值,这个值是fn异步函数的返回结果
  const fn = async () => {
    const blockedIPs = ['CY']; // 被禁止的IP列表
  try {
    const p = await fetch('https://api.ipdatacloud.com/v2/query?key=cccc')
    const d = await p.json()
  if (blockedIPs.includes(d.data.location.country_code)) {
    console.log(2222);
  // 符合条件则禁止页面访问,通过NextResponse.json在页面显示Forbidden字样
      return NextResponse.json('Forbidden', {
            status: 403
          })
    } else {
    // 正常就不做处理,直接下一步
      return NextResponse.next()
    }
  } catch (error) {
    console.log(error);
  }
  }
  // 必须在同步代码中返回,否则即使异步结果返回后符合禁止访问条件,还是能访问
  return fn()
};

你可能感兴趣的:(javascript,中间件,开发语言)