当前位置: 首页 > 编程日记 > 正文

Asp.net MVC 3 防止 Cross-Site Request Forgery (CSRF)原理及扩展 安全 注入

原理:http://blog.csdn.net/cpytiger/article/details/8781457

原文地址:http://www.cnblogs.com/wintersun/archive/2011/12/09/2282675.html

Cross-Site Request Forgery (CSRF) 是我们Web站点中常见的安全隐患。 下面我们在Asp.net MVC3 来演示一下。 例如我们有一个HomeContoller中一个Submit Action,我们标记了Http Post

[HttpPost]
public ActionResult Submit(FormCollection fc)
{
    if (!string.IsNullOrEmpty(fc["Title"]))
    {
        ViewBag.Message = "Submit success!";
        return View("Index");
    }
    return View("Error");
}

在View 使用Razor 简单提交是这样:

@using (Html.BeginForm("Submit", "Home"))
  {
      @Html.TextBox("Title","text");               
      <input type="submit" value="Submit" id="sb1" />
  }

点击这个Button我们就提交表单了,接下来我们轻易使用Fiddler来伪造这个Http Post请求:

image 

然后提交,成功了,返回 OK.

POST http://localhost:55181/Home/Submit  HTTP/1.1 
User-Agent: Fiddler 
Host: localhost:55181 
Content-Length: 10

Title=text


那在Asp.net MVC 3 Web Application中如何防止呢?在View中使用

@Html.AntiForgeryToken()

这时当Web应用程序运行时,查看生成HTML,你会看到form标签后有一个hidden input标签

<form action="/Home/Submit2" method="post">
<input name="__RequestVerificationToken" type="hidden"
 value="WiB+H5TNp6V27ALYB3z/1nkD9BLaZIBbWQOBEllj2R/+MkGZqOjLbIof2MJeEoyUJV2ljujNR4etYV6idzji
G4+JL77P9qmeewc4Erh8LnMBHX6zLas2L67GDhvCom0dpiDZl0cH+PykIC/R+HYzEIUTK/thXuF8OUtLwIfKdly0650U
3I7MD6/cIc5aersJBMZ/p6gv76gc6nvKJDt2w0eMy3tkEfAcnNPTdeWr59Ns+48gsGpZ2GSh6G+Uh7rb" />
<input id="Title" name="Title" type="text" value="text" />        
<br /> <input type="submit" value="Submit" id="sb1" />

看源代码是GetHtml方法序列化相应值生成的,

public HtmlString GetHtml(HttpContextBase httpContext, string salt, string domain, string path)
{
    Debug.Assert(httpContext != null);
 
    string formValue = GetAntiForgeryTokenAndSetCookie(httpContext, salt, domain, path);
    string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
 
    TagBuilder builder = new TagBuilder("input");
    builder.Attributes["type"] = "hidden";
    builder.Attributes["name"] = fieldName;
    builder.Attributes["value"] = formValue;
    return new HtmlString(builder.ToString(TagRenderMode.SelfClosing));
}

同时还写Cookies

__RequestVerificationToken_Lw__=T37bfAdCkz0o1iXbAvH4v0bdpGQxfZP2PI5aTJgLL/Yhr3128FUY+fvUPApBqz7CGd2uxPiW+lsZ5tvRbeLSetARbHGxPRqiw4LZiPpWrpU9XY8NO4aZzNAdMe+l3q5EMw2iIFB/6UfriWxD7X7n/8P43LJ4tkGgv6BbrGWmKFo= 

