商场技术点-3

news2024/9/21 11:12:33

1.后端服务校验

1.1JSR-303介绍

JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

  JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
  Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

在这里插入图片描述

 Hibernate 中填充一部分在这里插入图片描述

 2.后端校验实现

 1.需要在commons服务中添加对应的依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.4.12</version>
        </dependency>

2.在需要校验的Bean的字段头部添加对应的注解 

 3.通过@Valid注解来开启JSR303的校验

 4.测试,通过postman提交空的数据

当我们提交一个非空的数据是可以通过的

5.校验不合法的提示信息,在Controller中通过 BindingResult对象来获取校验的结果信息,然后解析出来后封装为R对象响应

   @RequestMapping("/save")
    //@RequiresPermissions("product:brand:save")
    public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
        if(result.hasErrors()){
            // 提交的数据经过JSR303校验后有非法的字段
            Map<String,String> map = new HashMap<>();
            List<FieldError> fieldErrors = result.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                // 获取非法数据的 field
                String field = fieldError.getField();
                // 获取非法的field的提示信息
                String defaultMessage = fieldError.getDefaultMessage();
                map.put(field,defaultMessage);
            }
            return R.error(400,"提交的品牌表单数据不合法").put("data",map);
        }
		brandService.save(brand);

        return R.ok();

6.测试

 然后完善其他字段你的校验规则

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	private Long brandId;
	/**
	 * 品牌名
	 */
	//@NotEmpty
	//@NotNull
	@NotBlank(message = "品牌的名称不能为空")
	private String name;
	/**
	 * 品牌logo地址
	 */
	@NotBlank(message = "logo不能为空")
	@URL(message = "logo必须是一个合法URL地址")
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@NotBlank(message = "检索首字母不能为空")
	@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是单个的字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(message = "排序不能为null")
	@Min(value = 0,message = "排序不能小于0")
	private Integer sort;

}

2. 统一的异常处理

1.在SpringMVC中的统一异常处理我们通过ControllerAdvice来处理(在通用模块中)

/**
 * 统一的异常处理类
 */
