基于.Net Framework 4.0 Web API開發(4):ASP.NET Web APIs 基于令牌TOKEN驗證的實現

概述:?

  ASP.NET Web API 的好用使用過的都知道,沒有復雜的配置文件,一個簡單的ApiController加上需要的Action就能工作。但是在使用API的時候總會遇到跨域請求的問題, 特別各種APP萬花齊放的今天,對API使用者身份角色驗證是不能避免的(完全開發的API不需要對使用者身份角色進行管控,可以繞過),這篇文章就來談談基于令牌TOKEN身份驗證的實現。

問題:

   對于Web API的選擇性的開放,使用者無論使用AJAX,還是HttpClient對接,總要對使用者的身份角色進行驗證,然而使用API總有跨域使用情況的存在,這樣就導致所有基于cookie驗證方式都不再適用于API的驗證。

原因:

  比如,基于form表單驗證的基礎是登錄驗證成功后,用戶的信息存在緩存或數據庫或cookie,無論哪種方式存儲用戶信息,都不能繞過對cookie的使用,所以form表單驗證方法對于禁用cookie的瀏覽器都不能正常使用,結論就是不能使用cookie 的環境就不能使用基本的form表單驗證方式。因此WEB API 由于跨域的使用,導致cookie不能正常工作,所以不能再使用基于表單驗證的方式來實現。

基于令牌TOKEN驗證方法的實現:

方法一:

???? 1. 實現對緩存TOKEN的管理,以防IIS服務器的宕機,可以對TOKEN進行持久化存儲處理,每次IIS重啟重新初始化已經登錄成功TOKEN緩存。實現如下:

復制代碼
  1 public class UserTokenManager2     {3         private static readonly IUserTokenRepository _tokenRep;4         private const string TOKENNAME = "PASSPORT.TOKEN";5 6         static UserTokenManager()7         {8             _tokenRep = ContainerManager.Resolve<IUserTokenRepository>();9         }10         /// <summary>11         /// 初始化緩存12         /// </summary>13         private static List<UserToken> InitCache()14         {15             if (HttpRuntime.Cache[TOKENNAME] == null)16             {17                 var tokens = _tokenRep.GetAll();18                 // cache 的過期時間, 令牌過期時間 *219                 HttpRuntime.Cache.Insert(TOKENNAME, tokens, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromDays(7 * 2));20             }21             var ts = (List<UserToken>)HttpRuntime.Cache[TOKENNAME];22             return ts;23         }24 25 26         public static int GetUId(string token)27         {28             var tokens = InitCache();29             var result = 0;30             if (tokens.Count > 0)31             {32                 var id = tokens.Where(c => c.Token == token).Select(c => c.UId).FirstOrDefault();33                 if (id != null)34                     result = id.Value;35             }36             return result;37         }38 39 40         public static string GetPermission(string token)41         {42             var tokens = InitCache();43             if (tokens.Count == 0)44                 return "NoAuthorize";45             else46                 return tokens.Where(c => c.Token == token).Select(c => c.Permission).FirstOrDefault();47         }48 49         public static string GetUserType(string token)50         {51             var tokens = InitCache();52             if (tokens.Count == 0)53                 return "";54             else55                 return tokens.Where(c => c.Token == token).Select(c => c.UserType).FirstOrDefault();56         }57 58         /// <summary>59         /// 判斷令牌是否存在60         /// </summary>61         /// <param name="token"></param>62         /// <returns></returns>63         public static bool IsExistToken(string token)64         {65             var tokens = InitCache();66             if (tokens.Count == 0) return false;67             else68             {69                 var t = tokens.Where(c => c.Token == token).FirstOrDefault();70                 if (t == null)71                     return false;72                 else if (t.Timeout < DateTime.Now)73                 {74                     RemoveToken(t);75                     return false;76                 }77                 else78                 {79                     // 小于8小時 更新過期時間80                     if ((t.Timeout - DateTime.Now).TotalMinutes < 1 * 60 - 1)81                     {82                         t.Timeout = DateTime.Now.AddHours(8);83                         UpdateToken(t);84                     }85                     return true;86                 }87 88             }89         }90 91         /// <summary>92         /// 添加令牌, 沒有則添加,有則更新93         /// </summary>94         /// <param name="token"></param>95         public static void AddToken(UserToken token)96         {97             var tokens = InitCache();98             // 不存在  怎增加99             if (!IsExistToken(token.Token))
100             {
101                 token.ID = 0;
102                 tokens.Add(token);
103                 // 插入數據庫
104                 _tokenRep.Add(token);
105             }
106             else  // 有則更新
107             {
108                 UpdateToken(token);
109             }
110         }
111 
112         public static bool UpdateToken(UserToken token)
113         {
114             var tokens = InitCache();
115             if (tokens.Count == 0) return false;
116             else
117             {
118                 var t = tokens.Where(c => c.Token == token.Token).FirstOrDefault();
119                 if (t == null)
120                     return false;
121                 t.Timeout = token.Timeout;
122                 // 更新數據庫
123                 var tt = _tokenRep.FindByToken(token.Token);
124                 if (tt != null)
125                 {
126                     tt.UserType = token.UserType;
127                     tt.UId = token.UId;
128                     tt.Permission = token.Permission;
129                     tt.Timeout = token.Timeout;
130                     _tokenRep.Update(tt);
131                 }
132                 return true;
133             }
134         }
135         /// <summary>
136         /// 移除指定令牌
137         /// </summary>
138         /// <param name="token"></param>
139         /// <returns></returns>
140         public static void RemoveToken(UserToken token)
141         {
142             var tokens = InitCache();
143             if (tokens.Count == 0) return;
144             tokens.Remove(token);
145             _tokenRep.Remove(token);
146         }
147 
148         public static void RemoveToken(string token)
149         {
150             var tokens = InitCache();
151             if (tokens.Count == 0) return;
152 
153             var ts = tokens.Where(c => c.Token == token).ToList();
154             foreach (var t in ts)
155             {
156                 tokens.Remove(t);
157                 var tt = _tokenRep.FindByToken(t.Token);
158                 if (tt != null)
159                     _tokenRep.Remove(tt);
160             }
161         }
162 
163 
164         public static void RemoveToken(int uid)
165         {
166             var tokens = InitCache();
167             if (tokens.Count == 0) return;
168 
169             var ts = tokens.Where(c => c.UId == uid).ToList();
170             foreach (var t in ts)
171             {
172                 tokens.Remove(t);
173                 var tt = _tokenRep.FindByToken(t.Token);
174                 if (tt != null)
175                     _tokenRep.Remove(tt);
176             }
177         }
178     }
復制代碼

???? 2. 新建ApiAuthorizeAttribute類,繼承AuthorizeAttribute,重寫方法IsAuthorized,這樣基于TOKEN驗證方式就完成了。實現如下:

復制代碼
 1   public class ApiAuthorizeAttribute : AuthorizeAttribute2     {3         protected override bool IsAuthorized(HttpActionContext actionContext)4         {5             // 驗證token6             //var token = actionContext.Request.Headers.Authorization;7             var ts = actionContext.Request.Headers.Where(c => c.Key.ToLower() == "token").FirstOrDefault().Value;8             if (ts != null && ts.Count() > 0)9             {
10                 var token = ts.First<string>();
11                 // 驗證token
12                 if (!UserTokenManager.IsExistToken(token))
13                 {
14                     return false;
15                 }
16                 return true;
17             }
18 
19             if (actionContext.Request.Method == HttpMethod.Options)
20                 return true;
21             return false;
22         }
23     }
復制代碼

???? 3. 登錄實現