更多细节,请查询源代码。然后在Action上增加 [ValidateAntiForgeryToken] 就可以了,它是这样工作的: 

   1: public void Validate(HttpContextBase context, string salt) {
   2:     Debug.Assert(context != null);
   3:  
   4:     string fieldName = AntiForgeryData.GetAntiForgeryTokenName(null);
   5:     string cookieName = AntiForgeryData.GetAntiForgeryTokenName(context.Request.ApplicationPath);
   6:  
   7:     HttpCookie cookie = context.Request.Cookies[cookieName];
   8:     if (cookie == null || String.IsNullOrEmpty(cookie.Value)) {
   9:         // error: cookie token is missing
  10:         throw CreateValidationException();
  11:     }
  12:     AntiForgeryData cookieToken = Serializer.Deserialize(cookie.Value);
  13:  
  14:     string formValue = context.Request.Form[fieldName];
  15:     if (String.IsNullOrEmpty(formValue)) {
  16:         // error: form token is missing
  17:         throw CreateValidationException();
  18:     }
  19:     AntiForgeryData formToken = Serializer.Deserialize(formValue);
  20:  
  21:     if (!String.Equals(cookieToken.Value, formToken.Value, StringComparison.Ordinal)) {
  22:         // error: form token does not match cookie token
  23:         throw CreateValidationException();
  24:     }
  25:  
  26:     string currentUsername = AntiForgeryData.GetUsername(context.User);
  27:     if (!String.Equals(formToken.Username, currentUsername, StringComparison.OrdinalIgnoreCase)) {
  28:         // error: form token is not valid for this user
  29:         // (don't care about cookie token)
  30:         throw CreateValidationException();
  31:     }
  32:  
  33:     if (!String.Equals(salt ?? String.Empty, formToken.Salt, StringComparison.Ordinal)) {
  34:         // error: custom validation failed
  35:         throw CreateValidationException();
  36:     }
  37: }

从Cookie中获得之前序列化存入的Token,然后反序列化与表单提交的Token进行对比。 接着,又对当前请求的用户认证进行确认。 最后看有没有设置Salt,有的话再进行比较。其中有一步验证没有通过,则throw异常。 
有时的需求是这样的,我们需要使用Session验证用户,那么我们可在上面方法修改增加下面的代码块,意图是对比之前Session值是否与当前认证后Session值相等: 
//verify session 
if (!String.Equals(formToken.SessionId, AntiForgeryData.GetGUIDString(), StringComparison.Ordinal))
{
    throw CreateValidationException();
}

在修改AntiForgeryDataSerializer类,它负责序列化,这里我们增加了SessionId属性: 
   1: internal class AntiForgeryDataSerializer
   2:   {
   3:       [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "MemoryStream is resilient to double-Dispose")]
   4:       public virtual AntiForgeryData Deserialize(string serializedToken)
   5:       {
   6:           if (String.IsNullOrEmpty(serializedToken))
   7:           {
   8:               throw new ArgumentException("Argument_Cannot_Be_Null_Or_Empty", "serializedToken");
   9:           }
  10:  
  11:           try
  12:           {
  13:               using (MemoryStream stream = new MemoryStream(Decoder(serializedToken)))
  14:               using (BinaryReader reader = new BinaryReader(stream))
  15:               {
  16:                   return new AntiForgeryData
  17:                   {
  18:                       Salt = reader.ReadString(),
  19:                       Value = reader.ReadString(),
  20:                       CreationDate = new DateTime(reader.ReadInt64()),
  21:                       Username = reader.ReadString(),
  22:                       SessionId=reader.ReadString()
  23:                   };
  24:               }
  25:           }
  26:           catch (Exception ex)
  27:           {
  28:               throw new System.Web.Mvc.HttpAntiForgeryException("AntiForgeryToken_ValidationFailed", ex);
  29:           }
  30:       }
  31:  
  32:       [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "MemoryStream is resilient to double-Dispose")]
  33:       public virtual string Serialize(AntiForgeryData token)
  34:       {
  35:           if (token == null)
  36:           {
  37:               throw new ArgumentNullException("token");
  38:           }
  39:  
  40:           using (MemoryStream stream = new MemoryStream())
  41:           using (BinaryWriter writer = new BinaryWriter(stream))
  42:           {
  43:               writer.Write(token.Salt);
  44:               writer.Write(token.Value);
  45:               writer.Write(token.CreationDate.Ticks);
  46:               writer.Write(token.Username);
  47:               writer.Write(token.SessionId);
  48:  
  49:               return Encoder(stream.ToArray());
  50:           }
  51:       }
  52: }

