接續昨天的,前言什麼的不重要,也沒有人想看!
昨天的專案我們已經完成了基本的帳號的Function和API的建立,可以方便管理者管理使用者帳戶的新增、編輯、刪除,而今天我們就要來建置,當使用者驗證完成帳號後,會回傳一組token回去,利用此組token可以在一定時間內在權限內暢行無阻 (?
他就像是 臨時的身分證
,有了這張證,當每次來跟我要東西的時候,都會先去對對看身分證是否符合本人,若符合就給你資訊。
STEP1. 首先我們要先建立Model:新增 Models/auth/loginInfo.cs
類別檔。
loginInfo
:登入輸入資訊tokenObj
:權限設定輸入資訊using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace OLMapAPI.Models.auth
{
public class loginInfo
{
public string userid;
public string password;
public loginInfo(string _userid, string _password)
{
userid = _userid;
password = _password;
}
public loginInfo() { }
}
/// <summary>
/// 權限設定input
/// </summary>
public class tokenObj
{
public string status;
public string token;
public tokenObj(string _status, string _token)
{
status = _status;
token = _token;
}
public tokenObj() { }
}
}
STEP2. 建立相對應的function:建立 Infrastructure/auth/authFunc.cs
授權相關程式碼檔。
整體驗證步驟如下:
validatesApiUser()
驗證帳號密碼
sys_apiUser
資料表檢查該帳號是否有被lock。
aspnetUserId
根據昨天建立的功能去new一個 UserManager()
物件來撈取 user
。CheckPassword()
來檢查是否正確。createToken()
,建立並回傳 tokenObj
資訊。using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.Identity;
using OLMapAPI.Models;
using System.Data;
using System.Configuration;
using System.Web.UI.WebControls;
using System.Data.SqlClient;
using OLMapAPI.Models.auth;
namespace OLMapAPI.Infrastructure.auth
{
public class authFunc
{
/// <summary>
/// apiuser-驗證帳號
/// </summary>
public tokenObj validatesApiUser(loginInfo login)
{
string sqlstr = @"SELECT userId,aspnetUserId,aspnetPassword FROM sys_apiUser where lockYN='0' and userId=@userId";
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand(sqlstr, conn);
conn.Open();
cmd.Parameters.AddWithValue("@userId", login.userid);
SqlDataReader dr = cmd.ExecuteReader();
string userId = "";
string aspnetUserId = "";
while (dr.Read())
{
userId = dr["userId"].ToString();
aspnetUserId = dr["aspnetUserId"].ToString();
}
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
if (userId != "" && aspnetUserId != "" && login.password != "")
{
var manager = new UserManager();
ApplicationUser user = manager.FindByName(aspnetUserId);
bool YN = manager.CheckPassword(user, "apiUser@_" + login.password);
if (YN)
{
string token = createToken(aspnetUserId, "apiuser");
tokenObj tokenobj = new tokenObj("success", token);
return tokenobj;
}
else
{
tokenObj error = new tokenObj("error-no dotnet user", "");
return error;
}
}
else
{
tokenObj error = new tokenObj("error-no user", "");
return error;
}
}
// token相關功能
}
}
STEP3. 接續STEP2進行token相關功能的建置。
delExpiredToken()
,將資料表內所有超過有效期限的token都刪除。createToken()
來進行token的建立。
generateToken()
進行建立。GetRandomString()
作預設,明天會再講解token的加密設計。getClientIP()
去撈取ip資訊。sys_tokens
資料表,並設計有效期限只有8小時 (可自行設定)。validatesToken()
來驗證其有效性,並回傳 userid
。// token相關功能
private string createToken(string userid, string type)
{
string token = generateToken();
delExpiredToken();
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand("INSERT INTO sys_tokens (userid,type,token,ip,issuedOn,expiredOn) Values (@userid,@type,@token,@ip,@issuedOn,@expiredOn)", conn);
conn.Open();
cmd.Parameters.AddWithValue("@userid", userid);
cmd.Parameters.AddWithValue("@type", type);
cmd.Parameters.AddWithValue("@token", token);
cmd.Parameters.AddWithValue("@ip", getClientIP());
cmd.Parameters.AddWithValue("@issuedOn", DateTime.Now);
cmd.Parameters.AddWithValue("@expiredOn", DateTime.Now.AddHours(8)); //8小時後刪除
SqlDataReader dr = cmd.ExecuteReader();
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return token;
}
private void delExpiredToken()
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand("DELETE sys_tokens Where GETDATE() > expiredOn", conn);
conn.Open();
SqlDataReader dr = cmd.ExecuteReader();
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
}
protected string generateToken()
{
return GetRandomString(10);
}
public static string GetRandomString(int length)
{
Random r = new Random();
string code = "";
for (int i = 0; i < length; ++i)
switch (r.Next(0, 3))
{
case 0: code += r.Next(0, 10); break;
case 1: code += (char)r.Next(65, 91); break;
case 2: code += (char)r.Next(97, 122); break;
}
return code;
}
protected string getClientIP()
{
//判所client端是否有設定代理伺服器
//if (HttpContext.Current.Request.ServerVariables["HTTP_VIA"] == null)
// return HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"].ToString();
//else
// return HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();
string VisitorsIPAddr = string.Empty;
if (HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"] != null)
{
VisitorsIPAddr = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString();
}
else if (HttpContext.Current.Request.UserHostAddress.Length != 0)
{
VisitorsIPAddr = HttpContext.Current.Request.UserHostAddress;
}
return VisitorsIPAddr;
}
/// <summary>
/// 驗證token
/// </summary>
public string validatesToken(string token)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
SqlCommand cmd = new SqlCommand("select userid from sys_tokens where token=@token and GETDATE()<=expiredOn", conn);
conn.Open();
cmd.Parameters.AddWithValue("@token", token);
SqlDataReader dr = cmd.ExecuteReader();
string userid = "";
while (dr.Read())
{
userid = dr["userid"].ToString();
}
dr.Close(); dr.Dispose(); conn.Close(); conn.Dispose();
return userid;
}
STEP4. 新增Controller:新增 Authcontroller.cs
頁面。
就只有一個API,validatesApiUser()
使用已註冊的帳號密碼取得token,而且這隻API不用經過權限驗證。
這隻就是用來取得Token的,所以介接這支API不需要輸入token。
using OLMapAPI.Infrastructure.auth;
using OLMapAPI.Models.auth;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace OLMapAPI.Controllers
{
/// <summary>
/// VS驗證
/// </summary>
//[Authorize]
[RoutePrefix("api/Auth")]
public class AuthController : ApiController
{
/// <summary>
/// 由帳號密碼返回token
/// </summary>
/// <remarks>使用已註冊的帳號密碼取得token</remarks>
/// <response code="200">OK</response>
/// <response code="400">Not found</response>
//介接api用的帳號,僅提供token返回,不提供token取得資訊,該token僅可使用一般API
[Route("validatesApiUser")]
[HttpPost, AllowAnonymous]
public tokenObj validatesApiUser(loginInfo login)
{
authFunc auth = new authFunc();
tokenObj token = auth.validatesApiUser(login);
return token;
}
}
}
這邊先穿插一段用來記錄API日誌的套件NLog,使用NuGet安裝NLog套件
NLog is a flexible and free logging platform for various .NET platforms, including .NET standard. NLog makes it easy to write to several targets. (database, file, console) and change the logging configuration on-the-fly.
因為NLog講下去也是落落長,在這邊就不太詳細說明,請參考:
新增 NLog.config
裡面紀錄儲存路徑、格式等相關設定;這部分是直接參考上述第一篇。
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true">
<!--[變數] 文字樣板 -->
<variable name="Layout" value="${longdate} | ${level:uppercase=true} | ${logger} | ${message} ${newline}"/>
<variable name="LayoutFatal" value="${longdate} | ${level:uppercase=true} | ${logger} | ${message} | ${exception:format=tostring} ${newline}"/>
<!--[變數] 檔案位置 -->
<variable name="LogTxtDir" value="${basedir}/App_Data/Logs/${shortdate}/"/>
<variable name="LogTxtLocation" value="${LogTxtDir}/${logger}.log"/>
<variable name="LogTxtLocationFatal" value="${LogTxtDir}/FatalFile.log"/>
<!--[設定] 寫入目標-->
<targets>
<target name="File" xsi:type="File" fileName="${LogTxtLocation}" layout="${Layout}"
encoding="utf-8" maxArchiveFiles="30" archiveNumbering="Sequence"
archiveAboveSize="1048576" archiveFileName="${LogTxtDir}/${logger}.log{#######}" />
<target name="FileFatal" xsi:type="File" fileName="${LogTxtLocationFatal}" layout="${LayoutFatal}"
encoding="utf-8" maxArchiveFiles="30" archiveNumbering="Sequence"
archiveAboveSize="1048576" archiveFileName="${LogTxtDir}/FatalFile.log{#######}" />
<target name="EventLog" xsi:type="EventLog" source="NLogLogger" log="Application"
layout="${date}| ${level} | ${message}"/>
</targets>
<!--[設定] 紀錄規則-->
<rules>
<logger name="*" levels="Trace,Debug,Info,Warn" writeTo="File" />
<logger name="*" levels="Error,Fatal" writeTo="FileFatal" />
<logger name="*" levels="Fatal" writeTo="EventLog" />
</rules>
</nlog>
接下來,新增 MessageHandler/LogMessageHandler.cs
程式碼檔,這邊就直接看程式碼吧。
using System;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Collections.Generic;
using NLog;
using OLMapAPI.Infrastructure.auth;
namespace OLMapAPI.MessageHandler
{
public class actionlog : ILog
{
private static Logger logger = NLog.LogManager.GetCurrentClassLogger();
public void Save(string logContent)
{
// 原本 Nlog 的寫法
logger.Info(logContent);
//// 擷取自訂訊息
//List<string> singleAttribute = logContent.Split(',').ToList<string>();
//string name = singleAttribute[0].Trim(new Char[] { '{', '}' }).Split(new char[] { ':' }, 2)[1];
//string ip = singleAttribute[1].Trim(new Char[] { '{', '}' }).Split(new char[] { ':' }, 2)[1];
//// 紀錄自訂訊息
//logger.LogExt(LogLevel.Info, logContent, name, "UserName"); //userid
}
}
public class actionlogISerializer : ISerializer
{
public string Serialize<T>(RequestLogInfo info)
{
//return info.IpAddress + "|" + info.RequestTime + "|" + info.Signature + "|" + info.HttpMethod + "|" + info.UrlAccessed + "|" + info.BodyContent;
return "{userid:" + info.Signature + "},{ip:" + info.IpAddress + "},{method:" + info.HttpMethod + "},{time:" + info.RequestTime.ToString("yyyy/MM/dd HH:mm:ss") + "},{request:" + info.UrlAccessed + "}";
}
public string Serialize<T>(ResponseLogInfo info)
{
return info.ReturnCode + "|" + info.ReturnMessage + "|" + info.ResponseTime + "|" + info.BodyContent;
}
}
public interface ILog
{
void Save(string logContent);
}
public interface ISerializer
{
string Serialize<T>(RequestLogInfo info);
string Serialize<T>(ResponseLogInfo info);
}
public class LogMessageHandler : MessageProcessingHandler
{
private ILog _log;
private ISerializer _serializer;
public LogMessageHandler(ILog log, ISerializer serializer)
{
this._log = log;
this._serializer = serializer;
}
/// <summary>
/// 將 request 相關訊息記錄 log
/// </summary>
/// <param name="request">The HTTP request message to process.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// Returns <see cref="T:System.Net.Http.HttpRequestMessage" />.The HTTP request message that was processed.
/// </returns>
private const string _header = "Authorization";
protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
//由token換成userid
string userid = "null";
var token = Enumerable.Empty<string>();
bool isHeaderExist = request.Headers.TryGetValues(_header, out token);
if (isHeaderExist)
{
if (token.Count() > 0)
{
authFunc auth = new authFunc();
userid = auth.validatesToken(token.First());
}
}
if (request == null)
{
throw new ArgumentNullException("request");
}
var info = new RequestLogInfo
{
HttpMethod = request.Method.Method,
UrlAccessed = request.RequestUri.AbsoluteUri,
IpAddress = HttpContext.Current != null ? HttpContext.Current.Request.UserHostAddress : "0.0.0.0",
RequestTime = DateTime.Now,
Token = this.GetToken(request),
Signature = userid,
Timestamp = this.GetTimestamp(request),
BodyContent = request.Content == null ? string.Empty : request.Content.ReadAsStringAsync().Result
};
var logContent = this._serializer.Serialize<RequestLogInfo>(info);
this._log.Save(logContent);
return request;
}
/// <summary>
/// 將 response 相關訊息記錄 log
/// </summary>
/// <param name="response">The HTTP response message to process.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <returns>
/// Returns <see cref="T:System.Net.Http.HttpResponseMessage" />.The HTTP response message that was processed.
/// </returns>
protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, System.Threading.CancellationToken cancellationToken)
{
if (response == null)
{
throw new ArgumentNullException("response");
}
var info = new ResponseLogInfo
{
StatusCode = ((int)response.StatusCode).ToString(),
ResponseTime = DateTime.Now,
ReturnCode = this.GetReturnCode(response),
ReturnMessage = this.GetReturnMessage(response),
Signature = this.GetSignature(response),
BodyContent = response.Content == null ? string.Empty : response.Content.ReadAsStringAsync().Result
};
var logContent = this._serializer.Serialize<ResponseLogInfo>(info);
this._log.Save(logContent);
return response;
}
private string GetReturnCode(HttpResponseMessage response)
{
return HeaderHelper.GetHeaderValue(response.Headers, "api-returnCode").Item2;
}
private string GetReturnMessage(HttpResponseMessage response)
{
return HeaderHelper.GetHeaderValue(response.Headers, "api-returnMessage").Item2;
}
private string GetSignature(HttpResponseMessage response)
{
return HeaderHelper.GetHeaderValue(response.Headers, "api-signature").Item2;
}
private string GetSignature(HttpRequestMessage request)
{
return HeaderHelper.GetHeaderValue(request.Headers, "api-signature").Item2;
}
private string GetTimestamp(HttpRequestMessage request)
{
return HeaderHelper.GetHeaderValue(request.Headers, "api-timestamp").Item2;
}
private string GetToken(HttpRequestMessage request)
{
return HeaderHelper.GetHeaderValue(request.Headers, "api-token").Item2;
}
}
public class RequestLogInfo
{
public string BodyContent { get; set; }
public string HttpMethod { get; set; }
public string IpAddress { get; set; }
public DateTime RequestTime { get; set; }
public string Signature { get; set; }
public string Timestamp { get; set; }
public string Token { get; set; }
public string UrlAccessed { get; set; }
}
public class ResponseLogInfo
{
public string BodyContent { get; set; }
public DateTime ResponseTime { get; set; }
public string ReturnCode { get; set; }
public string ReturnMessage { get; set; }
public string Signature { get; set; }
public string StatusCode { get; set; }
}
internal class HeaderHelper
{
internal static Tuple<bool, string> GetHeaderValue(HttpResponseHeaders httpResponseHeaders, string headerName)
{
var result = string.Empty;
var specialHeaders = Enumerable.Empty<string>();
var isExistHeader = httpResponseHeaders.TryGetValues(headerName, out specialHeaders);
if (isExistHeader)
{
result = specialHeaders.LastOrDefault();
}
return Tuple.Create(isExistHeader, result);
}
internal static Tuple<bool, string> GetHeaderValue(HttpRequestHeaders httpRequestHeaders, string headerName)
{
var result = string.Empty;
var specialHeaders = Enumerable.Empty<string>();
var isExistHeader = httpRequestHeaders.TryGetValues(headerName, out specialHeaders);
if (isExistHeader)
{
result = specialHeaders.LastOrDefault();
}
return Tuple.Create(isExistHeader, result);
}
}
}
假設今天是2020-10-01,就會在專案檔資料夾下 ~\OLMapAPI\App_Data\Logs\2020-10-01\OLMapAPI.MessageHandler.actionlog.log
看到以下Log資訊。
Message Handler,可以讓在不改 Web API 的情況下,透過它來處理一些 WebAPI 共同要處理的項目,服務收到請求後可能會經過數個 Message Handler 逐步處理後再返回訊息至客戶端,見下圖。
來源:ASP.NET Web API 中的 HTTP 訊息處理常式
新增 MessageHandler/AuthMessageHandler.cs
的頁面,主要就是在執行API之前先執行這支MessageHandler,撈取header裡面的 Authorization
參數值。
(我這邊是設計token要帶在 Authorization
這個參數內,也可自行設計 ex.直接使用Token
)
將token分離出來以後,執行 validatesToken()
回傳userid並 SetPrincipal()
。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Http;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using OLMapAPI.Models;
using OLMapAPI.Infrastructure.auth;
namespace OLMapAPI.MessageHandler
{
public class AuthMessageHandler : DelegatingHandler
{
/// <summary>
/// Header名稱預設為「APIKey」,改為Authorization
/// </summary>
private const string _header = "Authorization";
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//// 檢查request裡是否有「Authorization」這個Header
var token = Enumerable.Empty<string>();
bool isHeaderExist = request.Headers.TryGetValues(_header, out token);
if (isHeaderExist)
{
if (token.Count() > 0)
{
authFunc auth = new authFunc();
string userid = auth.validatesToken(token.First());
this.SetPrincipal(userid);
}
}
return base.SendAsync(request, cancellationToken);
}
/// <summary>
/// 設定IPrincipal
/// </summary>
private void SetPrincipal(string userid)
{
//// 設定使用者識別 => 就是使用者名稱啦
//// GenericIdentity.IsAuthenticated 預設為true
GenericIdentity identity = new GenericIdentity(userid);
account acc = new account();
String[] mMyStringArray = acc.ListUserRoles(userid).ToArray();//{ "admin" };
//// 將使用者的識別與其所屬群組設定到GenericPrincipal類別上
GenericPrincipal principal = new GenericPrincipal(identity, mMyStringArray);
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
}
}
每次撈取API之前,先去檢查是否需要權限授權,與使用者名稱是否有授權,就像濾鏡一樣先篩掉不符合規定的
新增 filter/apiAuthorizationFilter.cs
程式碼檔。
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Threading;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace OLMapAPI.filter
{
public class apiAuthorizationFilter : AuthorizationFilterAttribute
{
/// <summary>
/// The authorization service
/// </summary>
private IAuthorizationService authorizationService = new CustomAuthorizationService();
/// <summary>
/// 在處理序要求授權時呼叫。
/// </summary>
/// <param name="actionContext">動作內容,該內容封裝 <see cref="T:System.Web.Http.Filters.AuthorizationFilterAttribute" /> 的使用資訊。</param>
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool isAuthorizated = false;
//// 從Thread取出IPrincipal
IPrincipal principal = Thread.CurrentPrincipal;
isAuthorizated = authorizationService.IsAuthorizated(principal, "");
if (SkipAuthorization(actionContext))
{
return;
}
if (!isAuthorizated)
{
//// CreateResponse是System.Net.Http命名空間的擴充方法
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
private static bool SkipAuthorization(HttpActionContext actionContext)
{
if (!Enumerable.Any<AllowAnonymousAttribute>((IEnumerable<AllowAnonymousAttribute>)actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>()))
return Enumerable.Any<AllowAnonymousAttribute>((IEnumerable<AllowAnonymousAttribute>)actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>());
else
return true;
}
}
/// <summary>
/// admin Filter
/// </summary>
public class apiAdminFilter : AuthorizationFilterAttribute
{
private IAuthorizationService authorizationService = new CustomAuthorizationService();
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
bool isAuthorizated = false;
IPrincipal principal = Thread.CurrentPrincipal;
isAuthorizated = authorizationService.IsAuthorizated(principal, "admin");
if (SkipAuthorization(actionContext)) { return; }
if (!isAuthorizated) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized); }
}
private static bool SkipAuthorization(HttpActionContext actionContext)
{
if (!Enumerable.Any<AllowAnonymousAttribute>((IEnumerable<AllowAnonymousAttribute>)actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>()))
return Enumerable.Any<AllowAnonymousAttribute>((IEnumerable<AllowAnonymousAttribute>)actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>());
else
return true;
}
}
/// <summary>
/// IAuthorizationService
/// </summary>
public interface IAuthorizationService
{
/// <summary>
/// 檢查該使用者的名稱是否有權限
/// </summary>
bool IsAuthorizated(IPrincipal principal, string checkrole);
}
/// <summary>
/// CustomAuthorizationService
/// </summary>
public class CustomAuthorizationService : IAuthorizationService
{
public bool IsAuthorizated(IPrincipal principal, string checkrole)
{
string userId = principal.Identity.Name;
if (userId == "")
{
return false;
}
else if (userId != "" && checkrole == "")
{
return true;
}
else
{
return principal.IsInRole(checkrole);
}
}
}
}
在 web.config
中新增 lockYN
參數,用來控制此API是否要開啟驗證機制;這邊當然預設要開啟 "Y"
。
<!-- 本專案API Filter -->
<add name="lockYN" connectionString="Y" />
在WebApiconfig.cs
中新增MessageHandlers
和Filter
,並設定 CORS
。
CORS的部分可以直接鎖Domain,若只有要給自己的網頁使用,可以只鎖定在架設網站的那台AP上。
config.MessageHandlers.Add(new AuthMessageHandler());
config.MessageHandlers.Add(new LogMessageHandler(new actionlog(), new actionlogISerializer()));
/*允許所有的方法跨域*/
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
//註冊整個專案的Filter
string lockYN = ConfigurationManager.ConnectionStrings["lockYN"].ConnectionString;
if (lockYN == "Y")
{
config.Filters.Add(new apiAuthorizationFilter());
}
今天的長度好像是20天以來最長的,但主要篇幅都在程式碼上面。
至於這個系列的資安問題,我這邊比較少去驗證、阻擋和著墨,不過最低最低的限度就是不要造成 SQL Injection
還有 明碼儲存和驗證
的部分。
今天已經大致完成了90%的驗證機制的建立,token設計
的部份明天再行說明。
我想這一系列大家應該已經快看不下去了