復制代碼
  1     /// <summary>2     /// 賬戶3     /// </summary>4     public class AccountController : ApiController5     {6         /// <summary>7         /// 登錄8         /// </summary>9         /// <param name="user">登錄人員信息: 賬號,密碼 ,是否記住密碼</param>10         /// <returns></returns>11         [HttpPost]12         [AllowAnonymous]13         public ResultData Login([FromBody]LoginUser user)14         {15             string mobile = user.Mobile;16             string password = user.Password;17             bool IsRememberMe = user.IsRememberMe;18 19             if (string.IsNullOrEmpty(mobile) || string.IsNullOrEmpty(password))20                 return new ResultData(((int)LoginResultEnum.UserNameOrPasswordError), EnumExtension.GetEnumDescription(LoginResultEnum.UserNameOrPasswordError));21 22             User u=null;23             IMembershipService membershipSvc = ContainerManager.Container.Resolve<IMembershipService>();24             LoginResultEnum loginResult = membershipSvc.Login(mobile, password, out u);25             if (loginResult == LoginResultEnum.Success)26             {27                 //SetAuthenticationTicket(u, IsRememberMe);28 29                 // token   處理30                 UserTokenManager.RemoveToken(u.ID);31                 // 生成新Token32                 var token = Utility.MD5Encrypt(string.Format("{0}{1}", Guid.NewGuid().ToString("D"), DateTime.Now.Ticks));33                 // token過期時間34                 int timeout = 8;35                 if (!int.TryParse(ConfigurationManager.AppSettings["TokenTimeout"], out timeout))36                     timeout = 8;37                 // 創建新token38                 var ut = new UserToken()39                 {40                     Token = token,41                     Timeout = DateTime.Now.AddHours(timeout),42                     UId = u.ID,43                     UserType = (u.IsSaler.HasValue && u.IsSaler.Value) ? "Saler" : "Vip"44                 };45 46                 UserTokenManager.AddToken(ut);47 48 49                 // 登錄log50                 var logRep = ContainerManager.Container.Resolve<ISysLogRepository>();51                 var log = new Log()52                 {53                     Action = "Login",54                     Detail = "會員登錄:" + u.Mobile + "|" + u.Name,55                     CreateDate = DateTime.Now,56                     CreatorLoginName = u.Mobile,57                     IpAddress = GetClientIp(this.Request)58                 };59 60                 logRep.Add(log);61 62                 var data = new63                 {64                     id = u.ID,65                     issaler = u.IsSaler.HasValue ? u.IsSaler.Value : false,66                     mobile = u.Mobile,67                     token = token68                 };69                 var result = new ResultData(data);70                 result.desc = "登錄成功";71                 return result;72             }73 74             if (loginResult == LoginResultEnum.UserNameUnExists)75             {76                 return new ResultData(((int)LoginResultEnum.UserNameUnExists), EnumExtension.GetEnumDescription(LoginResultEnum.UserNameUnExists));77             }78             if (loginResult == LoginResultEnum.VerifyCodeError)79             {80                 return new ResultData(((int)LoginResultEnum.VerifyCodeError), EnumExtension.GetEnumDescription(LoginResultEnum.VerifyCodeError));81             }82             if (loginResult == LoginResultEnum.UserNameOrPasswordError)83             {84                 return new ResultData(((int)LoginResultEnum.UserNameOrPasswordError), EnumExtension.GetEnumDescription(LoginResultEnum.UserNameOrPasswordError));85             }86             return new ResultData(ResultType.UnknowError, "登錄失敗,原因未知");87         }88         /// <summary>89         /// 退出當前賬號90         /// </summary>91         /// <returns></returns>92         [HttpPost]93         public ResultData SignOut()94         {95             // 登錄log96             var logRep = ContainerManager.Resolve<ISysLogRepository>();97             var log = new Log()98             {99                 Action = "SignOut",
