springboot2集成knife4j(swagger3)

news2024/11/17 13:19:42

springboot2集成knife4j(swagger3)

  • springboot2集成knife4j(swagger3)
    • 环境说明
    • 集成knife4j
      • 第一步:引入依赖
      • 第二步:编写配置类
      • 第三步:放行相关资源 & 保证启动了knife4j
      • 第四步:测试一下
        • 第一小步:编写controller
        • 第二小步:启动项目,访问api文档
    • 相关资料

环境说明

  • springboot:2.6.4
  • knife4j-spring-boot-starter:3.0.3

集成knife4j

第一步:引入依赖

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

第二步:编写配置类

提示:可以借助配置文件,进一步改造

import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.github.xiaoymin.knife4j.core.model.MarkdownProperty;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import com.ideaaedi.demo.support.EnumDescriptor;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ModelSpecificationBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.PropertySpecificationBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.RequestParameterBuilder;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Knife4j配置
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Slf4j
@Configuration
@EnableSwagger2
public class Knife4jConfig implements WebMvcConfigurer {
    
    /** 对于管控了权限的应用,应放行以下资源 */
    public static String[] RESOURCE_URLS = new String[]{"/webjars/**", "/swagger**", "/v3/api-docs", "/doc.html"};
    
    @Value("${spring.application.name:default}")
    private String applicationName;
    
    @Resource
    private OpenApiExtensionResolver openApiExtensionResolver;
    
    @Bean
    public Docket docket() {
        // 指定使用Swagger2规范
        Docket docket = new Docket(DocumentationType.OAS_30)
                .apiInfo(new ApiInfoBuilder()
                        // 简介(支持Markdown语法)
                        .description("# 我是API简介")
                        // 服务地址
                        .termsOfServiceUrl("http://local.idea-aedi.com/")
                        // 作者及联系信息
                        .contact(new Contact("JustryDeng", "https://gitee.com/JustryDeng", "13548417409@163.com"))
                        // api版本
                        .version("1.0.0")
                        .build())
                .extensions(openApiExtensionResolver.buildExtensions(applicationName))
                //分组名称(微服务项目可以用微服务名分组)
                .groupName(applicationName)
                .select()
                // 定位api
                .apis(
                        RequestHandlerSelectors.basePackage(getProjectBasePackage())
                                .and(RequestHandlerSelectors.withClassAnnotation(RestController.class)
                                        .or(RequestHandlerSelectors.withClassAnnotation(Controller.class))
                                )
                )
                .paths(PathSelectors.any())
                .build();
    
        docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());
        