在View这样使用,并引入Salt,这使得我们安全机制又提升了一点儿。

@using (Html.BeginForm("Submit2", "Home"))
  {
      @Html.AntiForgeryToken(DebugMvc.Controllers.Config.SALT);                          
      @Html.TextBox("Title","text");                  
      <br />
      <input type="submit" value="Submit" id="sb1" />
        
  }

Action的特性上,我们也配置对应的Salt字符串:

[HttpPost]
[ValidateAntiForgeryToken(Salt = Config.SALT)]
public ActionResult Submit2(FormCollection fc)
{
    if (!string.IsNullOrEmpty(fc["Title"]))
    {
        ViewBag.Message = "Submit success!";
        return View("Index");
    }
    return View("Error");
}
配置类:
public class Config
{
    public const string SALT = "Why you are here";
}

这个实现一个简单的Session在HttpModule中,

public class MySessionModule:IHttpModule
{
    #region IHttpModule Members
 
    public void Dispose(){}
 
    public void Init(HttpApplication context)
    {
        context.AcquireRequestState += new EventHandler(this.AcquireRequestState);
    }
 
    #endregion
 
    protected void AcquireRequestState(object sender, EventArgs e)
    {
        HttpApplication httpApp = (HttpApplication)sender;
        if (httpApp.Context.CurrentHandler is IRequiresSessionState)
        {
            if (httpApp.Session.IsNewSession)
            {
                httpApp.Session["GUID"] = Guid.NewGuid();
            }
 
        }
    }
}

这时我们再使用Fiddler模拟请求POST到这个Action,后得到下面的结果,这个异常信息也是可以修改的:

AntiForgeryToken_ValidationFailed

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
Exception Details: System.Web.Mvc.HttpAntiForgeryException: AntiForgeryToken_ValidationFailed


