前言
RESTful Web API 使用Owin自托管的程序需要对客户端的访问做身份验证,可以使用FORM身份验证加Cookie来实现,缺点是由于使用了Cookie,不支持跨域的操作。
实现过程
定义FormAuthenticationFilterAttribute类
定义一个FormAuthenticationFilterAttribute,该类继承自AuthorizationFilterAttribute,并重写其OnAuthorization,在该方法中添加从请求头中获取有无登录的Cookie,若有则表示登录成功,否则失败,代码如下:
public class FormAuthenticationFilterAttribute : AuthorizationFilterAttribute
{
private const string UnauthorizedMessage = "Unauthorized";
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0)
{
base.OnAuthorization(actionContext);
return;
}
var ctx = actionContext.Request.GetOwinContext();
if (ctx.Request.User != null && ctx.Request.User.Identity.IsAuthenticated)
{
base.OnAuthorization(actionContext);
return;
}
var cookies = actionContext.Request.Headers.GetCookies();
if (cookies == null || cookies.Count < 1)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent(UnauthorizedMessage, Encoding.UTF8) };
return;
}
FormsAuthenticationTicket ticket = GetTicket(cookies);
if (ticket == null)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent(UnauthorizedMessage, Encoding.UTF8) };
return;
}
if (ticket.Expired)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent(UnauthorizedMessage, Encoding.UTF8) };
return;
}
base.OnAuthorization(actionContext);
}
private FormsAuthenticationTicket GetTicket(Collection<CookieHeaderValue> cookies)
{
FormsAuthenticationTicket ticket = null;
foreach (var item in cookies)
{
var cookie = item.Cookies.SingleOrDefault(c => c.Name == FormsAuthentication.FormsCookieName);
if (cookie != null)
{
ticket = FormsAuthentication.Decrypt(cookie.Value);
break;
}
}
return ticket;
}
添加上述授权过滤器
FormAuthenticationFilterAttribute,也可在global文件中将该类添加到全局过滤器中,同时定义一个登录ACTION,用于登录入口,示例代码如下:
[FormAuthenticationFilter]
[EnableCors(origins: "*", headers: "*", methods: "*")]
[RoutePrefix("api/post/auth")]
public class AuthController : ApiController
{
[AllowAnonymous]
[HttpPost]
[Route("login")]
public HttpResponseMessage DoLogin(string token)
{
var context = Request.GetOwinContext();
if ("admin".Equals(token, StringComparison.OrdinalIgnoreCase))
{
//创建票据
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, token, DateTime.Now, DateTime.Now.AddMinutes(1), false, string.Empty);
//加密票据
string authTicket = FormsAuthentication.Encrypt(ticket);
//存储为cookie
context.Response.Cookies.Append(FormsAuthentication.FormsCookieName, authTicket,
new CookieOptions(){ Expires = DateTime.Now.Add(TimeSpan.FromMinutes(1))});
return Request.CreateResponse(HttpStatusCode.OK, "登录成功!");
}
else
{
return Request.CreateResponse(HttpStatusCode.Unauthorized, "登录失败!");
}
}
[HttpGet]
[Route("get-status")]
public HttpResponseMessage GetStatus()
{
return Request.CreateResponse(HttpStatusCode.OK, "Authentication");
}
}
测试
可直接在浏览器中访问需要授权的方法(即:Login除外),如:http://192.168.1.100:8088/api/post/auth/get-status,响应结果如下:
可见,没有授权时返回了Unauthorized。
使用Postman进行测试,没有授权时,访问api/post/auth/get-status的响应结果如下:
授权,响应结果如下:
授权成功后,再访问api/post/auth/get-status的响应结果如下:
评论区