        // 自定义文档解析
        try {
            Field markdownPropertiesField = FieldUtils.getDeclaredField(OpenApiExtensionResolver.class,
                    "markdownProperties", true);
            List<MarkdownProperty> markdownProperties = (List<MarkdownProperty>)markdownPropertiesField.get(openApiExtensionResolver);
            if (!CollectionUtils.isEmpty(markdownProperties)) {
                for (MarkdownProperty markdownProperty : markdownProperties) {
                    docket.extensions(openApiExtensionResolver.buildExtensions(markdownProperty.getGroup()));
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        
        return docket;
    }
    
    private List<SecurityScheme> securitySchemes() {
        // 设置请求头信息
        List<SecurityScheme> result = new ArrayList<>();
        // 第一个参数name,自定义即可。 在配置securityContexts时,通过此name对应到apiKey即可
        // 第二个参数,header name自定义即可。 如:JWT_TOKEN_KEY=Auth-Token,然后在代码里request.getHeader(JWT_TOKEN_KEY)取值
        String JWT_TOKEN_KEY="Auth-Token";
        ApiKey apiKey = new ApiKey("JustryDengApiKey", JWT_TOKEN_KEY, "header");
        result.add(apiKey);
        return result;
    }
    
    private List<SecurityContext> securityContexts() {
        // 设置需要登录认证的路径
        List<SecurityContext> result = new ArrayList<>();
        List<SecurityReference> securityReferences = defaultAuth();
        result.add(
                SecurityContext.builder().securityReferences(securityReferences).forPaths(
                        // 当直接使用swagger文档发送请求时,这些api需要满足securityReferences认证要求
                        PathSelectors.regex("/.*")
                                .and(
                                        // 当直接使用swagger文档发送请求时,这些api不需要满足securityReferences认证要求. '.*'表示匹配所有
                                        PathSelectors.regex("/hello.*").or(PathSelectors.regex("/hi.*"))
                                                .negate()
                                )
                ).build()
        );
        return result;
    }
    
    private List<SecurityReference> defaultAuth() {
        List<SecurityReference> result = new ArrayList<>();
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        // 这里指定使用哪个apiKey进行认证鉴权. 这里指定使用上面名为JustryDengApiKey的apiKey
        result.add(new SecurityReference("JustryDengApiKey", authorizationScopes));
        return result;
    }
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    
    /**
     * 获取项目包前缀
     */
    private String getProjectBasePackage() {
        String projectBasePackage;
        String currPackageName = this.getClass().getPackage().getName();
        String[] packageItemArr = currPackageName.split("\\.");
        if (packageItemArr.length > 3) {
            projectBasePackage = String.join(".", packageItemArr[0], packageItemArr[1], packageItemArr[2]);
        } else {
            projectBasePackage = currPackageName;
        }
        log.info("Base package to scan api is -> {}", projectBasePackage);
        return projectBasePackage;
    }
    
    /**
     * 显示自定义枚举类型注释
     * <p>
     * <br/> 参考<a
     * href="https://blog.gelu.me/2021/Knife4j-Swagger%E8%87%AA%E5%AE%9A%E4%B9%89%E6%9E%9A%E4%B8%BE%E7%B1%BB%E5%9E%8B
     * /">here</a>
     */
    @Component
    @SuppressWarnings("unchecked")
    public static class Knife4jSwaggerEnumPlugin implements ModelPropertyBuilderPlugin, ParameterBuilderPlugin {
        
        private static final Field parameterDescriptionField;
        
        private static final Field modelPropertyBuilderDescriptionField;
        
        
        static {
            parameterDescriptionField = ReflectionUtils.findField(RequestParameterBuilder.class, "description");
            Objects.requireNonNull(parameterDescriptionField, "parameterDescriptionField should noe be null.");
            ReflectionUtils.makeAccessible(parameterDescriptionField);
            
            modelPropertyBuilderDescriptionField = ReflectionUtils.findField(PropertySpecificationBuilder.class, "description");
            Objects.requireNonNull(modelPropertyBuilderDescriptionField, "ModelPropertyBuilder_descriptionField should noe be null.");
            ReflectionUtils.makeAccessible(modelPropertyBuilderDescriptionField);
        }
        
        /**
         * {@link ApiModelProperty}相关
         * <p>
         * 主要处理枚举对象直接作为方法参数的内部字段的情况. 如:
         * <pre>
         *  &nbsp;  @Data
         *  &nbsp;  public class LoginTokenRespVO {
         *  &nbsp;
         *  &nbsp;      @ApiModelProperty("用户类型")
         *  &nbsp;      private UserTypeEnum userType;
         *  &nbsp;  }
         * </pre>
         */
        @Override
        public void apply(ModelPropertyContext context) {
            Optional<BeanPropertyDefinition> optional = context.getBeanPropertyDefinition();
            if (!optional.isPresent()) {
                return;
            }
            // 对应被@ApiModelProperty标注的字段
            BeanPropertyDefinition beanPropertyDefinition = optional.get();
            Class<?> fieldType = beanPropertyDefinition.getField().getRawType();
            if (!Enum.class.isAssignableFrom(fieldType)) {
                return;
            }
            Class<Enum<?>> enumType = (Class<Enum<?>>) fieldType;
            Enum<?>[] enumConstants = enumType.getEnumConstants();
            PropertySpecificationBuilder modelPropertyBuilder = context.getSpecificationBuilder();
            Object oldValue = ReflectionUtils.getField(modelPropertyBuilderDescriptionField, modelPropertyBuilder);
            // 解析枚举
            List<String> enumDescList =
                    Arrays.stream(enumConstants).map(this::obtainEnumDescription).collect(Collectors.toList());
            modelPropertyBuilder.description((oldValue == null ? "" : oldValue) + buildHtmlUnOrderList(enumDescList))
                    .type(new ModelSpecificationBuilder().scalarModel(ScalarType.UUID).build());
        }
        
        /**
         * {@link ApiParam}、{@link io.swagger.v3.oas.annotations.Parameter}相关.
         * <p> 主要处理:枚举对象直接作为方法参数的情况. 如:
         * <pre>
         *  &nbsp;  @PostMapping("/test1")
         *  &nbsp;  @ApiOperation(value = "测试1")
         *  &nbsp;  public void test1(@ApiParam(value = "用户类型", required = true) UserTypeEnum userTypeEnum)
         * </pre>
         */
        @Override
        public void apply(ParameterContext context) {
            Class<?> type = context.resolvedMethodParameter().getParameterType().getErasedType();
            RequestParameterBuilder parameterBuilder = context.requestParameterBuilder();
            if (!Enum.class.isAssignableFrom(type)) {
                return;
            }
            Class<Enum<?>> enumType = (Class<Enum<?>>) type;
            Enum<?>[] enumConstants = enumType.getEnumConstants();
            // 解析枚举
            List<String> enumDescList = Arrays.stream(enumConstants).map(this::obtainEnumDescription).collect(Collectors.toList());
            Object oldValue = ReflectionUtils.getField(parameterDescriptionField, parameterBuilder);
            parameterBuilder.description((oldValue == null ? "" : oldValue) + buildHtmlUnOrderList(enumDescList));
        }
        
        /**
         * 此插件是否支持处理该DocumentationType
         */
        @Override
        public boolean supports(@NonNull DocumentationType documentationType) {
            return true;
        }
        
        /**
         * 获取枚举描述
         *
         * @param enumObj 枚举对象
         *
         * @return 枚举描述
         */
        private String obtainEnumDescription(@NonNull Enum<?> enumObj) {
            String name = enumObj.name();
            /*
             * 枚举说明器示例:
             *
             * public interface EnumDescriptor {
             *     // 获取枚举项说明
             *     String obtainDescription();
             * }
             */
            if (enumObj instanceof EnumDescriptor) {
                return name + ":" + ((EnumDescriptor) enumObj).obtainDescription();
            }
            return name;
        }
        
        /**
         * 构建无序列表html
         *
         * @param itemList 列表元素
         *
         * @return 无序列表html
         */
        private String buildHtmlUnOrderList(@Nullable List<String> itemList) {
            if (CollectionUtils.isEmpty(itemList)) {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            sb.append("<ul>");
            for (String item : itemList) {
                sb.append("<li>");
                sb.append(item);
                sb.append("</li>");
            }
            sb.append("</ul>");
            return sb.toString();
        }
    }
}

第三步:放行相关资源 & 保证启动了knife4j

  • 放行相关资源

    对于管控了权限的应用,应放行以下资源

    /** 对于管控了权限的应用,应放行以下资源 */
    public static String[] RESOURCE_URLS = new String[]{"/webjars/**", "/swagger**", "/v3/api-docs", "/doc.html"};
    
  • 保证启动了knife4j

    knife4j:
      # 启动knife4j(注:有时,如果我们不进行此配置,knife4j不会开启)
      # 其实核心是:保证com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration生效即可。如果knife4j怎么也没启动,请debug此类,确保其被加载
      enable: true
      # 添加自定义文档
      documents:
        - group: my-group # 这里的group是处理自定义文档时用到的标识,自定义唯一即可
          # 效果等价使用 @Api(tags = "自定义文档")
          name: 自定义文档
          # 指定.md文档位置 (更多详见官网:https://doc.xiaominfo.com/docs/features/selfdocument)
          # 每个md文档的标题,则等价于 @ApiOperation(value = "md文档的标题")
          locations: classpath:api-doc/*.md
    
    # 兼容swagger3
    spring:
      mvc:
        pathmatch:
          matching-strategy: ant_path_matcher
    

第四步:测试一下

第一小步:编写controller

import com.ideaaedi.demo.controller.model.UserAddReqVO;
import com.ideaaedi.demo.controller.model.UserDetailRespVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * 用于测试knife4j的controller
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@RestController
@Api(tags = "我是DemoController")
public class TestController {
    
    @GetMapping("/hello")
    @ApiOperation(value = "哈喽")
    public String hello(@ApiParam(name = "name", value = "姓名", required = true) @RequestParam String name) {
        return "hello " + name;
    }
    
    @GetMapping("/hi")
    @ApiOperation(value = "嘿")
    public String hei(@ApiParam(name = "name", value = "姓名", required = true) @RequestParam String name) {
        return "hello " + name;
    }
    
    @PostMapping("/user/add")
    @ApiOperation(value = "新增用户")
    public UserDetailRespVO addUser(@RequestBody UserAddReqVO req) {
        UserDetailRespVO resp = new UserDetailRespVO();
        resp.setId(9527L);
        resp.setName(req.getName());
        resp.setAge(req.getAge());
        return resp;
    }
    
    @DeleteMapping("/user/delete/{id}")
    @ApiOperation(value = "删除用户")
    public Boolean addUser(@ApiParam(name = "id", value = "数据id", required = true) @PathVariable Long id) {
        return true;
    }
    
    /**
     * 测试 @RequestBody、@RequestParam、@PathVariable并存
     */
    @PostMapping("/multi-anno/{id}")
    @ApiOperation(value = "组合使用测试")
    public UserDetailRespVO testMultiAnno(@RequestBody UserAddReqVO req, @ApiParam(name = "name", value = "姓名",
            required = true) @RequestParam String name,
                                          @ApiParam(name = "id", value = "数据id", required = true) @PathVariable Long id) {
        UserDetailRespVO resp = new UserDetailRespVO();
        resp.setId(9527L);
        resp.setName(req.getName());
        resp.setAge(req.getAge());
        return resp;
    }
    
}

此controller中用到的相关模型

  • UserAddReqVO

    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    import javax.validation.constraints.NotBlank;
    
    /**
     * 用户新增req模型
     */
    @Data
    public class UserAddReqVO {
        
        @ApiModelProperty(value = "姓名",required = true)
        @NotBlank(message = "姓名不能为空")
        private String name;
        
        @ApiModelProperty("年龄")
        private Integer age;
    }
    
  • UserDetailRespVO

    import io.swagger.annotations.ApiModelProperty;
    import lombok.Data;
    
    /**
     * 用户详情resp模型
     */
    @Data
    public class UserDetailRespVO {
        
        @ApiModelProperty("id")
        private Long id;
        
        @ApiModelProperty("姓名")
        private String name;
        
        @ApiModelProperty("年龄")
        private Integer age;
    }
    

第二小步:启动项目,访问api文档

启动项目后,直接访问http://{ip}:{端口}/doc.html即可

在这里插入图片描述

在这里插入图片描述

说明:

  • 文档分组(不设置则默认分组名称为default):可切换观察其余分组下的api
  • 主页:概览
  • Swagger Models:可以查看所有请求模型的信息
  • 文档管理:可以导出文档、进行高级设置(如设置后处理脚本等)、进行全局参数设置、查看api信息
  • 自定义文档:可以添加一些自定义的md格式的文档
  • 点击进入文档后,会展示api的详细信息,也可以进行调试,还可以打开api json数据等等

相关资料

  • demo代码下载(knife4j-swagger3-springboot2分支)
  • knife4j官网
  • 本文已被收录进《程序员成长笔记》 ,笔者JustryDeng

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lecture 14:Life-long Learning

目录 Catastrophic Forgetting 灾难性遗忘(Catastrophic Forgetting)的克服之道 Selective Synaptic Plasticity Additional Neural Resource Allocation Memory Reply 其他 Catastrophic Forgetting ——为什么今日的人工智慧无法成为天网&#xff1f;灾难性遗忘 Life…

将ssh发布密钥添加到服务器的ssh授权密钥中,但是为什么我仍然无法ssh登录到此服务器?

我已经将ssh发布密钥添加到服务器的ssh授权密钥中&#xff0c;但是为什么我仍然无法ssh登录到此服务器&#xff1f; 即使将ssh公钥添加到服务器的授权密钥中&#xff0c;您也可能无法通过SSH登录到服务器&#xff0c;这有几个原因: 1.服务器的authorized_keys文件的权限不正确…

DAY 56 MySQL数据库的索引

索引的概念 索引是一个排序的列表&#xff0c;在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址&#xff08;类似于c语言的链表通过指针指向数据记录的内存地址&#xff09;。 使用索引后可以不用扫描全表来定位某行的数据&#xff0c;而是先通过索引表找到该行…

专业的媒体邀约服务

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 媒体邀约是什么&#xff1f;专业的媒体邀约服务包含哪些内容&#xff1f;怎么选择合适的媒体邀约服务机构&#xff1f; 今天胡老师就跟大家分享下这方面你的经验。 一&#xff0c;媒体邀约…

【Java入门合集】第四章继承(三)

【Java入门合集】第四章继承&#xff08;三&#xff09; 博主&#xff1a;命运之光专栏&#xff1a;JAVA入门 学习目标 1.掌握继承性的主要作用、实现、使用限制&#xff1b; 2.掌握this和super的含义及其用法&#xff1b; 3.掌握方法覆写的操作&#xff1b; 4.掌握final关键字…

【Spring MVC】Web程序开发

文章目录 1. 什么是Spring MVC&#xff1f;2. 如何创建Spring MVC项目&#xff1f;3. 简单示例演示4. 路由接口映射4.1 RequestMapping4.2 GetMapping和PostMapping 5. 获取参数5.1 获取单个参数5.2 获取多个参数5.3 获取对象5.4 RequestParam参数重命名5.5 ResquestBody接收JS…

告别PPT手残党!这6款AI神器,让你秒变PPT王者!

如果你是一个PPT手残党&#xff0c;每每制作PPT总是让你焦头烂额&#xff0c;那么你一定需要这篇幽默拉风的推广文案&#xff01; 我向你保证&#xff0c;这篇文案将帮助你发现6款AI自动生成PPT的神器&#xff0c;让你告别PPT手残党的身份&#xff0c;成为一名PPT王者。 无论…

GoogleOptions命令行参数解析工具

GoogleOptions命令行参数解析工具 GoogleOptions GoogleOptions GoogleOptions是来自 Bazel Project 的命令行参数解析器。将 com.google.devtools.common.options 包拆分为一个单独的 jar&#xff0c;用于通用实用程序。 环境搭建: 引入maven依赖 <dependency><gr…

如何在Windows 10上使用Kali Linux应用程序

Linux操作系统在一些企业中越来越受欢迎。有许多发行版&#xff0c;包括Mint、Ubuntu和Kali。Kali是最著名的Linux发行版之一&#xff0c;渗透测试人员通常使用它。Kali Linux配备了一系列免费的开源软件&#xff0c;成为渗透测试的理想选择。目前&#xff0c;安全专家和渗透测…

【Vue】学习笔记-CLI Todo-List案例

学习笔记-CLI Todo-List案例 Todo-List案例src/App.vuesrc/components/MyHeader.vuesrc/components/MyList.vuesrc/components/MyItem.vuesrc/components/MyFooter.vue Todo-List案例 组件化编码流程 拆分静态组件&#xff1a;组件要按照功能点拆分&#xff0c;命名不要与html…

VSCode 开发flutter 实现设备远程调试

目前只找到了安卓的调试方案&#x1f62c;。 安卓端&#xff1a; 1首先安装 ADB Commanads for VSCode扩展 并且必须确保ADB已经添加到系统环境变量中 如未添加请按照下面的方式添加&#xff0c;如添加请直接跳到下面。 2添加环境变量&#xff08;windows可参考&#xff0c…

【Java系列】深入解析Java多线程

序言 你只管努力&#xff0c;其他交给时间&#xff0c;时间会证明一切。 文章标记颜色说明&#xff1a; 黄色&#xff1a;重要标题红色&#xff1a;用来标记结论绿色&#xff1a;用来标记一级重要蓝色&#xff1a;用来标记二级重要 希望这篇文章能让你不仅有一定的收获&#xf…

sqli-labs通关(十七)

第十七关 这一关跟前面的关卡都不一样&#xff0c;是全新的关卡&#xff0c;页面是一个密码重置页面&#xff0c;需要输入用户名&#xff0c;然后输入新的密码&#xff0c;就会把我们的旧密码替换掉。所以就会用到数据库的update更新数据&#xff0c;不再是前面的查询数据&…

327页16万字市智慧人社项目建设方案(word可编辑)

本资料来源公开网络&#xff0c;仅供个人学习&#xff0c;请勿商用&#xff0c;如有侵权请联系删除 第 1 章 项目建设总体框架设计 1.1 系统总体架构设计 市智慧人社项目从总体逻辑上可分为信息访问层、门户层、应用服务层、应用支撑层、数据资源层和基础设施层等六个层次&a…

华三(H3C)新服务器安装Windows server系统后插拔网线没有反应,华三R4900 G3服务器安装网卡驱动和芯片驱动

官网&#xff08;根据自己的服务器下载驱动&#xff09;&#xff1a;​​​​​​https://www.h3c.com/cn/BizPortal/DownLoadAccessory/DownLoadAccessoryFilt.aspx 在服务器没有光盘槽和U盘不被服务识别的时候的时候&#xff0c;服务器通过在管理口中的KVM控制&#xff0c;进…

第八章 使用Apache服务部署静态网站

文章目录 第八章 使用Apache服务部署静态网站一、网站服务程序1、网站服务介绍2、Apache程序介绍 二、配置服务文件参数1、Linux系统中的配置文件2、配置httpd服务程序时最常用的参数以及用途描述 三、SELinux安全子系统1、SELinux介绍2、SELinux服务配置模式3、Semanage命令4、…

前端响应超时、API-server 服务内存不足...碰见这类 DolphinScheduler 资源中心相关问题怎么办?...

作者 | 刘森 卡特加特 大数据工程师 Apache DolphinScheduler Contributor 最近&#xff0c;有些用户小伙伴反映在使用 Apache DolphinScheduler 资源中心时会遇到问题&#xff0c;社区小伙伴整理了一些常见问题&#xff0c;希望帮大家解决燃眉之急。 [WARN] 2023-04-25 03:02…

网络工程师网络管理软件SNMPc软件的下载,安装和使用教程说明

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

2直接连接的网络与VLAN划分【实验】【计算机网络】

2直接连接的网络与VLAN划分【实验】【计算机网络】 前言推荐2直接连接的网络与VLAN划分2.1共享式以太网和交换式以太网实验目的实验内容及实验环境实验原理共享式以太网交换式以太网 实验过程搭建实验环境初始化序训练操作共享式以太网-操作交换式以太网查看共享式以太网冲突查…

QueryStorm Crack

QueryStorm Crack 应用程序现在可以指定“minRuntimeVersion”。 添加了用于节流和API密钥管理的HTTP请求基础结构(请求/尝试/重试循环)。 改进了许可提示的处理(避免在多个单元格中评估许可功能时出现多个提示)。 已添加“IDialogServiceExt”接口&#xff0c;该接口允许应用程…