[Abp 源码分析]自动审计记录

点击上方蓝字关注我们

0.简介

Abp 框架为我们自带了审计日志功能,审计日志可以方便地查看每次请求接口所耗的时间,能够帮助我们快速定位到某些性能有问题的接口。除此之外,审计日志信息还包含有每次调用接口时客户端请求的参数信息,客户端的 IP 与客户端使用的浏览器。有了这些数据之后,我们就可以很方便地复现接口产生 BUG 时的一些环境信息。

当然如果你脑洞更大的话,可以根据这些数据来开发一个可视化的图形界面,方便开发与测试人员来快速定位问题。

PS:

如果使用了 Abp.Zero 模块则自带的审计记录实现是存储到数据库当中的,但是在使用 EF Core + MySQL(EF Provider 为 Pomelo.EntityFrameworkCore.MySql) 在高并发的情况下会有数据库连接超时的问题,这块推荐是重写实现,自己采用 Redis 或者其他存储方式。

如果需要禁用审计日志功能,则需要在任意模块的预加载方法(PreInitialize()) 当中增加如下代码关闭审计日志功能。

public class XXXStartupModule
{public override PreInitialize(){// 禁用审计日志Configuration.Auditing.IsEnabled = false;}
}

1.启动流程

审计组件与参数校验组件一样,都是通过 MVC 过滤器与 Castle 拦截器来实现记录的。也就是说,在每次调用接口/方法时都会进入 过滤器/拦截器 并将其写入到数据库表 AbpAuditLogs 当中。

其核心思想十分简单,就是在执行具体接口方法的时候,先使用 StopWatch 对象来记录执行完一个方法所需要的时间,并且还能够通过 HttpContext 来获取到一些客户端的关键信息。

2.1 过滤器注入

同上一篇文章所讲的一样,过滤器是在 AddAbp() 方法内部的 ConfigureAspNetCore() 方法注入的。

private static void ConfigureAspNetCore(IServiceCollection services, IIocResolver iocResolver)
{// ... 其他代码//Configure MVCservices.Configure<MvcOptions>(mvcOptions =>{mvcOptions.AddAbp(services);});// ... 其他代码
}

而下面就是过滤器的注入方法:

internal static class AbpMvcOptionsExtensions
{public static void AddAbp(this MvcOptions options, IServiceCollection services){// ... 其他代码AddFilters(options);// ... 其他代码}// ... 其他代码private static void AddFilters(MvcOptions options){// ... 其他过滤器注入// 注入审计日志过滤器options.Filters.AddService(typeof(AbpAuditActionFilter));// ... 其他过滤器注入}// ... 其他代码
}

2.2 拦截器注入

注入拦截器的地方与 DTO 自动验证的拦截器的位置一样,都是在 AbpBootstrapper 对象被构造的时候进行注册。

public class AbpBootstrapper : IDisposable
{private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null){// ... 其他代码if (!options.DisableAllInterceptors){AddInterceptorRegistrars();}}// ... 其他代码// 添加各种拦截器private void AddInterceptorRegistrars(){ValidationInterceptorRegistrar.Initialize(IocManager);AuditingInterceptorRegistrar.Initialize(IocManager);EntityHistoryInterceptorRegistrar.Initialize(IocManager);UnitOfWorkRegistrar.Initialize(IocManager);AuthorizationInterceptorRegistrar.Initialize(IocManager);}// ... 其他代码
}

转到 AuditingInterceptorRegistrar 的具体实现可以发现,他在内部针对于审计日志拦截器的注入是区分了类型的。

internal static class AuditingInterceptorRegistrar
{public static void Initialize(IIocManager iocManager){iocManager.IocContainer.Kernel.ComponentRegistered += (key, handler) =>{// 如果审计日志配置类没有被注入,则直接跳过if (!iocManager.IsRegistered<IAuditingConfiguration>()){return;}var auditingConfiguration = iocManager.Resolve<IAuditingConfiguration>();// 判断当前 DI 所注入的类型是否应该为其绑定审计日志拦截器if (ShouldIntercept(auditingConfiguration, handler.ComponentModel.Implementation)){handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(AuditingInterceptor)));}};}// 本方法主要用于判断当前类型是否符合绑定拦截器的条件private static bool ShouldIntercept(IAuditingConfiguration auditingConfiguration, Type type){// 首先判断当前类型是否在配置类的注册类型之中,如果是,则进行拦截器绑定if (auditingConfiguration.Selectors.Any(selector => selector.Predicate(type))){return true;}// 当前类型如果拥有 Audited 特性,则进行拦截器绑定if (type.GetTypeInfo().IsDefined(typeof(AuditedAttribute), true)){return true;}// 如果当前类型内部的所有方法当中有一个方法拥有 Audited 特性,则进行拦截器绑定if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true))){return true;}// 都不满足则返回 false,不对当前类型进行绑定return false;}
}

可以看到在判断是否绑定拦截器的时候,Abp 使用了 auditingConfiguration.Selectors 的属性来进行判断,那么默认 Abp 为我们添加了哪些类型是必定有审计日志的呢?

通过代码追踪,我们来到了 AbpKernalModule 类的内部,在其预加载方法里面有一个 AddAuditingSelectors() 的方法,该方法的作用就是添加了一个针对于应用服务类型的一个选择器对象。

public sealed class AbpKernelModule : AbpModule
{public override void PreInitialize(){// ... 其他代码AddAuditingSelectors();// ... 其他代码}// ... 其他代码private void AddAuditingSelectors(){Configuration.Auditing.Selectors.Add(new NamedTypeSelector("Abp.ApplicationServices",type => typeof(IApplicationService).IsAssignableFrom(type)));}// ... 其他代码
}

我们先看一下 NamedTypeSelector 的一个作用是什么,其基本类型定义由一个 string 和 Func<Type, bool> 组成,十分简单,重点就出在这个断言委托上面。

public class NamedTypeSelector
{// 选择器名称public string Name { get; set; }// 断言委托public Func<Type, bool> Predicate { get; set; }public NamedTypeSelector(string name, Func<Type, bool> predicate){Name = name;Predicate = predicate;}
}

回到最开始的地方,当 Abp 为 Selectors 添加了一个名字为 "Abp.ApplicationServices" 的类型选择器。其断言委托的大体意思就是传入的 **type ** 参数是继承自 IApplicationService 接口的话,则返回 true,否则返回 false

这样在程序启动的时候,首先注入类型的时候,会首先进入上文所述的拦截器绑定类当中,这个时候会使用 Selectors 内部的类型选择器来调用这个集合内部的断言委托,只要这些选择器对象有一个返回 true,那么就直接与当前注入的 type 绑定拦截器。

2.代码分析

2.1 过滤器代码分析

首先查看这个过滤器的整体类型结构,一个标准的过滤器,肯定要实现 IAsyncActionFilter 接口。从下面的代码我们可以看到其注入了 IAbpAspNetCoreConfiguration 和一个 IAuditingHelper 对象。这两个对象的作用分别是判断是否记录日志,另一个则是用来真正写入日志所使用的。

public class AbpAuditActionFilter : IAsyncActionFilter, ITransientDependency
{// 审计日志组件配置对象private readonly IAbpAspNetCoreConfiguration _configuration;// 真正用来写入审计日志的工具类private readonly IAuditingHelper _auditingHelper;public AbpAuditActionFilter(IAbpAspNetCoreConfiguration configuration, IAuditingHelper auditingHelper){_configuration = configuration;_auditingHelper = auditingHelper;}public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){// ... 代码实现}// ... 其他代码
}

接着看 AbpAuditActionFilter() 方法内部的实现,进入这个过滤器的时候,通过 ShouldSaveAudit() 方法来判断是否要写审计日志。

之后呢与 DTO 自动验证的过滤器一样,通过 AbpCrossCuttingConcerns.Applying() 方法为当前的对象增加了一个标识,用来告诉拦截器说我已经处理过了,你就不要再重复处理了。

再往下就是创建审计信息,执行具体接口方法,并且如果产生了异常的话,也会存放到审计信息当中。

最后接口无论是否执行成功,还是说出现了异常信息,都会将其性能计数信息同审计信息一起,通过 IAuditingHelper 存储起来。

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{// 判断是否写日志if (!ShouldSaveAudit(context)){await next();return;}// 为当前类型打上标识using (AbpCrossCuttingConcerns.Applying(context.Controller, AbpCrossCuttingConcerns.Auditing)){// 构造审计信息(AuditInfo)var auditInfo = _auditingHelper.CreateAuditInfo(context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(),context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo,context.ActionArguments);// 开始性能计数var stopwatch = Stopwatch.StartNew();try{// 尝试调用接口方法var result = await next();// 产生异常之后,将其异常信息存放在审计信息之中if (result.Exception != null && !result.ExceptionHandled){auditInfo.Exception = result.Exception;}}catch (Exception ex){// 产生异常之后,将其异常信息存放在审计信息之中auditInfo.Exception = ex;throw;}finally{// 停止计数,并且存储审计信息stopwatch.Stop();auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);await _auditingHelper.SaveAsync(auditInfo);}}
}

2.2 拦截器代码分析

拦截器处理时的总体思路与过滤器类似,其核心都是通过 IAuditingHelper 来创建审计信息和持久化审计信息的。只不过呢由于拦截器不仅仅是处理 MVC 接口,也会处理内部的一些类型的方法,所以针对同步方法与异步方法的处理肯定会复杂一点。

拦截器呢,我们关心一下他的核心方法 Intercept() 就行了。

public void Intercept(IInvocation invocation)
{// 判断过滤器是否已经处理了过了if (AbpCrossCuttingConcerns.IsApplied(invocation.InvocationTarget, AbpCrossCuttingConcerns.Auditing)){invocation.Proceed();return;}// 通过 IAuditingHelper 来判断当前方法是否需要记录审计日志信息if (!_auditingHelper.ShouldSaveAudit(invocation.MethodInvocationTarget)){invocation.Proceed();return;}// 构造审计信息var auditInfo = _auditingHelper.CreateAuditInfo(invocation.TargetType, invocation.MethodInvocationTarget, invocation.Arguments);// 判断方法的类型,同步方法与异步方法的处理逻辑不一样if (invocation.Method.IsAsync()){PerformAsyncAuditing(invocation, auditInfo);}else{PerformSyncAuditing(invocation, auditInfo);}
}// 同步方法的处理逻辑与 MVC 过滤器逻辑相似
private void PerformSyncAuditing(IInvocation invocation, AuditInfo auditInfo)
{var stopwatch = Stopwatch.StartNew();try{invocation.Proceed();}catch (Exception ex){auditInfo.Exception = ex;throw;}finally{stopwatch.Stop();auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);_auditingHelper.Save(auditInfo);}
}// 异步方法处理
private void PerformAsyncAuditing(IInvocation invocation, AuditInfo auditInfo)
{var stopwatch = Stopwatch.StartNew();invocation.Proceed();if (invocation.Method.ReturnType == typeof(Task)){invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithFinally((Task) invocation.ReturnValue,exception => SaveAuditInfo(auditInfo, stopwatch, exception));}else //Task<TResult>{invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithFinallyAndGetResult(invocation.Method.ReturnType.GenericTypeArguments[0],invocation.ReturnValue,exception => SaveAuditInfo(auditInfo, stopwatch, exception));}
}private void SaveAuditInfo(AuditInfo auditInfo, Stopwatch stopwatch, Exception exception)
{stopwatch.Stop();auditInfo.Exception = exception;auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);_auditingHelper.Save(auditInfo);
}

这里异步方法的处理在很早之前的工作单元拦截器就有过讲述,这里就不再重复说明了。

2.3 核心的 IAuditingHelper

从代码上我们就可以看到,不论是拦截器还是过滤器都是最终都是通过 IAuditingHelper 对象来储存审计日志的。Abp 依旧为我们实现了一个默认的 AuditingHelper ,实现了其接口的所有方法。我们先查看一下这个接口的定义:

public interface IAuditingHelper
{// 判断当前方法是否需要存储审计日志信息bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false);// 根据参数集合创建一个审计信息,一般用于拦截器AuditInfo CreateAuditInfo(Type type, MethodInfo method, object[] arguments);// 根据一个参数字典类来创建一个审计信息,一般用于 MVC 过滤器AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments);// 同步保存审计信息void Save(AuditInfo auditInfo);// 异步保存审计信息Task SaveAsync(AuditInfo auditInfo);
}

我们来到其默认实现 AuditingHelper 类型,先看一下其内部注入了哪些接口。

public class AuditingHelper : IAuditingHelper, ITransientDependency
{// 日志记录器,用于记录日志public ILogger Logger { get; set; }// 用于获取当前登录用户的信息public IAbpSession AbpSession { get; set; }// 用于持久话审计日志信息public IAuditingStore AuditingStore { get; set; }// 主要作用是填充审计信息的客户端调用信息private readonly IAuditInfoProvider _auditInfoProvider;// 审计日志组件的配置相关private readonly IAuditingConfiguration _configuration;// 在调用 AuditingStore 进行持久化的时候使用,创建一个工作单元private readonly IUnitOfWorkManager _unitOfWorkManager;// 用于序列化参数信息为 JSON 字符串private readonly IAuditSerializer _auditSerializer;public AuditingHelper(IAuditInfoProvider auditInfoProvider,IAuditingConfiguration configuration,IUnitOfWorkManager unitOfWorkManager,IAuditSerializer auditSerializer){_auditInfoProvider = auditInfoProvider;_configuration = configuration;_unitOfWorkManager = unitOfWorkManager;_auditSerializer = auditSerializer;AbpSession = NullAbpSession.Instance;Logger = NullLogger.Instance;AuditingStore = SimpleLogAuditingStore.Instance;}// ... 其他实现的接口
}

2.3.1 判断是否创建审计信息

首先分析一下其内部的 ShouldSaveAudit() 方法,整个方法的核心作用就是根据传入的方法类型来判定是否为其创建审计信息。

其实在这一串 if 当中,你可以发现有一句代码对方法是否标注了 DisableAuditingAttribute 特性进行了判断,如果标注了该特性,则不为该方法创建审计信息。所以我们就可以通过该特性来控制自己应用服务类,控制里面的的接口是否要创建审计信息。同理,我们也可以通过显式标注 AuditedAttribute 特性来让拦截器为这个方法创建审计信息。

public bool ShouldSaveAudit(MethodInfo methodInfo, bool defaultValue = false)
{if (!_configuration.IsEnabled){return false;}if (!_configuration.IsEnabledForAnonymousUsers && (AbpSession?.UserId == null)){return false;}if (methodInfo == null){return false;}if (!methodInfo.IsPublic){return false;}if (methodInfo.IsDefined(typeof(AuditedAttribute), true)){return true;}if (methodInfo.IsDefined(typeof(DisableAuditingAttribute), true)){return false;}var classType = methodInfo.DeclaringType;if (classType != null){if (classType.GetTypeInfo().IsDefined(typeof(AuditedAttribute), true)){return true;}if (classType.GetTypeInfo().IsDefined(typeof(DisableAuditingAttribute), true)){return false;}if (_configuration.Selectors.Any(selector => selector.Predicate(classType))){return true;}}return defaultValue;
}

2.3.2 创建审计信息

审计信息在创建的时候,就为我们将当前调用接口时的用户信息存放在了审计信息当中,之后通过 IAuditInfoProvider 的 Fill() 方法填充了客户端 IP 与浏览器信息。

public AuditInfo CreateAuditInfo(Type type, MethodInfo method, IDictionary<string, object> arguments)
{// 构建一个审计信息对象var auditInfo = new AuditInfo{TenantId = AbpSession.TenantId,UserId = AbpSession.UserId,ImpersonatorUserId = AbpSession.ImpersonatorUserId,ImpersonatorTenantId = AbpSession.ImpersonatorTenantId,ServiceName = type != null? type.FullName: "",MethodName = method.Name,// 将参数转换为 JSON 字符串Parameters = ConvertArgumentsToJson(arguments),ExecutionTime = Clock.Now};try{// 填充客户 IP 与浏览器信息等_auditInfoProvider.Fill(auditInfo);}catch (Exception ex){Logger.Warn(ex.ToString(), ex);}return auditInfo;
}

2.4 审计信息持久化

通过上一小节我们知道了在调用审计信息保存接口的时候,实际上是调用的 IAuditingStore 所提供的 SaveAsync(AuditInfo auditInfo) 方法来持久化这些审计日志信息的。

如果你没有集成 Abp.Zero 项目的话,则使用的是默认的实现,就是简单通过 ILogger 输出审计信息到日志当中。

默认有这两种实现,至于第一种是 Abp 的单元测试项目所使用的。

这里我们就简单将一下 AuditingStore 这个实现吧,其实很简单的,就是注入了一个仓储,在保存的时候往审计日志表插入一条数据即可。

这里使用了 AuditLog.CreateFromAuditInfo() 方法将 AuditInfo 类型的审计信息转换为数据库实体,用于仓储进行插入操作。

public class AuditingStore : IAuditingStore, ITransientDependency
{private readonly IRepository<AuditLog, long> _auditLogRepository;public AuditingStore(IRepository<AuditLog, long> auditLogRepository){_auditLogRepository = auditLogRepository;}public virtual Task SaveAsync(AuditInfo auditInfo){// 向表中插入数据return _auditLogRepository.InsertAsync(AuditLog.CreateFromAuditInfo(auditInfo));}
}

同样,这里建议重新实现一个 AuditingStore,存储在 Redis 或者其他地方。

3. 后记

前几天发现 Abp 的团队有开了一个新坑,叫做 Abp vNext 框架,该框架全部基于 .NET Core 进行开发,而且会针对微服务项目进行专门的设计,有兴趣的朋友可以持续关注。

其 GitHub 地址为:https://github.com/abpframework/abp/

官方地址为:https://abp.io/

作者:myzony

出处:https://www.cnblogs.com/myzony/p/9723531.html

公众号“码侠江湖”所发表内容注明来源的,版权归原出处所有(无法查证版权的或者未注明出处的均来自网络,系转载,转载的目的在于传递更多信息,版权属于原作者。如有侵权,请联系,笔者会第一时间删除处理!

扫描二维码

获取更多精彩

码侠江湖


喜欢就点个在看再走吧

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

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

相关文章

我的老公是枚码农

前两天看到一篇写程序员的爆文&#xff0c;虽然略显夸张&#xff0c;但也着实有趣。忽然想到身边人也是一枚码农&#xff0c;浑身上下也是浓厚的码农气息&#xff0c;遂也胡乱写了几笔&#xff0c;博君一笑&#xff0c;为了方便起见&#xff0c;就称其为“码农哥”。 1 码农哥还…

如何修复硬盘坏道?

首先需要先确认硬盘的坏道是逻辑坏道还是物理坏道。 方法很简单&#xff0c;在电脑刚启动时按“F8”键&#xff0c;选择“Command Prompt only”进入DOS模式。这种方法的操作系统必须为Windows95/98&#xff0c;若为Windows2000/XP/2003&#xff0c;请使用DOS启动盘启动。然后执…

c++11 string u8_深入理解C++11:C++11新

一.数据对齐在了解为什么数据需要对齐之前&#xff0c;我们可以回顾一下打印结构体的大小这个C/C中的经典案例。先看代码&#xff1a;#include using namespace std;struct HowManyBytes{char a;int b;};int main(){cout<>endl;cout<>endl;cout<>endl;cout&l…

harmonyos con,鸿蒙HarmonyOS系统中的JS开发框架

HarmonyOS开源至今已经一个多月&#xff0c;源码托管在国内知名开源平台码云上&#xff0c;https://gitee.com/openharmony 。我最感兴趣的就是JS 框架 ace_lite_jsfwk&#xff0c;从名字中可以看出来这是一个非常轻量级的框架&#xff0c;官方介绍说是“轻量级 JS 核心开发框架…

python公式_Python读取excel文件中带公式的值的实现

在进行excel文件读取的时候&#xff0c;我自己设置了部分直接从公式获取单元格的值 但是用之前的读取方法进行读取的时候&#xff0c;返回值为空 import os import xlrd from xlutils.copy import copy file_path os.path.abspath(os.path.dirname(__file__)) # 获取当前文件目…

.NET工资低?那肯定是你打开的方式不正确

点击上方蓝字关注我们因为工作的关系&#xff0c;本人总是会接触到一些刚踏入社会没多久的.NET开发小伙伴。尤其是年关将近&#xff0c;这时候想要跳槽的人特别多&#xff0c;所以收到一些小伙伴的迷茫求解。今天就拿其中一个来说&#xff0c;我们暂且称他为A同学吧。A同学是一…

10分钟读懂人工智能、机器学习到底有什么关系

文末彩蛋&#xff0c;错过哭一年。。。。 人工智能的浪潮正在席卷全球&#xff0c;诸多词汇时刻萦绕在我们耳边&#xff1a;人工智能&#xff08;Artificial Intelligence&#xff09;、机器学习&#xff08;Machine Learning&#xff09;。不少人对这些高频词汇的含义及其背后…

复制Oracle表的结构

概述&#xff1a; 复制表的结构(只复制表结构,源表名&#xff1a;b_Jkpt_Oaxt_Trafficaccident 新表名&#xff1a;Test_OAXT_TrafficAccident) Oralce语句: create table Test_OAXT_TrafficAccident as select * from b_Jkpt_Oaxt_Trafficaccident where 12

Web API实现微信公众平台开发-接收数据Post

介绍当普通微信用户向公众账号发消息时&#xff0c;微信服务器将POST消息的XML数据包到开发者填写的URL上。在微信用户和公众号产生交互的过程中&#xff0c;用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址&#xff0c;从而开发…

gbdt 算法比随机森林容易_随机森林与GBDT

Bagging策略1.总样本数量是n个&#xff0c;从样本中重采样(有放回的)选出n个样本 &#xff0c;会有约33.2%的样本不会被抽到2.在所有属性上对这n个样本建立分类器(比如决策树&#xff0c;svm&#xff0c;lr)3.重复步骤1和2m次&#xff0c;建立了m个分类器4.将数据放在这m个分类…

苏泊尔搭载华为鸿蒙系统,华为鸿蒙打算在一年内跨过生死线,拿下16%的市场份额...

原标题&#xff1a;华为鸿蒙打算在一年内跨过生死线&#xff0c;拿下16%的市场份额华为鸿蒙操作系统发布已经有一段时间了&#xff0c;这个操作系统直到上个月月底才开启了公测&#xff0c;很多用户都已经使用上了华为的这个鸿蒙操作系统。根据不少用户的反馈情况来看&#xff…

python获取系统时间函数_简单记录python的时间函数操作

1. time和datetime模块 import datetime,time 2. 获得当前时间 time.time() #获得当前时间&#xff0c;返回float型 time.localtime([float time]) #获得本地当前时间&#xff0c;返回time.struct_time类型 说明&#xff1a;struct_time是一个只读的9元组&#xff0c;其中参数命…

10个最佳的大数据处理编程语言

大数据的浪潮仍在继续。它渗透到了几乎所有的行业&#xff0c;信息像洪水一样地席卷企业&#xff0c;使得软件越发庞然大物&#xff0c;比如Excel看上去就变得越来越笨拙。数据处理不再无足轻重&#xff0c;并且对精密分析和强大又实时处理的需要变得前所未有的巨大。 那么&…

简单谈谈Server2008的NAP到底是什么

什么是NAP&#xff1f; NAP-Network Access Protection&#xff0c;网络访问保护。我觉得其实还不完整&#xff0c;我认为完整的应该叫做网络策略访问保护。他的作用是用策略来保护客户端对网络的访问&#xff0c;确保整个网络的访问过程是达到一定安全级别的。07年初前我开…

再记一次 应用服务器 CPU 暴高事故分析

一&#xff1a;背景 1. 前言大概有2个月没写博客了&#xff0c;不是不想写哈????&#xff0c;关注公号的朋友应该知道我这两个月一直都在翻译文章&#xff0c;前前后后大概100篇左右吧&#xff0c;前几天看公号的 常读用户 降了好几十&#xff0c;心疼哈&#xff0c;还得回…

request-promise 获取返回头信息_http返回的状态码 大全

00 Continue 继续。客户端应继续其请求101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议&#xff0c;例如&#xff0c;切换到HTTP的新版本协议200 OK 请求成功。一般用于GET与POST请求201 Created 已创建。成功请求并创建了新的资源…

android shell用户界面,shell界面下安装和卸载Android应用程序(apk包)

安装1.$ adb install apk文件名安装系统应用$ adb push apk文件名 /system/app卸载$ adb uninstall 包名Home->设置->应用程序->管理应用程序->选择某应用程序->卸载相关文件/system/app/apk文件2.第三方应用/data/data/包名4.dex文件是Android 虚拟机Dalvik 支…

乐高创意机器人moc_乐高MOC佳作欣赏丨机械之美机器人乐高作品集1

导语乐高似乎能搭建任何事物&#xff0c;即使是机器人&#xff0c;机甲也不丝毫逊色&#xff0c;巧妙地运用零件&#xff0c;打破常规组合模式&#xff0c;让乐高零件组合在一起竟能呈现充满未来科技感的机器人&#xff0c;它们有的形态奇特&#xff0c;面貌诡异&#xff0c;有…

”残酷“人生第一步

仔仔终于要面对人生中的第一次巨大困难&#xff1a;断奶了。恰逢夫人要出差几日&#xff0c;刚刚开始&#xff0c;我们都很犹豫。确实很怕他整夜哭闹。但是思考再三还是决定&#xff0c;让小家伙独自面对吧。于是&#xff0c;他的“残酷”人生第一步&#xff0c;就这样准备开始…

IdentityServer4之Authorization Code(授权码)相对更安全

前言接着授权模式聊&#xff0c;这次说说Authorization Code(授权码)模式&#xff0c;熟悉的微博接入、微信接入、QQ接入都是这种方式(这里说的是oauth2.0的授权码模式)&#xff0c;从用户体验上来看&#xff0c;交互方式和Implicit没啥改变&#xff0c;随便找个网站瞅瞅&#…