/*@ResponseBody
@ControllerAdvice*/
@Slf4j
@RestControllerAdvice(basePackages = "com.msb.mall.product.controller")
public class ExceptionControllerAdvice {
    /**
     * 处理验证异常的方法
     * @param e
     */

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handlerValidExecption(MethodArgumentNotValidException e){
        Map<String,String> map = new HashMap<>();
        e.getFieldErrors().forEach((fieldError)->{
            map.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(400,"提交的数据不合法").put("data",map);
    }

    /**
     * 系统其他的异常处理
     * @param throwable
     * @return
     */
    @ExceptionHandler(Throwable.class)
    public R handlerExecption(Throwable throwable){
        log.error("错误信息:",throwable);
        return R.error(400,"未知异常信息").put("data",throwable.getMessage());
    }
}

2. 响应编码的规制制订,因为随着后面的业务越来越复杂,我们在响应异常信息的时候尽量准确的给客户端有用的提示信息。

通用的错误列表,响应的编码统一为5位数字,前面两位约定为业务场景,最后三位约定为错误码

10:表示通用

/001:参数格式错误 10001

/002:未知异常 10002

11:商品

12:订单

13:物流

14:会员

.....

3.定义对应的枚举类

package com.msb.common.exception;

/**
 * 错误编码和错误信息的枚举类
 */
public enum BizCodeEnume {

    UNKNOW_EXCEPTION(10000,"系统未知异常"),
    VALID_EXCEPTION(10001,"参数格式异常");

    private int code;
    private String msg;

    BizCodeEnume(int code,String msg){
        this.code = code;
        this.msg = msg;
    }
    public int getCode(){
        return code;
    }

    public String getMsg(){
        return msg;
    }
}

 4.在统一异常处理中我们就可以使用通用的编码

package com.msb.mall.product.exception;

import com.msb.common.exception.BizCodeEnume;
import com.msb.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 统一的异常处理类
 */
/*@ResponseBody
@ControllerAdvice*/
@Slf4j
@RestControllerAdvice(basePackages = "com.msb.mall.product.controller")
public class ExceptionControllerAdvice {
    /**
     * 处理验证异常的方法
     * @param e
     */

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handlerValidExecption(MethodArgumentNotValidException e){
        Map<String,String> map = new HashMap<>();
        e.getFieldErrors().forEach((fieldError)->{
            map.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        //return R.error(400,"提交的数据不合法").put("data",map);
        return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(), BizCodeEnume.VALID_EXCEPTION.getMsg())
                .put("data",map);
    }

    /**
     * 系统其他的异常处理
     * @param throwable
     * @return
     */
    @ExceptionHandler(Throwable.class)
    public R handlerExecption(Throwable throwable){
        log.error("错误信息:",throwable);
        //return R.error(400,"未知异常信息").put("data",throwable.getMessage());
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg())
                .put("data",throwable.getMessage());
    }
}

 5.测试

3.分组校验

在实际的业务场景中同一个Entity的校验可能会有不同的规则,比如添加数据品牌id必须为空,而更新数据品牌Id必须不为空,针对这种情况我们需要使用分组校验来实现

1.创建不同的接口

package com.msb.common.valid.group;

/**
 * 添加的接口
 */
public interface AddGroupInterface {
}
package com.msb.common.valid.group;

/**
 * 更新的接口
 */
public interface UpdateGroupInteface {
}

2.在Entity中指定分组规则 

package com.msb.mall.product.entity;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;

import com.msb.common.valid.group.AddGroupInterface;
import com.msb.common.valid.group.UpdateGroupInteface;
import com.msb.common.valid.ListValue;
import lombok.Data;
import org.hibernate.validator.constraints.URL;

import javax.validation.constraints.*;

/**
 * 品牌
 * 
 * @author qhl
 * @email 1970405838@.qqcom
 * @date 2023-01-09 20:42:37
 */
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	@NotNull(message = "更新时品牌id不能为空",groups = UpdateGroupInteface.class)
	@Null(message = "添加时品牌id必须为空",groups = AddGroupInterface.class)
	//@NotNull(message = "更新时品牌id不能为空")

	private Long brandId;
	/**
	 * 品牌名
	 */

	@NotBlank(message = "产品名称不能为空",groups = AddGroupInterface.class)//只能作用于String类型上且不能为null不能是“” 可以为“null”
	@NotBlank(message = "产品名称不能为空")
	private String name;
	/**
	 * 品牌logo地址
	 */
	@URL(message = "logo的URL不合法",groups = AddGroupInterface.class)
	@NotBlank(message = "logo不能为空",groups = {AddGroupInterface.class,UpdateGroupInteface.class})
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	@NotNull(message = "显示状态不能为空",groups = AddGroupInterface.class)
	@ListValue(val={0,1},groups = {AddGroupInterface.class,UpdateGroupInteface.class})
	private Integer showStatus;
	/**
	 * 检索首字母
	 */

	@NotBlank(message = "检索首字母不能为空",groups = AddGroupInterface.class)
	@Pattern(regexp = "^[a-zA-Z]$",message = "检索首字母必须是单个的字母",groups = {UpdateGroupInteface.class,AddGroupInterface.class})
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(message = "排序不能为null",groups = AddGroupInterface.class)
	@Min(value = 0,message = "排序不能小于0",groups = {UpdateGroupInteface.class,AddGroupInterface.class})
	private Integer sort;

}

3.通过@Validated注解来实现分组校验 

  /**
     * 保存
     */
    @RequestMapping("/save")
   // @RequiresPermissions("product:brand:save")
    public R save(/**@Valid*/ @Validated(AddGroupInterface.class)@RequestBody BrandEntity brand ) {
        

        brandService.save(brand);

        return R.ok();
    }