最后让我们来看单元测试的代码:

   1: namespace DebugMvc.Ut
   2: {
   3:     using System;
   4:     using System.Collections.Generic;
   5:     using System.Linq;
   6:     using System.Web;
   7:     using Microsoft.VisualStudio.TestTools.UnitTesting;
   8:     using DebugMvc.Controllers;
   9:     using System.Web.Mvc;
  10:     using Moq;
  11:     using System.Collections.Specialized;
  12:     using Match = System.Text.RegularExpressions.Match;
  13:     using System.Text.RegularExpressions;
  14:     using System.Globalization;
  15:  
  16:     [TestClass]
  17:     public class UnitTestForAll
  18:     {
  19:         private static string _antiForgeryTokenCookieName = AntiForgeryData.GetAntiForgeryTokenName("/SomeAppPath");
  20:         private const string _serializedValuePrefix = @"<input name=""__RequestVerificationToken"" type=""hidden"" value=""Creation: ";
  21:         private const string _someValueSuffix = @", Value: some value, Salt: some other salt, Username: username"" />";
  22:         private readonly Regex _randomFormValueSuffixRegex = new Regex(@", Value: (?<value>[A-Za-z0-9/\+=]{24}), Salt: some other salt, Username: username"" />$");
  23:         private readonly Regex _randomCookieValueSuffixRegex = new Regex(@", Value: (?<value>[A-Za-z0-9/\+=]{24}), Salt: ");
  24:  
  25:         [TestMethod]
  26:         public void TestValidateAntiForgeryToken2Attribute()
  27:         {
  28:             //arrange
  29:             var mockHttpContext = new Mock<HttpContextBase>();
  30:  
  31:             var context = mockHttpContext.Object;
  32:             var authorizationContextMock = new Mock<AuthorizationContext>();
  33:             authorizationContextMock.SetupGet(ac => ac.HttpContext).Returns(context);
  34:  
  35:             bool validateCalled = false;
  36:             Action<HttpContextBase, string> validateMethod = (c, s) =>
  37:             {
  38:                 Assert.AreSame(context, c);
  39:                 Assert.AreEqual("some salt", s);
  40:                 validateCalled = true;
  41:             };
  42:             var attribute = new ValidateAntiForgeryToken2Attribute(validateMethod)
  43:             {
  44:                 Salt = "some salt"
  45:             };
  46:  
  47:             // Act
  48:             attribute.OnAuthorization(authorizationContextMock.Object);
  49:  
  50:             // Assert
  51:             Assert.IsTrue(validateCalled);
  52:         }
  53:  
  54:         [TestMethod]
  55:         public void GetHtml_ReturnsFormFieldAndSetsCookieValueIfDoesNotExist()
  56:         {
  57:             // Arrange
  58:             AntiForgeryWorker worker = new AntiForgeryWorker()
  59:             {
  60:                 Serializer = new DummyAntiForgeryTokenSerializer()
  61:             };
  62:             var context = CreateContext();
  63:  
  64:             // Act
  65:             string formValue = worker.GetHtml(context, "some other salt", null, null).ToHtmlString();
  66:  
  67:             // Assert
  68:             Assert.IsTrue(formValue.StartsWith(_serializedValuePrefix), "Form value prefix did not match.");
  69:  
  70:             Match formMatch = _randomFormValueSuffixRegex.Match(formValue);
  71:             string formTokenValue = formMatch.Groups["value"].Value;
  72:  
  73:             HttpCookie cookie = context.Response.Cookies[_antiForgeryTokenCookieName];
  74:             Assert.IsNotNull(cookie, "Cookie was not set correctly.");
  75:             Assert.IsTrue(cookie.HttpOnly, "Cookie should have HTTP-only flag set.");
  76:             Assert.IsTrue(String.IsNullOrEmpty(cookie.Domain), "Domain should not have been set.");
  77:             Assert.AreEqual("/", cookie.Path, "Path should have remained at '/' by default.");
  78:  
  79:             Match cookieMatch = _randomCookieValueSuffixRegex.Match(cookie.Value);
  80:             string cookieTokenValue = cookieMatch.Groups["value"].Value;
  81:  
  82:             Assert.AreEqual(formTokenValue, cookieTokenValue, "Form and cookie token values did not match.");
  83:         }
  84:  
  85:         private static HttpContextBase CreateContext(string cookieValue = null, string formValue = null, string username = "username")
  86:         {
  87:             HttpCookieCollection requestCookies = new HttpCookieCollection();
  88:             if (!String.IsNullOrEmpty(cookieValue))
  89:             {
  90:                 requestCookies.Set(new HttpCookie(_antiForgeryTokenCookieName, cookieValue));
  91:             }
  92:             NameValueCollection formCollection = new NameValueCollection();
  93:             if (!String.IsNullOrEmpty(formValue))
  94:             {
  95:                 formCollection.Set(AntiForgeryData.GetAntiForgeryTokenName(null), formValue);
  96:             }
  97:  
  98:             Mock<HttpContextBase> mockContext = new Mock<HttpContextBase>();
  99:             mockContext.Setup(c => c.Request.ApplicationPath).Returns("/SomeAppPath");
 100:             mockContext.Setup(c => c.Request.Cookies).Returns(requestCookies);
 101:             mockContext.Setup(c => c.Request.Form).Returns(formCollection);
 102:             mockContext.Setup(c => c.Response.Cookies).Returns(new HttpCookieCollection());
 103:             mockContext.Setup(c => c.User.Identity.IsAuthenticated).Returns(true);
 104:             mockContext.Setup(c => c.User.Identity.Name).Returns(username);
 105:  
 106:             var sessionmock = new Mock<HttpSessionStateBase>();
 107:             sessionmock.Setup(s => s["GUID"]).Returns(Guid.NewGuid().ToString());
 108:  
 109:             mockContext.Setup(c => c.Session).Returns(sessionmock.Object);
 110:  
 111:             return mockContext.Object;
 112:         }
 113:     }
 114:  
 115:     internal class DummyAntiForgeryTokenSerializer : AntiForgeryDataSerializer
 116:     {
 117:         public override string Serialize(AntiForgeryData token)
 118:         {
 119:             return String.Format(CultureInfo.InvariantCulture, "Creation: {0}, Value: {1}, Salt: {2}, Username: {3}",
 120:                     token.CreationDate, token.Value, token.Salt, token.Username);
 121:         }
 122:         public override AntiForgeryData Deserialize(string serializedToken)
 123:         {
 124:             if (serializedToken == "invalid")
 125:             {
 126:                 throw new HttpAntiForgeryException();
 127:             }
 128:             string[] parts = serializedToken.Split(':');
 129:             return new AntiForgeryData()
 130:             {
 131:                 CreationDate = DateTime.Parse(parts[0], CultureInfo.InvariantCulture),
 132:                 Value = parts[1],
 133:                 Salt = parts[2],
 134:                 Username = parts[3]
 135:             };
 136:         }
 137:     }
 138: }