100                 Detail = "會員退出:" + RISContext.Current.CurrentUserInfo.UserName,
101                 CreateDate = DateTime.Now,
102                 CreatorLoginName = RISContext.Current.CurrentUserInfo.UserName,
103                 IpAddress = GetClientIp(this.Request)
104             };
105             logRep.Add(log);
106             //System.Web.Security.FormsAuthentication.SignOut();
107             UserTokenManager.RemoveToken(this.Token);
108             return new ResultData(ResultType.Success, "退出成功");
109         }
110     }
復制代碼

??? 4. 測試API

???????? 這樣就可以配合.NET原有的?AllowAnonymousAttribute?屬性使用,?使用方法如下:
???????? 不需要驗證身份的 類或者Action 添加 ?[AllowAnonymous]屬性,否則添加[ApiAuthorize]

復制代碼
 1     /// <summary>2     /// 測試3     /// </summary>4     [ApiAuthorize]5     public class TestController : BaseApiController6     {7         /// <summary>8         /// 測試權限19         /// </summary>
10         [HttpGet]
11         public string TestAuthorize1()
12         {
13             return "TestAuthorize1";
14         }
15         /// <summary>
16         /// 測試權限2
17         /// </summary>
18         [AllowAnonymous]
19         [HttpGet]
20         public string TestAuthorize2()
21         {
22             return "TestAuthorize2";
23         }
24     }
復制代碼

?

測試一:

復制代碼
 1 //TestAuthorize2 function TestAuthorize1() {3     $.ajax({4         type: "get",5         url: host + "/mobileapi/test/TestAuthorize1",6         dataType: "text",7         data: {},8         beforeSend: function (request) {9             request.setRequestHeader("token", $("#token").val());  // 請求發起前在頭部附加token
10         },
11         success: function (data) {
12             alert(data);
13         },
14         error: function (x, y, z) {
15             alert("報錯無語");
16         }
17     });
18 }
復制代碼

???? 結果如下:

?

測試二:

復制代碼
 1 //TestAuthorize2 function TestAuthorize2() {3     $.ajax({4         type: "get",5         url: host + "/mobileapi/test/TestAuthorize2",6         dataType: "text",7         data: {},8         beforeSend: function (request) {9             request.setRequestHeader("token", $("#token").val());  // 請求發起前在頭部附加token
10         },
11         success: function (data) {
12             alert(data);
13         },
14         error: function (x, y, z) {
15             alert("報錯無語");
16         }
17     });
18 }
復制代碼

?? ?結果如下:

?

測試三:

復制代碼
 1 //TestAuthorize2 function TestAuthorize1() {3     $.ajax({4         type: "get",5         url: host + "/mobileapi/test/TestAuthorize1",6         dataType: "text",7         data: {},8         beforeSend: function (request) {9             //request.setRequestHeader("token", $("#token").val());  // 請求發起前在頭部附加token
10         },
11         success: function (data) {
12             alert(data);
13         },
14         error: function (x, y, z) {
15             alert("報錯無語");
16         }
17     });
18 }
復制代碼

???? 結果如下:

?

測試四:

復制代碼
 1 //TestAuthorize2 function TestAuthorize2() {3     $.ajax({4         type: "get",5         url: host + "/mobileapi/test/TestAuthorize2",6         dataType: "text",7         data: {},8         beforeSend: function (request) {9             //request.setRequestHeader("token", $("#token").val());  // 請求發起前在頭部附加token
10         },
11         success: function (data) {
12             alert(data);
13         },
14         error: function (x, y, z) {
15             alert("報錯無語");
16         }
17     });
18 }
復制代碼

??? 結果如下:


方法二:

?? 此方法缺點就是每次請求都需要附帶token請求參數,這對于有強迫癥的程序猿來說是一種折磨,不細說,實現代碼如下,有需要的自己研究研究:

復制代碼
 1     /// <summary>2     /// 用戶令牌驗證3     /// </summary>4     public class TokenAuthorizeAttribute : ActionFilterAttribute5     {6         private const string UserToken = "token";7         public override void OnActionExecuting(HttpActionContext actionContext)8         {9             // 匿名訪問驗證