    /**
     * 修改
     */
    @RequestMapping("/update")
    //@RequiresPermissions("product:brand:update")
    public R update(@Validated(UpdateGroupInteface.class)@RequestBody BrandEntity brand){
		brandService.updateById(brand);

        return R.ok();
    }

4.自定义校验注解

面临特殊的校验需要我们可以通过正则表达式来处理,当然我们也可以通过自定义校验注解的方式来实现。

1.创建自定义的校验注解

/**
 * 自定义的校验注解
 */
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {

    String message() default "{com.msb.common.valid.ListValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    int[] val() default {};
}

 2.对应需要创建提示信息的属性文件

 3.创建一个自定义的校验器

package com.msb.common.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.annotation.Annotation;
import java.sql.ClientInfoStatus;
import java.util.HashSet;

/**
 * 对应的校验注解的校验器
 */
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
    private HashSet<Integer> set = new HashSet<>();

    /**
     * 初始化的方法
     * 举例:@ListValue(val={1,0})
     * 获取到 1 0
     * @param constraintAnnotation
     */
    @Override
    public void initialize(ListValue constraintAnnotation) {
        int[] val = constraintAnnotation.val();// 0 1
        for (int i : val) {
            set.add(i);
        }
    }

    /**
     * 判断校验是否成功的方法
     * @param value 客户端传递的对应的属性的值 判断value是否在0 , 1 中
     * @param context
     * @return
     */
    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return set.contains(value);
    }
}

4.关联自定义的校验注解和校验器

 

5.提取的知识点 

5.1@Validated和@Valid区别:

1. 分组

@Validated:提供了一个分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制,这个网上也有资料,不详述。@Valid:作为标准JSR-303规范,还没有吸收分组的功能。

2. 注解地方

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

两者是否能用于成员属性(字段)上直接影响能否提供嵌套验证的功能。

3. 嵌套验证

在比较两者嵌套验证时,先说明下什么叫做嵌套验证。比如我们现在有个实体叫做Item:

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "至少要有一个属性")
    private List<Prop> props;
}

Item带有很多属性,属性里面有属性id,属性值id,属性名和属性值,如下所示:

public class Prop {

    @NotNull(message = "pid不能为空")
    @Min(value = 1, message = "pid必须为正整数")
    private Long pid;

    @NotNull(message = "vid不能为空")
    @Min(value = 1, message = "vid必须为正整数")
    private Long vid;

    @NotBlank(message = "pidName不能为空")
    private String pidName;

    @NotBlank(message = "vidName不能为空")
    private String vidName;
}

属性这个实体也有自己的验证机制,比如属性和属性值id不能为空,属性名和属性值不能为空等。

现在我们有个ItemController接受一个Item的入参,想要对Item进行验证,如下所示:

@RestController
public class ItemController {

    @RequestMapping("/item/add")
    public void addItem(@Validated Item item, BindingResult bindingResult) {
        doSomething();
    }
}

在上图中,如果Item实体的props属性不额外加注释,只有@NotNull和@Size,无论入参采用@Validated还是@Valid验证,Spring Validation框架只会对Item的id和props做非空和数量验证,不会对props字段里的Prop实体进行字段验证,也就是@Validated和@Valid加在方法参数前,都不会自动对参数进行嵌套验证。也就是说如果传的List<Prop>中有Prop的pid为空或者是负数,入参验证不会检测出来。

为了能够进行嵌套验证,必须手动在Item实体的props字段上明确指出这个字段里面的实体也要进行验证。由于@Validated不能用在成员属性(字段)上,但是@Valid能加在成员属性(字段)上,而且@Valid类注解上也说明了它支持嵌套验证功能,那么我们能够推断出:@Valid加在方法参数时并不能够自动进行嵌套验证,而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated或@Valid来进行嵌套验证。

我们修改Item类如下所示:

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @Valid // 嵌套验证必须用@Valid
    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "props至少要有一个自定义属性")
    private List<Prop> props;
}

然后我们在ItemController的addItem函数上再使用@Validated或者@Valid,就能对Item的入参进行嵌套验证。此时Item里面的props如果含有Prop的相应字段为空的情况,Spring Validation框架就会检测出来,bindingResult就会记录相应的错误。

总结一下@Validated和@Valid在嵌套验证功能上的区别:

@Validated:用在方法入参上无法单独提供嵌套验证功能。不能用在成员属性(字段)上,也无法提示框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

@Valid:用在方法入参上无法单独提供嵌套验证功能。能够用在成员属性(字段)上,提示验证框架进行嵌套验证。能配合嵌套验证注解@Valid进行嵌套验证。

5.2@NotNull与@NotBlank

@NotBlank只能作用于String类型上且不能为null不能是“” 可以为“null”

@NotNull不能为null,不能为"",也不能为"null",可以作用在所有字段上

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

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

相关文章

springboot项目配置文件加密

1背景&#xff1a; springboot项目中要求不能采用明文密码&#xff0c;故采用配置文件加密. 目前采用有密码的有redis nacos rabbitmq mysql 这些配置文件 2技术 2.1 redis nacos rabbitmq 配置文件加密 采用加密方式是jasypt 加密 2.1.1 加密步骤 2.1.2 引入maven依赖 …

Android进阶之路 - StringUtils、NumberUtils 场景源码

忘记是在去年还是前年的时候遇到一个需要检测所传字符串是否为数字的场景&#xff0c;开始使用 NumberUtils.isNumber() 提示错误 &#xff0c;没有解决问题&#xff08;可能是因为依赖版本导致&#xff09;&#xff0c;最后使用的是StringUtils.isNumeric()&#xff0c;当时关…

剑指 Offer 43. 1~n 整数中 1 出现的次数

题目 输入一个整数 n &#xff0c;求1&#xff5e;n这n个整数的十进制表示中1出现的次数。 例如&#xff0c;输入12&#xff0c;1&#xff5e;12这些整数中包含1 的数字有1、10、11和12&#xff0c;1一共出现了5次。 思路 要求出小于等于 n 的非负整数中数字 1 出现的个数…

Prometheus系列(五)grafana web 配置邮件告警

目录 1. contact points&#xff08;创建告警渠道&#xff09; 2. Notification policies&#xff08;创建告警通道匹配规则&#xff09; 3. Alert rules&#xff08;配置告警策略&#xff09; 告警配置 告警页面名词解释&#xff1a; 1. contact points&#xff08;创建告…

玩转数据结构之Java实现线段树

前言 线段树是一种二叉搜索树&#xff0c;线段树的每个结点都存储了一个区间&#xff0c;也可以理解成一个线段&#xff0c;在这些线段上进行搜索操作得到你想要的答案。 线段树的适用范围很广&#xff0c;可以在线维护修改以及查询区间上的最值&#xff0c;求和。更可以扩充到…

一文浅谈sql中的 in与not in,exists与not exists的区别以及性能分析

文章目录1. 文章引言2. 查询对比2.1 in和exists2.2 not in 和not exists2.3 in 与 的区别3. 性能分析3.1 in和exists3.2 NOT IN 与NOT EXISTS4. 重要总结1. 文章引言 我们在工作的过程中&#xff0c;经常使用in&#xff0c;not in&#xff0c;exists&#xff0c;not exists来…

Unity2018.4.x~2021.3.x版 Android资源处理

注意&#xff1a;本文都是针对使用Gradle编译从Unity2018.4.x到Unity2020.3.x都是可以直接将Android的适配资源直接放到${PROJECT_PATH}/Assets/Plugins/Android/对应的目录下的&#xff0c;如&#xff1a;在此目录下可以方安卓平台对应的assets、res目录及子目录资源&#xff…

前端的CSS样式表知识提要

文章目录前言基本概念屏幕尺寸屏幕分辨率屏幕像素密度/像素密度/屏幕密度视口和浏览器窗口长度单位&#xff1a;px、em/rem和vhCSS属性的继承与覆盖CSS选择器CSS 布局基础盒子模型绝对定位和相对定位display属性浮动正常布局流&#xff08;normal flow&#xff09;Flexbox 布局…

【Python表白代码】 2.14“Valentine‘s Day”“没别的意思 就是借着特殊日子说声喜欢你”你在哪儿?我去见你~(各种玫瑰源码合集)