这里只是UnitTest的一部分,使用Moq来实现Mock HttpContext,从而实现对HttpContext的单元测试。 

小结: Web站点的安全问题,不可轻视。特别现在Ajax大量应用,做好安全检测很重要。

希望对您Web开发有帮助。

转载于:https://www.cnblogs.com/niaowo/p/3782578.html

相关文章:

Windows下安装Redis服务

2019独角兽企业重金招聘Python工程师标准>>> Redis是有名的NoSql数据库&#xff0c;一般Linux都会默认支持。但在Windows环境中&#xff0c;可能需要手动安装设置才能有效使用。这里就简单介绍一下Windows下Redis服务的安装方法&#xff0c;希望能够帮到你。 1、要安…

(C++)1022 D进制的A+B 除基取余法将10进制数换成给定进制数

#include<cstdio> //除基取余法 const int M 30;int main(){long long a,b,c;int D,ans[M2];scanf("%lld%lld%d",&a,&b,&D);cab;int num0;do{ans[num]c%D;c c/D;}while(c!0);for(int inum-1;i>0;i--){printf("%d",ans[i]);}return…

Linux zip-tar.gz 压缩解压

tar -zcvf /home/xahot.tar.gz /xahot tar -zcvf 打包后生成的文件名全路径 要打包的目录 例子&#xff1a;把/xahot文件夹打包后生成一个/home/xahot.tar.gz的文件。zip 压缩方法&#xff1a; 压缩当前的文件夹 zip -r ./xahot.zip ./* -r表示递归zip [参数] [打包后的文件名]…

Spring(ApplicationContextBeanFactory)

BeanFactory 才是 Spring 容器中的顶层接口。ApplicationContext 是它的子接口。 BeanFactory 和 ApplicationContext 的区别&#xff1a;创建对象的时间点不一样。 BeanFactory接口 spring的原始接口,针对原始接口的实现类功能较为单一.BeanFactory接口实现类的容器,特点是每次…

spoolsv.exe占CPU100% 的解决方法

spoolsv.exe占CPU100% 的解决方法spoolsv.exe是打印缓冲&#xff0c;没有打印机就关掉&#xff0c;有的话在打印时会占用很大内存。因为要打印的文件要转化格式。禁止Print spooler服务&#xff0c;如果用打印机&#xff0c;此方法不可行。  解决办法如下&#xff1a;1、在服…

(C++)1037 在霍格沃茨找零钱的两种解法

解法一 #include<cstdio> //十七个银西可(Sickle)兑一个加隆(Galleon)&#xff0c;二十九个纳特(Knut)兑一个西可 //1 G 17 S , 1 S 29 Kint main(){int g1,s1,k1,g2,s2,k2,g3,s3,k3;scanf("%d.%d.%d",&g2,&s2,&k2);//定价 scanf("%d.%d.%…

开发人员角色分析

开发人员角色分析&#xff1a; 开发人员Developers 主要包含以下角色&#xff1a; n 软件架构师Software Architect n 设计员Designer n 用户界面设计员User-Interface Designer n 数据库设计员 Database Designer n 实施员Implementer n …

php empty()和isset()的区别

在使用 php 编写页面程序时&#xff0c;我经常使用变量处理函数判断 php 页面尾部参数的某个变量值是否为空&#xff0c;开始的时候我习惯了使用 empty() 函数&#xff0c;却发现了一些问题&#xff0c;因此改用 isset() 函数&#xff0c;问题不再。 顾名思义&#xff0c;empty…