10             var anonymousAction = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>();
11             if (!anonymousAction.Any())
12             {
13                 // 驗證token
14                 var token = TokenVerification(actionContext);
15             }
16             base.OnActionExecuting(actionContext);
17         }
18 
19         /// <summary>
20         /// 身份令牌驗證
21         /// </summary>
22         /// <param name="actionContext"></param>
23         protected virtual string TokenVerification(HttpActionContext actionContext)
24         {
25             string msg = "";
26             // 獲取token
27             var token = GetToken(actionContext, out msg);
28             if (!string.IsNullOrEmpty(msg))
29                 actionContext.Response = actionContext.Request.CreateResponse<NoAuthData>(System.Net.HttpStatusCode.OK, new NoAuthData() { code = "401", msg = msg });
30             // 判斷token是否有效
31             if (!UserTokenManager.IsExistToken(token))
32             {
33                 actionContext.Response = actionContext.Request.CreateResponse<NoAuthData>(System.Net.HttpStatusCode.OK, new NoAuthData() { code = "401", msg = "Token已失效,請重新登錄!" });
34                 //actionContext.Response = actionContext.Request.CreateResponse<NoAuthData>(System.Net.HttpStatusCode.Unauthorized, new NoAuthData() { code = "401", msg = "Token已失效,請重新登錄!" });
35                 // actionContext.Response = actionContext.Request.CreateErrorResponse(System.Net.HttpStatusCode.Unauthorized, "Token已失效,請重新登錄!");
36             }
37 
38             return token;
39         }
40 
41         private string GetToken(HttpActionContext actionContext, out string msg)
42         {
43             Dictionary<string, object> actionArguments = actionContext.ActionArguments;
44             HttpMethod type = actionContext.Request.Method;
45             msg = "";
46             var token = "";
47             if (type == HttpMethod.Post)
48             {
49                 if (actionArguments.ContainsKey(UserToken))
50                 {
51                     if (actionArguments[UserToken] != null)
52                         token = actionArguments[UserToken].ToString();
53                 }
54                 else
55                 {
56                     foreach (var value in actionArguments.Values)
57                     {
58                         if (value != null && value.GetType().GetProperty(UserToken) != null)
59                             token = value.GetType().GetProperty(UserToken).GetValue(value, null).ToString();
60                     }
61                 }
62 
63                 if (string.IsNullOrEmpty(token))
64                     msg = "登錄超時,請重新登錄!";
65             }
66             else if (type == HttpMethod.Get)
67             {
68                 if (!actionArguments.ContainsKey(UserToken))
69                     msg = "還未登錄";
70                 // throw new HttpException(401, "還未登錄");
71 
72                 if (actionArguments[UserToken] != null)
73                     token = actionArguments[UserToken].ToString();
74                 else
75                     msg = "登錄超時,請重新登錄!";
76             }
77             else
78             {
79                 throw new HttpException(404, "暫未開放除POST,GET之外的訪問方式!");
80             }
81             return token;
82         }
83     }
84 
85     public class NoAuthData
86     {
87         public string code { get; set; }
88         public string msg { get; set; }
89     }
復制代碼


此篇到此結束,歡迎大家討論!

?

??

?

那些曾以為念念不忘的事情就在我們念念不忘的過程中,被我們遺忘了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/260080.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/260080.shtml
英文地址,請注明出處:http://en.pswp.cn/news/260080.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

匈牙利命名法為何被淘汰_為何甲烷的習慣命名法用甲烷而不是一烷?

其實在有機化合物中文翻譯早期&#xff0c;是有過用數字命名的階段的&#xff0c;先上圖。部分有機化合物在不同時期的漢譯名對照那么后來為何未采用數字&#xff0c;而使用天干。與商務印書館和鄭貞文個人其實有很大關系。那么有機物中文命名的演進是怎樣的呢&#xff1f;下面…