导语 Valentines Day Every man is a poet when he is in love 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 哈喽&#xff01;我是你们的木木子吖~ 情人节又到了&#xff0c;礼物备好了没&am…

k8s部署Prometheus+Grafana

1.prometheus简介 Prometheus是一个开源的系统监控和警报工具包&#xff0c;最初由SoundCloud开发的&#xff0c;社区活跃&#xff0c;2016年加入了云原生计算基金会成为继Kubernetes之后的第二个托管项目&#xff1b;普罗米修斯以时间序列数据的形式收集并存储度量值&#xff…

python爬虫--xpath模块简介

一、前言 前两篇博客讲解了爬虫解析网页数据的两种常用方法&#xff0c;re正则表达解析和beautifulsoup标签解析&#xff0c;所以今天的博客将围绕另外一种数据解析方法&#xff0c;它就是xpath模块解析&#xff0c;话不多说&#xff0c;进入内容&#xff1a; 一、简介 XPat…

2023年谷歌蜘蛛池最全指南

本文主要是2023年关于谷歌蜘蛛池的一系列疑问&#xff0c;我们逐一提供解答。 本文由光算创作&#xff0c;有可能会被修改和剽窃&#xff0c;我们佛系对待这种行为吧。 首先最常见的新手问题是“什么叫谷歌蜘蛛池&#xff1f;” 答案是&#xff1a;谷歌蜘蛛池是一个深度研究谷…

微服务网关(九)负载均衡底层详细

微服务网关&#xff08;九&#xff09;负载均衡 四大负载均衡策略 随机负载 随机挑选目标服务器IP 轮询负载 ABC三台服务器&#xff0c;以ABCABC的顺序依次轮询 加权轮询 给目标服务器设置访问权重值&#xff0c;按照权重轮询负载 一致性哈希轮询 固定的一个客户端IP请求访…

图文解答之最短路径||

最短路径|| 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为“Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为“Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上角到…

rust过程宏 proc-macro-workshop解题-0-介绍

名字版本号rust1.69.0OSubuntu22.04proc-macro-workshop是一个学习rust过程宏非常好的工程。里边包含五大类题目。并且每种题目都有实际价值,可以应用在企业级项目中。我们在这里先介绍一下这个项目如何运行如何测试,如何验证过程宏的正确性以及如何调试。 本文只围绕以下几个…

2月编程语言排行榜出炉,第一名势头强劲

近日&#xff0c;TIOBE公布了2023年2月编程语言排行榜&#xff0c;本月各个语言表现如何&#xff1f;谁又摘得桂冠&#xff1f; TIOBE 2月Top15编程语言&#xff1a; 详细榜单查看TIOBE官网 https://www.tiobe.com/tiobe-index/ 关注IT行业的小伙伴们都知道&#xff0c;编程…

Linux入门篇(一)

Linux前言Linux初探Linux内核GNU实用工具shellLinux发行版bash shell 基础Linux文件系统Linux文件操作命令前言 在阅读诸如docker之类的书的时候&#xff0c;经常碰到Linux的知识。同时&#xff0c;大部分的盲区也是在Linux方面。因此就想稍微了解一下这个广为人使用的操作系统…

docker-入门到精通

docker知识总结 参考文档 https://jiajially.gitbooks.io/dockerguide/content/chapter_fastlearn/docker_run/–volumes-from.html 1、什么是docker ​ 容器技术、虚拟化技术已经成为一种被大家广泛认可的服务器资源共享方式&#xff0c;容器技术可以在按需构建操作系统实例…

vue3 Proxy响应式原理分析(面试题)

在开始正文前&#xff0c;先理一下vue2 Object.defineProperty 和 vue 3 Proxy 区别&#xff1a; Object.defineProperty&#xff1a;数据劫持 Proxy&#xff1a;数据代理 注意&#xff1a; 响应式原理和双向数据绑定原理是两回事&#xff0c;一般面试官会先问响应式原理再问双…

内网渗透(二十三)之Windows协议认证和密码抓取-Mimikatz介绍和各种模块使用方法

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…