AWS-WAF-CDN基于速率rate的永久黑名单方案(基于lambda实现)

参考方案(有坑), 所以产生了这篇博客: 点击跳转

1. 部署waf (有则跳过)

在这里插入图片描述
必须存在一个rate速率规则,后面的方案堆栈要用

新建rate速率规则

在这里插入图片描述
在这里插入图片描述
关联cdn资源
在这里插入图片描述

2.部署堆栈 (美国东部 (弗吉尼亚北部 us-east-1)

1 .堆栈文件获取方式:

1.公开s3桶调用:https://actwill-cloudformation-template.s3.amazonaws.com/waf-block-rate-ip/waf_block_rate_ip_20231208.template

2.也可以手动选择复制保存

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  Scope:
    Type: String
    Description: Enter WebACL Scope CLOUDFRONT or REGIONAL
    AllowedValues: [REGIONAL, CLOUDFRONT]
  WebACLName:
    Type: String
    Description: Enter WebACL name
  WebACLId:
    Type: String
    Description: Enter WebACL ID
  RateBasedRuleName:
    Type: String
    Description: Enter Rate Based Rule Name
  CustomBlockPeriod:
    Type: Number
    Description: Enter custom block period for blocking the IP addresses in minutes. Minimum is 06 minutes
    MinValue: 6

Resources:
  CustomRBRLogBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Join
      - "-"
      - - "custom-rbr-log-bucket"
        - !Select
          - 0
          - !Split
            - "-"
            - !Select
              - 2
              - !Split
                - "/"
                - !Ref "AWS::StackId"
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LoggingConfiguration: 
        DestinationBucketName: !Ref AccessLoggingBucket

  CustomRBRLogBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: CustomRBRLogBucket
      PolicyDocument:
        Statement:
        - Action: "s3:*"
          Condition:
            Bool:
              aws:SecureTransport: 'false'
          Effect: Deny
          Principal: "*"
          Resource:
            - !GetAtt CustomRBRLogBucket.Arn
            - !Join ["/", [!GetAtt CustomRBRLogBucket.Arn, "*"]]
          Sid: HttpsOnly
        Version: '2012-10-17'

  AccessLoggingBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: True
        BlockPublicPolicy: True
        IgnorePublicAcls: True
        RestrictPublicBuckets: True
    Metadata:
      cfn_nag:
        rules_to_suppress:
          - id: W35
            reason: "This bucket is an access logging bucket for another bucket and does not require access logging to be configured for it."    

  AccessLoggingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: AccessLoggingBucket
      PolicyDocument:
        Statement:
        - Action: "s3:*"
          Condition:
            Bool:
              aws:SecureTransport: 'false'
          Effect: Deny
          Principal: "*"
          Resource:
            - !GetAtt AccessLoggingBucket.Arn
            - !Join ["/", [!GetAtt AccessLoggingBucket.Arn, "*"]]
          Sid: HttpsOnly
        Version: '2012-10-17'

  IPv4IPset:
    Type: "AWS::WAFv2::IPSet"
    Properties:
      Name: !Join
      - "-"
      - - "IPv4-IPset"
        - !Select
          - 0
          - !Split
            - "-"
            - !Select
              - 2
              - !Split
                - "/"
                - !Ref "AWS::StackId"
      Scope: !Ref Scope
      Description: "IPv4 IP set for custom rate based block rule"
      IPAddressVersion: "IPV4"
      Addresses: []

  IPv6IPset:
    Type: "AWS::WAFv2::IPSet"
    Properties:
      Name: !Join
      - "-"
      - - "IPv6-IPset"
        - !Select
          - 0
          - !Split
            - "-"
            - !Select
              - 2
              - !Split
                - "/"
                - !Ref "AWS::StackId"
      Scope: !Ref Scope
      Description: "IPv6 IP set for custom rate based block rule"
      IPAddressVersion: "IPV6"
      Addresses: []

  CustomRBRLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      FunctionName: !Join
      - "-"
      - - "CustomRBRLambdaFunction"
        - !Select
          - 0
          - !Split
            - "-"
            - !Select
              - 2
              - !Split
                - "/"
                - !Ref "AWS::StackId"
      Description: Lambda function containing the logic for custom RBR
      Handler: index.lambda_handler
      Role: !GetAtt LambdaRole.Arn
      Runtime: python3.9
      Environment:
        Variables:
          SCOPE: !Ref Scope
          WEB_ACL_NAME: !Ref WebACLName
          WEB_ACL_ID: !Ref WebACLId
          RATE_BASED_RULE_NAME: !Ref RateBasedRuleName
          CUSTOM_BLOCK_PERIOD: !Ref CustomBlockPeriod
          CONFIG_LOG_BUCKET: !Ref CustomRBRLogBucket
          CONFIG_LOG_KEY: blocked_ips_list.json
          IP_SET_ID_CUSTOM_V4: !GetAtt IPv4IPset.Id
          IP_SET_NAME_CUSTOM_V4: !Select
                                    - "0"
                                    - !Split [ "|" , Ref: IPv4IPset]
          IP_SET_ID_CUSTOM_V6: !GetAtt IPv6IPset.Id
          IP_SET_NAME_CUSTOM_V6: !Select
                                    - "0"
                                    - !Split [ "|" , Ref: IPv6IPset]
      Code:
        ZipFile: |
          import json
          import boto3
          import logging
          import datetime
          import os
          
          wafv2_client = boto3.client('wafv2')
          s3_client = boto3.client('s3')
          
          def update_custom_ipset_and_config(log, latest_ipv4_blocked_list,
                                             latest_ipv6_blocked_list):
              try:
                  # update the custom v4 IP set
                  ipv4_lock_token = get_lock_token(
                      log, wafv2_client,
                      os.getenv('IP_SET_ID_CUSTOM_V4'),
                      os.getenv('IP_SET_NAME_CUSTOM_V4')
                  )
                  update_ip_set(
                      log, wafv2_client,
                      os.getenv('IP_SET_ID_CUSTOM_V4'),
                      list(latest_ipv4_blocked_list.keys()),
                      ipv4_lock_token,
                      os.getenv('IP_SET_NAME_CUSTOM_V4')
                  )
          
                  # update the custom v6 IP set
                  ipv6_lock_token = get_lock_token(
                      log, wafv2_client,
                      os.getenv('IP_SET_ID_CUSTOM_V6'),
                      os.getenv('IP_SET_NAME_CUSTOM_V6')
                  )
                  update_ip_set(
                      log, wafv2_client,
                      os.getenv('IP_SET_ID_CUSTOM_V6'),
                      list(latest_ipv6_blocked_list.keys()),
                      ipv6_lock_token,
                      os.getenv('IP_SET_NAME_CUSTOM_V6')
                  )
              except Exception as e:
                  # log error message
                  log.error("[update_custom_ipset_and_config] "
                            "Error updating custom ipset.")
                  raise e
          
              try:
                  # create json object of the latest custom config
                  latest_custom_config = {
                      'IPv4': latest_ipv4_blocked_list,
                      'IPv6': latest_ipv6_blocked_list
                  }
                  byte_latest_custom_config = json.dumps(latest_custom_config).encode()
          
                  # upload the config to s3
                  s3_client.put_object(
                      Bucket=os.getenv('CONFIG_LOG_BUCKET'),
                      Body=byte_latest_custom_config,
                      Key=os.getenv('CONFIG_LOG_KEY')
                  )
              except Exception as e:
                  # log error message
                  log.error("[update_custom_ipset_and_config] "
                            "Error uploading config to S3.")
                  raise e
          
          
          def get_lock_token(log, wafv2_client, ip_set_id, name):
              try:
                  ipv4_get_response = wafv2_client.get_ip_set(
                      Scope=os.getenv('SCOPE'),
                      Name=name,
                      Id=ip_set_id
                  )
                  return ipv4_get_response['LockToken']
              except Exception as e:
                  log.error(f"Error in get_lock_token: {e}")
                  raise
          
          
          def update_ip_set(log, wafv2_client, ip_set_id, addresses,
                            lock_token, name):
              try:
                  wafv2_client.update_ip_set(
                      Scope=os.getenv('SCOPE'),
                      Name=name,
                      Id=ip_set_id,
                      Description='Last Update: ' +
                                  datetime.datetime.now(datetime.timezone.utc).strftime(
                                                               "%Y-%m-%d %H:%M:%S %Z%z"),
                      Addresses=addresses,
                      LockToken=lock_token
                  )
              except Exception as e:
                  log.error("Error in update_ip_set: {}".format(e))
                  raise
          
          
          def sync_ip_from_rbr_to_custom_ipset(log, rbr_managed_ip_list,
                                               custom_managed_ip_config):
              # Get the current timestamp in UTC format
              utc_now_timestamp = datetime.datetime.now(
                  datetime.timezone.utc)
              # Convert the timestamp to string
              utc_now_timestamp_str = utc_now_timestamp.strftime(
                  "%Y-%m-%d %H:%M:%S %Z%z")
          
              # Iterate over the managed IPs in the RBR list
              for managed_ip in rbr_managed_ip_list:
                  # If the IP is already in the custom IP config
                  if managed_ip in custom_managed_ip_config.keys():
                      # Get the timestamp when the IP was blocked in UTC format
                      utc_blocked_at = datetime.datetime.strptime(
                          custom_managed_ip_config[managed_ip],
                          "%Y-%m-%d %H:%M:%S %Z%z").astimezone(
                          datetime.timezone.utc)
                      # Calculate the difference in minutes between now and when the IP
                      # was blocked
                      total_diff_min = ((utc_now_timestamp - utc_blocked_at)
                                        .total_seconds()) / 60
                                        
                      # If the difference is greater than block period, update the timestamp
                      if round(total_diff_min) >= int(os.getenv('CUSTOM_BLOCK_PERIOD')):
                          custom_managed_ip_config[managed_ip] = utc_now_timestamp_str
                  # If the IP is not in the custom IP config, add it with the current
                  # timestamp
                  else:
                      custom_managed_ip_config[managed_ip] = utc_now_timestamp_str
          
              # Create a new dictionary to store the latest blocked IPs
              latest_ip_blocked_list = {}
              
              # Iterate over the custom IP config
              for blocked_ip, blocked_at_str in custom_managed_ip_config.items():
                  # Get the timestamp when the IP was blocked in UTC format
                  utc_blocked_at = datetime.datetime.strptime(
                      custom_managed_ip_config[blocked_ip],
                      "%Y-%m-%d %H:%M:%S %Z%z").astimezone(datetime.timezone.utc)
                  # Calculate the difference in minutes between now and when the IP
                  # was blocked
                  total_diff_min = ((utc_now_timestamp - utc_blocked_at)
                                    .total_seconds()) / 60
                  # If the difference is less than the custom block period
                  #then add it to the latest blocked IPs list
                  if round(total_diff_min) < int(os.getenv('CUSTOM_BLOCK_PERIOD')):
                      latest_ip_blocked_list[blocked_ip] = blocked_at_str
  
              return latest_ip_blocked_list
          
          
          def get_custom_config_file(log):
              try:
                  # Get the custom config file from S3
                  s3_response = s3_client.get_object(
                      Bucket=os.getenv('CONFIG_LOG_BUCKET'),
                      Key=os.getenv('CONFIG_LOG_KEY')
                  )
                  # Load the custom config file as a JSON object
                  custom_managed_ip_config = json.loads(
                      s3_response['Body'].read()
                  )
              except Exception as e:
                  log.error("[get_custom_config_file] Error to get the custom config "
                            "file from S3")
                  log.error(e)
                  # If there is an error, return an empty config
                  custom_managed_ip_config = {'IPv4': {}, 'IPv6': {}}
          
              return custom_managed_ip_config
          
          
          def get_rbr_managed_ip_list(log):
              try:       
                  # Get the list of IPs blocked by the rate based rule
                  wafv2_response = wafv2_client.get_rate_based_statement_managed_keys(
                      Scope=os.getenv('SCOPE'),
                      WebACLName=os.getenv('WEB_ACL_NAME'),
                      WebACLId=os.getenv('WEB_ACL_ID'),
                      RuleName=os.getenv('RATE_BASED_RULE_NAME')
                  )
          
                  return wafv2_response
              except Exception as e:
                  log.error("[get_rbr_managed_ip_list] "
                            "Error to get the list of IP blocked by rate based rule")
                  log.error(e)
                  # If there is an error, raise the exception
                  raise e
          
          
          def lambda_handler(event, context):
              log = logging.getLogger()
          
              try:
                  # Set Log Level
                  log.setLevel(logging.ERROR)
          
                  # Get the list of IP blocked by rate based rule
                  rbr_managed_list = get_rbr_managed_ip_list(log)
          
                  # Get custom config file from S3
                  custom_managed_ip_config = get_custom_config_file(log)
          
                  # Update IP from rate based rule list to custom list
                  latest_ipv4_blocked_list = sync_ip_from_rbr_to_custom_ipset(
                      log, rbr_managed_list['ManagedKeysIPV4']['Addresses'],
                      custom_managed_ip_config['IPv4'])
                  latest_ipv6_blocked_list = sync_ip_from_rbr_to_custom_ipset(
                      log, rbr_managed_list['ManagedKeysIPV6']['Addresses'],
                      custom_managed_ip_config['IPv6'])
          
                  # Update latest blocked list to S3 and WAF IPset
                  update_custom_ipset_and_config(log, latest_ipv4_blocked_list,
                                                 latest_ipv6_blocked_list)
          
                  return {
                      'statusCode': 200,
                      'body': json.dumps('Update Success!')
                  }
              except Exception as e:
                  log.error(e)
                  return {
                      'statusCode': 500,
                      'body': e
                  }
      Timeout: 10
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: There is no need to run this lambda in a VPC
        - id: W92
          reason: There is no need for Reserved Concurrency

  LambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:

        - Effect: "Allow"
          Principal:
            Service:
              - "lambda.amazonaws.com"
          Action: "sts:AssumeRole"

      ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

      Policies:
          - PolicyName: !Join
            - "-"
            - - "LambdaRolePolicy"
              - !Select
                - 0
                - !Split
                  - "-"
                  - !Select
                    - 2
                    - !Split
                      - "/"
                      - !Ref "AWS::StackId"

            PolicyDocument:
              Version: "2012-10-17"
              Statement:
              - Sid: "S3BucketPermissions"
                Effect: "Allow"
                Action:
                - "s3:PutObject"
                - "s3:GetObject"
                Resource:
                  - !Sub 'arn:${AWS::Partition}:s3:::${CustomRBRLogBucket}/blocked_ips_list.json'
              - Sid: "WAFIPSetPermissions"
                Effect: "Allow"
                Action:
                - "wafv2:GetIPSet"
                - "wafv2:UpdateIPSet"
                Resource:
                  - !GetAtt IPv6IPset.Arn
                  - !GetAtt IPv4IPset.Arn
              - Sid: "WAFRBRPermissions"
                Effect: "Allow"
                Action: "wafv2:GetRateBasedStatementManagedKeys"
                Resource: !Sub
                  - 'arn:${AWS::Partition}:wafv2:${AWS::Region}:${AWS::AccountId}:${WebACLSope}/webacl/${WebACLName}/${WebACLId}'
                  - WebACLSope: !If [IsRegional, "regional", "global"]


  EventBridgeRule:
      Type: "AWS::Events::Rule"
      Properties:
        Name: !Join
        - "-"
        - - "EventBridgeRule"
          - !Select
            - 0
            - !Split
              - "-"
              - !Select
                - 2
                - !Split
                  - "/"
                  - !Ref "AWS::StackId"
        ScheduleExpression: "rate(1 minute)"
        State: "ENABLED"
        Targets:
          - Id: "CustomRBRLambdaFunction"
            Arn: !GetAtt CustomRBRLambdaFunction.Arn

  LambdaPermissionForEventBridge:
    Type: "AWS::Lambda::Permission"
    Properties:
      FunctionName: !Ref CustomRBRLambdaFunction
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: !GetAtt EventBridgeRule.Arn

Outputs:
  IPv4IPsetName:
    Description: IPv4 IPSet for custom rate based block rule
    Value: !Select
              - "0"
              - !Split [ "|" , Ref: IPv4IPset]
  IPv6IPsetName:
    Description: IPv6 IPSet for custom rate based block rule
    Value: !Select
              - "0"
              - !Split [ "|" , Ref: IPv6IPset]

Conditions:
  IsRegional:
    !Equals [!Ref Scope, "REGIONAL"]

2. 新建堆栈

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
等待创建完成,查看相关参数资源
在这里插入图片描述
在这里插入图片描述

3. 回到waf确认,无误后开始测试

在这里插入图片描述

发现已经创建对应的ip规则集

在这里插入图片描述

添加到对应的waf中拒绝此ip集

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

开始测试

找一个终端,这里我使用cloudshell
使用我编写的脚本进行测试
在这里插入图片描述

$ cat waf.sh 

#!/bin/bash
while true; do 
    curl -I  http://xxxxxx.cloudfront.net/xxxx.png
    sleep 0.5
done


##开始测试
# sh waf.sh 

在这里插入图片描述
查看是否在ip黑名单中
在这里插入图片描述
cdn备用域名测试
在这里插入图片描述
说明方案生效(从ip集中删除即可恢复访问)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/256332.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【QT】QDockWidget控件的使用

目录 1.概述 2.常用函数介绍 3.QDockWidget布局相关 4.QDockWidget的使用注意事项 5.使用场景 6.简单应用示例代码 1.概述 QDockWidget类提供了一个小部件&#xff0c;可以停靠在QMainWindow中&#xff0c;也可以作为桌面上的顶级窗口浮动。 QDockWidget提供了dock Widg…

Github 2023-12-18 开源项目周报 Top14

根据Github Trendings的统计&#xff0c;本周(2023-12-18统计)共有14个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量TypeScript项目4Python项目4Jupyter Notebook项目3非开发语言项目1JavaScript项目1Rust项目1Go项目1 基于项目…

下载svn client,小乌龟

给兄弟们提供一个下载svn client的软件连接 不好用包退货 https://sourceforge.net/projects/tortoisesvn/ 点击download即可

21.三层链路聚合

三层链路聚合 交换机默认的接口模式为二层接口模式 1.先将交换机的接口改为三层模式 2.创建三层链路聚合端口组3 3.将端口加入链路聚合端口组3 4.给聚合后的端口配置IP地址 在路由器上也做链路聚合的操作 1.创建三层链路聚合端口组3 2.将端口加入链路聚合端口组3 …

基于ssm的航班订票管理系统论文

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对航班订票信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差…

Git账户密码http方式的配置

Git账户密码http方式的配置 入门 git在提交时每次都需要输入密码和账号信息&#xff0c;可以将账号和密码进行持久化存储&#xff0c; 当git push的时候输入一次用户名和密码就会被记录&#xff0c; 不需要每次输入&#xff0c;提高效率&#xff0c;进行一下配置&#xff1…

2024年完整湖北等保测评机构名单看这里!

等保测评机构是指经公安部认证的具有资质的测评机构&#xff0c;主要从事等级测评活动。一般过等保需要找正规具有资质的等保测评机构。那你知道2024年湖北等保测评机构有哪些&#xff1f;名单有吗&#xff1f; 2024年完整湖北等保测评机构名单看这里&#xff01; 1、湖北星…

端口占用命令 netstat (centos)+netstat (windows)

linux 1.使用 netstat 命令查看端口占用情况 netstat -tlnp 使用 -p 选项查看进程信息。 使用 -t 选项列出 TCP 协议的连接&#xff1a;类似&#xff08;使用 -u 选项列出 UDP 协议的连接&#xff1a;&#xff09; 2.查找占用指定端口号的应用信息 netstat -tlnp | grep 3…

Java智慧工地数字化云平台源码(SaaS模式)

智慧工地是智慧城市理念在建筑工程行业的具体体现&#xff0c;智慧工地解决方案是建立在高度信息化基础上一种支持人事物全面感知、施工技术全面智能、工作互通互联、信息协同共享、决策科学分析、风险智慧预控的新型信息化手段。围绕人、机、料、法、环等关键要素&#xff0c;…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)更改应用图标

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;更改应用图标 一、操作环境 操作系统: Windows 10 专业版 IDE:DevEco Studio 3.1 SDK:HarmonyOS 3.1 二、更改图标 图标的位置&#xff1a;entry->src->main->resources->-b…

瑞芯微RV1103与FPGA图像传输,实现网络推流

一、瑞芯微RV1103介绍 RV1106及RV1103具有以下六大核心技术优势&#xff1a; 1、内置自研第4代NPU&#xff0c;最高达0.5TOPs算力 RV1106及RV1103采用Cortex-A7 CPU及高性能MCU&#xff0c;内置瑞芯微自研第4代NPU&#xff0c;运算精度高&#xff0c;支持int4、in8、int16混合…

WebGL开发的应用程序类型

WebGL是一种用于在Web浏览器中进行高性能图形渲染的JavaScript API&#xff0c;它主要用于创建交互式的3D图形和图像。通过WebGL&#xff0c;您可以开发多种类型的Web应用程序&#xff0c;包括但不限于以下几种&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#…

【前端】vscode 相关插件

一 插件&#xff1a; 01、ESLint 用来识别并检查ECMAScript/JavaScript 代码的工具 02、Prettier 用来格式化代码&#xff0c;如.js、.vue、css等都可以进行格式化 03、Vetur 用来识别并高亮vue语法 04、EditorConfig 用来设置vscode的编程行为 二、安装依赖 01、…

新年跨年烟花超酷炫合集【内含十八个烟花酷炫效果源码】

❤️以下展示为全部烟花特效效果 ❤️下方仅展示部分代码 ❤️源码获取见文末 🎀HTML5烟花喷泉 <style> * {padding:0;margin:0; } html,body {positi

TSX-3225 (MHz范围晶体单元微型低轮廓贴片)

TSX-322系列晶体谐振器是爱普生主推的一款无源晶振型号&#xff0c;频率范围16mhz ~ 48mhz&#xff0c;3.2*2.5mm较小的外部尺寸&#xff0c;可以广泛使用在手机&#xff0c;蓝牙&#xff0c;无线-局域网、ISM 频段电台广播&#xff0c;MPU时钟等产品中。 规范 运动阻力(ESR) 外…

基于ssm餐饮掌上设备点餐系统论文

餐饮掌上设备点餐系统 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了餐饮掌上设备点餐系统的开发全过程。通过分析餐饮掌上设备点餐系统管理的不足&#xff0c;创建了一个计算机管理餐饮掌上设备点餐系统的…

Enum in C,C++

C(C11以上&#xff09;&#xff1a; 第一种方式&#xff1a; /*** file duLangMap.h* author geovindu,Geovin Du(geovindu163.com)* brief vscode c11* version 0.1* date 2023-12-18** copyright Copyright (c) 站在巨人的肩膀上 Standing on the Shoulders of Giants 2023…

RK3568 android11 调试mipi摄像头 gc2093

一&#xff0c;摄像头简介 GC2093是一个高质量的1080P CMOS图像传感器&#xff0c;用于安全相机产品、数码相机产品和手机相机应用程序。包含了一个1920H x 1080V像素阵列、片上10位ADC和图像信号处理器。高性能和低功耗功能的全面集成使GC2093最适合设计&#xff0c;减少了实…

TikTok矩阵玩法分享,如何建立TikTok矩阵?

矩阵是在 TikTok 上非常常见的营销方式&#xff0c;很多卖家想要通过矩阵化运营快速涨粉。但要想做好TikTok矩阵&#xff0c;需要有明确的方向和计划。下面东哥我将分享一些做TikTok矩阵的玩法&#xff0c;帮助大家更好地搭建自己的TikTok矩阵。 了解TikTok矩阵 TikTok矩阵是一…

vue导出element表格,xlsx和xlsx-style生成xlsx文件并修改样式

1.下载依赖 npm install xlsx --save npm install file-saver --save npm install xlsx-style --save2.先修改xlsx-style的源码&#xff0c;一旦引入xlsx-style则会报错 xlsx-style使用中常见问题及解决办法&#xff1a; xlsx-style使用中常见问题及解决办法-CSDN博客 在\n…