Windows下Mysql Cluster集群啟動腳本與啟動服務添加方法

3.2.8 集群啟動腳本及啟動服務注冊 從上面的啟動過程我們可以看出&#xff0c;Mysql集群啟動命令較復雜&#xff0c;容易造成cmd窗口因誤操作關閉而導致的數據庫停止&#xff0c;在此我介紹一下如何將這些命令的操作變成windos的服務項&#xff0c;當windos啟動時&#xff0…

c語言中for語句的作用是,c語言中for語句是怎么用的

匿名用戶1級2018-11-20 回答1&#xff1a;for循環for語句的格式為&#xff1a;for (初始化語句; 條件語句; 控制語句){語句1 ;語句2 ;....語句n ;}for 語句的執行順序是&#xff1a;首先執行“初始化語句”&#xff1b;然后測試“條件語句”&#xff1b;若條件成立&#xff0c;…

[Azure] Notification Hubs注冊模式

[Azure] Notification Hubs注冊模式 關于Azure Notification Hubs的注冊模式&#xff0c;可以參考下列連結的文件內容。 Notification Hubs Features - Registration Management 文件里介紹了兩種注冊模式&#xff1a;手機主動注冊、服務代理注冊&#xff0c;這兩個模式主要的差…

iOS 本地化應用程序匯總 國際化

最近要做一個應用要實現本地化&#xff0c;因為使用的是xcode4&#xff0c;應用程序本地化的問題跟以前的版本還是有些不同&#xff0c;在網上找了些資料對于xcode4以上的版本資料還是相對較少&#xff0c;有些最后要通過手動創建文件&#xff0c;這樣操作實在是太麻煩&#xf…

圖卷積神經網絡_深度層次化圖卷積神經網絡

來源&#xff1a;IJCAI 2019論文地址&#xff1a;https://arxiv.org/abs/1902.06667代碼地址&#xff1a;https://github.com/CRIPAC-DIG/H-GCNIntroduction1、問題定義&#xff1a;什么是半監督的節點分類&#xff1f;圖1 半監督節點分類如圖1所示&#xff0c;在標記數據量很少…

計算機二級怎么測試c語言,全國計算機等級測試二級C語言視頻教程(完整版).doc...

第一章 程序設計的基本概念01-01.程序設計的基本概念(1)01-02.程序設計的基本概念(2)01-03.程序設計的基本概念(3)第二章 C語言基礎知識02-01.簡單C語言的構成和格式(1)02-02.簡單C語言的構成和格式(2)02-03.標識符的組成和分類02-04.整型數據和實型數據(1)02-05.整型數據和實型…

去掉Phoca Download的Powered By

進入components/com_phocadownload/views/ 這是Phoca Download的模板文件夾 打開category/tmpl/default.php 分類頁 //echo </div><div class"pd-cb"> </div> . $this->tmpl[phoca_download];echo </div> ; 打開file/tmpl/default.php …

如何居中div

div居中 博客分類&#xff1a;筆記csshtmldiv CSS中怎么讓div居中 CSS 如何使div層水平居中今天用CSS遇到個的問題,div本身沒有定義自己居中的屬性, 網上很多的方法都是介紹用上級的text-align: center然后嵌套一層div來解決問題. 可是事實上這樣的方法科學嗎? 經過網絡搜索…

c語言小數加分,求救:c語言課程設計 員工工資管理程序 有加分的。謝謝

#define SIZE 50/* 定義結構體 */struct date{int month;int day;int year;};typedef struct employee{char name[50] ,num[10],sex,edu[10],duty[15];int age,income;struct employee *next;}empl[SIZE];/* 逐項輸出鏈表中的內容 */void view (struct employee* head){struct …

老師能提供什么幫助