jmap 内存情况

2019独角兽企业重金招聘Python工程师标准>>> -dump dump堆到文件,format指定输出格式&#xff0c;live指明是活着的对象,file指定文件名 [rootlocalhost ~]# jmap -dump:live,formatb,filedump.hprof 187784 Dumping heap to /home/yxgly/dump.hprof ... Heap dump …

(C++)1021 个位数统计

#include<cstdio> #include<cstring>const int M 1000;int main(){char str[M1];int count[10]{0};//全部初始化为0 scanf("%s",str);int len strlen(str);int i;for(i0;i<len;i){count[str[i]-0];}for(i0;i<10;i){if(count[i]>0){printf(&qu…

TOJ--3456--数学题

这题 做出来真的好爽啊... it is cool although it is easy 虽然 已经是大概1 2点的事了 我拖到现在才写是因为------lol 终于赢一把了 --- 先贴下题目&#xff1a; touch me 嗯 我一开始 用的是 3重for 我以为32767的数据量 是很小的.... 结果 TLE。。 OK 那么 我们只能换…

firewalled centos7

zone绑定网卡 firewall-cmd --zoneinternal --add-interfaceens192 --permanent firewall-cmd --permanent --zoneinternal --add-rich-rule"rule family"ipv4" source address"192.168.10.0/24" accept" [rootbyos000 system]# firewall-cmd -…

为Delphi程序添加事件和事件处理器

在Delphi中&#xff0c;事件实际上是专门化的属性&#xff0c;它是一个过程&#xff08;procedure&#xff09;的指针。要添加事件&#xff0c;首先应在所定义的类中说明一个用来指向事件过 程的指针&#xff0c;该指针的作用是当事件一旦发生&#xff0c;就通过这个指针执行所…

(C++)1031 查验身份证 3难点+3注意点

#include<cstdio> #include<cstring> //难点1&#xff1a;检查前17位是否全为数字 //解决之道1&#xff1a;本来就不是整型数字&#xff0c;是字符数字&#xff0c;判断是否在0和9之间即可 //难点2&#xff1a;遇到一个X后&#xff0c;如果不想处理sum了该怎么办 /…

Perl时间处理函数

官方网址&#xff1a;http://search.cpan.org/~stbey/Date-Calc-6.3/lib/Date/Calc.pod#___top use Date::Calc qw(Days_in_YearDays_in_MonthWeeks_in_Yearleap_yearcheck_datecheck_timecheck_business_dateDay_of_YearDate_to_DaysDay_of_WeekWeek_NumberWeek_of_YearMonda…

Linux环境搭建 | 手把手教你安装Linux虚拟机

2019独角兽企业重金招聘Python工程师标准>>> 前言 作为一名Linux工程师&#xff0c;不管是运维、应用、驱动方向&#xff0c;在工作中肯定会需要Linux环境。想要获得Linux环境&#xff0c;一个办法就是将电脑系统直接换成Linux系统&#xff0c;但我们平常用惯了Wind…

企业的覆灭,我监视你的Exchange邮件!

现在很多企业都搭建ExchangeServer平台&#xff0c;一个用户包括Domain admins都是不允许查阅其他用户的邮件信息的&#xff01;殊不知作为Domain Admins权限用户可以经过精心的设置&#xff0c;可以达到浏览到其他用户邮件信息&#xff01; 转载于:https://www.cnblogs.com/al…

(C++)1002 写出这个数

#include<cstdio> #include<cstring>const int M 100; //用字符数组装输入 //定义变量&#xff0c;输出字符数组的长度 //对字符数组遍历求和 //对结果逐位输出汉语拼音 void hanzi(int i){switch(i){case 0:printf("ling");break;case 1:printf("…

IO复用之epoll系列

epoll是什么&#xff1f; epoll是Linux内核为处理大批量文件描述符而作了改进的poll&#xff0c;是Linux下多路复用IO接口select/poll的增强版本&#xff0c;它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候&#xff0c;它…

