WebApi相關文章:
- C# 搭建一個簡單的Web API項目
- C# WebApi 路由配置
- c# WebApi之解決跨域問題:Cors
- c# WebApi之身份驗證:Basic基礎認證
- c# WebApi之接口傳回類型詳解
為什麼需要身份認證
身份認證是為了提高接口通路的安全性,如果沒有身份驗證,那麼任何匿名使用者隻要知道伺服器的url,就可以随意通路伺服器,進而通路或者操作資料庫,這會是很恐怖的事。
什麼是Basic基礎認證
Basic基礎認證是一種簡單的使用者名、密碼驗證過程,它的主要原理是加密使用者資訊,生成票據,每次需要身份驗證時将票據帶過來驗證,實作步驟為:
- 使用者登入,登入成功後将生成的票據傳回到前端;
- 前端登入成功後,收到票據資訊,跳轉到首頁面,并且吧票據一并帶過去,存入Session;
- 在需要請求頁面,把票據資訊加入到請求的Head裡面,将票據資訊随着請求一起發送到服務端去;
- 在WebApi服務裡面定義一個類,繼承AuthorizeAttribute類,然後重寫父類的OnAuthorization方法,在OnAuthorization方法裡面取到目前http請求的Head,從Head裡面取到我們前端傳過來的票據資訊。解密票據資訊,從解密的資訊裡面得到使用者名和密碼,然後驗證使用者名和密碼是否正确。如果正确,表示驗證通過,否則傳回自定義錯誤資訊。
Basic基礎認證的代碼示例:
首先建立兩個項目:Web測試站點、WebApi站點
1.1、在Web測試站點,添加一個登入頁面:
<div style="text-align:center;">
<div>使用者名:<input type="text" id="txt_username" /></div>
<div>密 碼:<input type="password" id="txt_password" /></div>
<div><input type="button" value="登入" id="btn_login" class="btn-default" /></div>
</div>
登入請求的ajax:
$(function () {
$("#btn_login").click(function () {
$.ajax({
type: "get",
url: "http://localhost:61593/api/account/login",
data: { strUser: $("#txt_username").val(), strPwd: $("#txt_password").val() },
success: function (data, status) {
if (status == "success") {
if (!data.bRes) {
alert("登入失敗");
return;
}
alert("登入成功");
//登入成功之後将使用者名和使用者票據帶到主界面
window.location = "/Home/Index?UserName=" + data.UserName + "&Ticket=" + data.Ticket;
}
},
error: function (e) {
},
complete: function () {
}
});
});
});
1.2、對應的WebApi站點的,登入的Api接口:
/// <summary>
/// 使用者登入
/// </summary>
/// <param name="strUser"></param>
/// <param name="strPwd"></param>
/// <returns></returns>
[HttpGet]
public object Login(string strUser, string strPwd)
{
if (!ValidateUser(strUser, strPwd))
{
return new { bRes = false };
}
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(, strUser, DateTime.Now,
DateTime.Now.AddHours(), true, string.Format("{0}&{1}", strUser, strPwd),
FormsAuthentication.FormsCookiePath);
//傳回登入結果、使用者資訊、使用者驗證票據資訊
var oUser = new UserInfo { bRes = true, UserName = strUser, Password = strPwd, Ticket = FormsAuthentication.Encrypt(ticket) };
//将身份資訊儲存在session中,驗證目前請求是否是有效請求
HttpContext.Current.Session[strUser] = oUser;
return oUser;
}
//校驗使用者名密碼(正式環境中應該是資料庫校驗)
private bool ValidateUser(string strUser, string strPwd)
{
if (strUser == "admin" && strPwd == "123456")
{
return true;
}
else
{
return false;
}
}
自定義UserInfo實體:
public class UserInfo
{
public bool bRes { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Ticket { get; set; }
}
建立的WebApi需要配置一下路由,打開App_Start檔案夾下的WebApiConfig.cs檔案,添加一條路由資訊:
public static void Register(HttpConfiguration config)
{
//解決跨域通路問題
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi1",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
如果是兩個站點的話可能會出現跨域問題,解決跨域通路問題可以參考:
http://blog.csdn.net/lwpoor123/article/details/78457589
2.1、在Web測試站點添加一個用于跳轉測試的index首頁面
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
</head>
<body>
測試結果:
<div id="div_test">
hello world
</div>
目前登入使用者:
<div id="username">
@ViewBag.UserName
</div>
</body>
</html>
ajax請求:
<script>
var ApiUrl = "http://localhost:61593/";
$(function () {
$.ajax({
type: "get",
url: ApiUrl + "api/Account/GetAllData",
data: {},
beforeSend:function(XHR){ XHR.setRequestHeader('Authorization','BasicAuth @ViewBag.Ticket')
},
success: function (data, status) {
if (status == "success") {
$("#div_test").html(data);
}
},
error: function (e) {
$("#div_test").html("Error");
}
});
});
</script>
這裡需要注意在beforeSend方法裡面,向請求的封包頭裡面增加票據資訊,用于把Ticket資訊一同帶到伺服器:
XHR.setRequestHeader(‘Authorization’,’BasicAuth @ViewBag.Ticket’)
2.2、index頁面的action,接收傳遞過來的票據資料,存入Session
public ActionResult Index(string UserName, string Ticket)
{
if (UserName != null)
{
Session["UserName"] = UserName;
}
if (Ticket != null)
{
Session["Ticket"] = Ticket;
}
ViewBag.UserName = Session["UserName"];
ViewBag.Ticket = Session["Ticket"];
return View();
}
2.3、對應的Api接口:
public class AccountController : ApiController
{
/// <summary>
/// 得到所有資料
/// </summary>
/// <returns>傳回資料</returns>
[HttpGet]
[RequestAuthorize]
public string GetAllData()
{
return "Success";
}
}
WebAip預設是沒有開啟Session,需要手動開啟:
在WebApi站點,打開Global.asax檔案,重寫Init()方法
public override void Init()
{
this.PostAuthenticateRequest += (sender, e) => HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
base.Init();
}
或者:
public override void Init()
{
PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpContext.Current.SetSessionStateBehavior(
SessionStateBehavior.Required);
}
3.1、WebApi身份驗證部分(重點)
在WebApi站點,添加一個RequestAuthorizeAttribute.cs檔案,繼承AuthorizeAttribute,自定義此特性用于接口的身份驗證:
/// <summary>
/// 自定義此特性用于接口的身份驗證
/// </summary>
public class RequestAuthorizeAttribute : AuthorizeAttribute
{
//重寫基類的驗證方式,加入我們自定義的Ticket驗證
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//從http請求的頭裡面擷取身份驗證資訊,驗證是否是請求發起方的ticket
var authorization = actionContext.Request.Headers.Authorization;
if ((authorization != null) && (authorization.Parameter != null))
{
//解密使用者ticket,并校驗使用者名密碼是否比對
var encryptTicket = authorization.Parameter;
if (ValidateTicket(encryptTicket))
{
base.IsAuthorized(actionContext);
}
else
{
HandleUnauthorizedRequest(actionContext);
}
}
//如果取不到身份驗證資訊,并且不允許匿名通路,則傳回未驗證401
else
{
var attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>();
bool isAnonymous = attributes.Any(a => a is AllowAnonymousAttribute);
if (isAnonymous) base.OnAuthorization(actionContext);
else HandleUnauthorizedRequest(actionContext);
}
}
protected override void HandleUnauthorizedRequest(HttpActionContext actioncontext)
{
base.HandleUnauthorizedRequest(actioncontext);
var response = actioncontext.Response = actioncontext.Response ?? new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Forbidden;
var content = new
{
code = -,
success = false,
errs = new[] { "服務端拒絕通路:你沒有權限,或者掉線了" }
};
response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
}
//校驗使用者名密碼(正式環境中應該是資料庫校驗)
private bool ValidateTicket(string encryptTicket)
{
//解密Ticket
var strTicket = FormsAuthentication.Decrypt(encryptTicket).UserData;
//從Ticket裡面擷取使用者名和密碼
var index = strTicket.IndexOf("&");
string strUser = strTicket.Substring(, index);
string strPwd = strTicket.Substring(index + );
if (strUser == "admin" && strPwd == "123456")
{
return true;
}
else
{
return false;
}
}
}
3.2、使用的時候隻需要在控制器前面加上自定義的身份驗證[RequestAuthorize]
/// <summary>
/// 得到所有資料
/// </summary>
/// <returns>傳回資料</returns>
[HttpGet]
[RequestAuthorize]
public string GetAllData()
{
return "Success";
}
如果不攜帶票據或者票據無效,服務端拒絕通路:
如果在控制器加了身份驗證,有些請求又不想使用驗證,可以在方法上面添加特性标注[AllowAnonymous]
[RequestAuthorize]
public class AccountController : ApiController
{
/// <summary>
/// 得到所有資料
/// </summary>
/// <returns>傳回資料</returns>
[HttpGet]
public string GetAllData()
{
return "Success";
}
[AllowAnonymous]
public string getData()
{
return "data";
}
}