雷达智富

首页 > 内容 > 程序笔记 > 正文

程序笔记

C# RESTful Owin 自托管的Web API使用FORM实现身份验证

2024-07-24 32

前言

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.GetCustomAttributesAllowAnonymousAttribute().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(CollectionCookieHeaderValue 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,响应结果如下:
image-1665402436617
可见,没有授权时返回了Unauthorized。

使用Postman进行测试,没有授权时,访问api/post/auth/get-status的响应结果如下:
image-1665402779695

授权,响应结果如下:
image-1665402841088

授权成功后,再访问api/post/auth/get-status的响应结果如下:
image-1665403190679

更新于:1个月前
赞一波!2

文章评论

全部评论