MVP Summit 2008 照片纪实(二)- 旧金山,Google总部和Stanford大学

坐在洛杉矶机场里&#xff0c;终于为这次MVP峰会的美国之行画上了句号。从旧金山到拉斯维加斯&#xff0c;从拉斯维加斯到大峡谷&#xff0c;最后从大峡谷返回洛杉矶&#xff0c;3天之中总共驾驶历程超过1600英里&#xff08;据说可以赶上出租车司机了&#xff09;。3天之中经历…

(C++)1025 PAT Ranking

#include<cstdio> #include<algorithm> #include<cstring>using namespace std;const int M 100*300;struct testee{//考生 char reg_num[14];//准考证号 int score;//分数 int final_rank;//最终排名 int loc_no;//考场号 int local_rank;//考场内排名 }te…

模态视图(转)

转载请注明出处&#xff0c;原文网址&#xff1a;http://blog.csdn.net/m_changgong/article/details/8127894 作者&#xff1a;张燕广 模态视图不是专门的某个类&#xff0c;而是通过视图控制器的presentViewController方法弹出的视图&#xff0c;我们称为模态视图。 模态视图…

MHA二种高可用架构切换演练

高可用架构一 proxysqlkeepalivedmysqlmha优势&#xff0c;最大程序的降低脑裂风险&#xff0c;可以读写分离&#xff08;需要开启相应的插件支持&#xff09; 一、proxysql 1、安装 tar -zxvf proxysql.tar.gz -C /usr/local/chmod -R 700 /usr/local/proxysqlcd /usr/local/p…

如何关闭事件跟踪程序

最近经常遇到一些独享服务器用户反应自己的服务器联系万网工程师重起后&#xff0c;重新登陆时遇到的界面不知道该如何操作问题。当您看到此界面时&#xff0c;只需要在“注释”下面的空白处随意输入字符即可激活“确定”按钮&#xff0c;点击“确定”后可以进入系统。 这个界…

(C++)1015 德才论

#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int M 100000;struct Testee{char no[10];int de;int cai;int type;//第几类 }peo[M10];bool cmp(Testee a,Testee b){//比较顺序依次为总分&#xff0c;德分&#xf…

Vim命令相关

在shell中&#xff0c;记住一些常用的vim命令&#xff0c;会在操作时候事半功倍。 光标移动 h,j,k,l,h #表示往左&#xff0c;j表示往下&#xff0c;k表示往右&#xff0c;l表示往上 Ctrl f #上一页 Ctrl b #下一页 w, e, W, E #跳到单词的后面&#xff0c;小…

做科研的几点体会

刚刚开始做实验的时候&#xff0c;别人怎么说我就怎么做&#xff0c;每天在实验台旁干到深夜&#xff0c;以为这就是科研了。两个月过去&#xff0c;突然发现自己还在原地踏步。那种感觉&#xff0c;只能用”沮丧”来形 容。我开始置疑自己的行为和观念。感觉有种习惯的力量在束…

ICMP报文分析

一.概述&#xff1a;1. ICMP同意主机或路由报告差错情况和提供有关异常情况。ICMP是因特网的标准协议&#xff0c;但ICMP不是高层协议&#xff0c;而是IP层的协议。通常ICMP报文被IP层或更高层协议&#xff08;TCP或UDP&#xff09;使用。一些ICMP报文把差错报文返回给用户进…

(C++)1029 旧键盘

#include<cstdio> #include<cstring>const int M 80;//值得注意的地方是“按照发现顺序 ” //采取的最佳策略是&#xff0c;对于字符串1中的每一个字符&#xff0c;看在字符串2中是否出现int hashmap(char c){int res 0;if(0<c&&c<9){res c-0;}e…

深入理解 python 元类

一、什么的元类 # 思考&#xff1a; # Python 中对象是由实例化类得来的&#xff0c;那么类又是怎么得到的呢&#xff1f; # 疑问&#xff1a; # python 中一切皆对象&#xff0c;那么类是否也是对象&#xff1f;如果是&#xff0c;那么它又是那个类实例化而来的呢&…