老師能提供什么幫助&#xff1f; 這篇本來想上周寫的&#xff0c;原計劃在感恩節推送&#xff0c;可惜太忙&#xff0c;計劃趕不上變化。 時隔十年&#xff0c;我又要給計算機系的同學們上課了&#xff0c;心里還是有些小激動的。我下學期要開一門大課叫程序設計與數據結構,涵蓋…

(轉)SystemProcessesAndThreadsInformation

http://hi.baidu.com/hanjdud8606/item/7a970408a95acc843d42e27f NTSTATUS NTAPI ZwQuerySystemInformation(ULONG SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength,PULONG ReturnLength ); 第一個參數是一個枚舉類型&#xff0c;傳入的是你需…

12個Icon圖標資源網站

1.除了Icon以外&#xff0c;還有很多不錯的UI設計素材。 地址&#xff1a;http://worldui.com/2.除了免費Icon資源下載以外&#xff0c;還提供Icon定制的付費服務。地址&#xff1a;http://dryicons.com/3.很喜歡這個icon資源站的展示方式。地址&#xff1a;http://www.icotrip…

python列表split_Python-split()函數實例用法講解

在Python中&#xff0c;split() 方法可以實現將一個字符串按照指定的分隔符切分成多個子串&#xff0c;這些子串會被保存到列表中(不包含分隔符)&#xff0c;作為方法的返回值反饋回來。split函數用法split(sepNone, maxsplit-1)參數sep – 分隔符&#xff0c;默認為所有的空字…

go語言基礎 -- 閉包

什么是閉包 閉包的概念&#xff0c;在很多語言中都有&#xff0c;比如python&#xff0c;這里要介紹的go&#xff0c;閉包就是函數與其相關的環境組合的一個整體。 我們通過一個例子來理解閉包&#xff1a; func Addfunc() func (x int) int {var n int 0// 返回的是一個函數…

linux中解決SSH連接慢問題

2019獨角獸企業重金招聘Python工程師標準>>> 現在連接linux服務器一般都是使用SSH遠程連接的方式。最近新裝了一臺服務器&#xff0c;發現telnet時速度很快&#xff0c;ping時一切也正常&#xff0c;但SSH連接的時候卻很慢。經過網上資料查詢&#xff0c;大致是有以…

利用Theme自定義Activity進入退出動畫

有沒有覺得Activity的默認動畫太快了或者太難看了。。我原來使用Activity.overridePendingTransition來自定義Activity的進入動畫&#xff0c;卻發現沒法定義退出的動畫。結果就發現了強大的Theme和Style&#xff0c;之后還需要好好研究一下。具體是這樣子的&#xff1a;在Andr…

阿里云深圳數據中心正式開放

阿里云深圳數據中心正式開放 ? 8月29日&#xff0c;阿里云深圳數據中心正式開放運營&#xff0c;這是繼杭州、青島、北京、香港之后&#xff0c;我們在全球開放的第五個數據中心。 深圳數據中心主要輻射以深圳、廣州為中心的華南區域&#xff0c;以滿足這一區域內的商貿企業、…

vue 文件及描述信息一起上傳_用Vue實現一個大文件上傳和斷點續傳

前言這段時間面試官都挺忙的&#xff0c;頻頻出現在博客文章標題&#xff0c;雖然我不是特別想蹭熱度&#xff0c;但是實在想不到好的標題了-。-&#xff0c;蹭蹭就蹭蹭 :)事實上我在面試的時候確實被問到了這個問題&#xff0c;而且是一道在線 coding 的編程題&#xff0c;當時…

c語言函數聲明定義參數命名,C語言函數聲明與定義

C語言函數聲明與定義教程在C語言函數聲明與定義語法type funcName(paramType1 param1, paramType2 param2){// 執行語句...return val}參數參數描述type函數的返回值類型&#xff0c;如果沒有任何返回值&#xff0c;則寫 void&#xff0c;不可以死省略不寫。funcName函數名。pa…