欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Asp.Net WebApi Token验证 权限验证

程序员文章站 2022-07-11 10:38:03
...

原文地址 https://www.cnblogs.com/w5942066/p/12055542.html
作者 魏杨杨

1、前言
WebAPI主要开放数据给手机APP,Pad,其他需要得知数据的系统,或者软件应用。Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能。我上次写的《Asp.Net MVC WebAPI的创建与前台Jquery ajax后台HttpClient调用详解》这种跟明显安全性不是那么好,于是乎这个就来了 ,用户需要访问的API都必须带有票据过来,说白了就是登陆之后含有用户信息的Token。开始撸…

2、新建一个WebApi项目
在App_Start文件夹下面新建一个BaseApiController控制器,这是基础的Api控制器,后面有要验证的接口都继承这个控制器:

using LoginReqToken.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;

namespace LoginReqToken.App_Start
{
    /// <summary>
    /// 基础Api控制器  所有的都继承他
    /// </summary>
    public class BaseApiController : ApiController
    {
       
       /// <summary>
       /// 构造函数赋值
       /// </summary>
        public BaseApiController()
        {
            TokenValue = HttpContext.Current.Session[LoginID] ?? "";
            HttpContext.Current.Request.Headers.Add("TokenValue", TokenValue.ToString());
        }
        /// <summary>
        /// 数据库上下文
        /// </summary>
        public WYDBContext db = WYDBContextFactory.GetDbContext();
        /// <summary>
        /// token值 登录后赋值请求api的时候添加到header中
        /// </summary>
        public static object TokenValue { get; set; } = "";
        /// <summary>
        /// 登录者账号
        /// </summary>
        public static string LoginID { get; set; } = "";
    }
}

这个构造函数里主动加一个header头信息 ,因为每次访问的时候都要执行构造函数,在那边验证的时候都要从Header中取出来,计算出用户名 是否跟Session缓存的一致这样判断的

3、在建一个TokenCheckFilter.cs
继承AuthorizeAttribute重写基类的验证方式,重写HandleUnauthorizedRequest

using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Security;
namespace LoginReqToken.App_Start
{
    /// <summary>
    /// token验证
    /// </summary>
    public class TokenCheckFilter: AuthorizeAttribute
    {

        /// <summary>
        /// 重写基类的验证方式,加入自定义的Ticket验证
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var content = actionContext.Request.Properties["MS_HttpContext"] as HttpContextBase;
            //获取token(请求头里面的值)
            var token = HttpContext.Current.Request.Headers["TokenValue"] ?? "";
            //是否为空
            if (!string.IsNullOrEmpty(token.ToString()))
            {
                //解密用户ticket,并校验用户名密码是否匹配
                if (ValidateTicket(token.ToString()))
                    base.IsAuthorized(actionContext);
                else
                    HandleUnauthorizedRequest(actionContext);
            }
            //如果取不到身份验证信息,并且不允许匿名访问,则返回未验证403
            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);
            }
        }

        //校验用户名密码(对Session匹配,或数据库数据匹配)
        private bool ValidateTicket(string encryptToken)
        {
            //解密Ticket
            var strTicket = FormsAuthentication.Decrypt(encryptToken).UserData;
            //从Ticket里面获取用户名和密码
            var index = strTicket.IndexOf("&");
            string userName = strTicket.Substring(0, index);
            string password = strTicket.Substring(index + 1);
            //取得session,不通过说明用户退出,或者session已经过期
            var token = HttpContext.Current.Session[userName];
            if (token == null)
                return false;
            //对比session中的令牌
            if (token.ToString() == encryptToken)
                return true;
            return false;
        }
        /// <summary>
        /// 重写HandleUnauthorizedRequest
        /// </summary>
        /// <param name="filterContext"></param>
        protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
        {
            base.HandleUnauthorizedRequest(filterContext);

            var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage();
            //状态码401改为其他状态码来避免被重定向。最合理的是改为403,表示服务器拒绝。
            response.StatusCode = HttpStatusCode.Forbidden;
            var content = new 
            {
                success = false,
                errs = new[] { "服务端拒绝访问:你没有权限?,或者掉线了?" }
            };
            response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
        }

    }
}

4、在WebApiConfig.cs配置文件里面修改一下路由加上/{action},这样就能调用到具体的哪一个了

 config.Routes.MapHttpRoute(
               name: "testapi",//name 仅仅是名字
               routeTemplate: "utoapi/{controller}/{action}/{id}", //utoapi是路径和iis网站名字没有关系 test是控制器名字 getuser是方法名  {id}是可选参数    http://localhost:44300/MYAPI/test/Getuser?username=1&password=2
               defaults: new { id = RouteParameter.Optional }
           );

Webapi默认是不支持Session的,所以我们需要在Global加载时候添加对Session的支持,在Global.asax里面重写Application_PostAuthorizeRequest,不然运行调用会直接异常

 public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
        /// <summary>
        /// 重写Application_PostAuthorizeRequest
        /// </summary>
        protected void Application_PostAuthorizeRequest()
        {
            //对Session的支持,不然运行调用会直接异常
            HttpContext.Current.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Required);
        }
    }

5、现在来写一个登陆和登录退出
新建一个控制器LoginController继承BaseApiController 里面写一个登陆的方法Login 登陆页面就直接在Home的index里面写一个简单的就行了这个控制器访问就不受限制了加上注解

    public class LoginController :BaseApiController {

        [HttpGet]
        public object Login(string username,string password) {
            if (username=="SYS"&&password=="123") {  //数据库验证,这边模拟验证
                FormsAuthenticationTicket token = new FormsAuthenticationTicket(0, username, DateTime.Now, DateTime.Now.AddHours(12), true, $"{username}&{password}", FormsAuthentication.FormsCookiePath);
                //返回登录结果、用户信息、用户验证票据信息
                var _token = FormsAuthentication.Encrypt(token);
                //将身份信息保存在session中,验证当前请求是否是有效请求
                LoginID = username;
                TokenValue = _token;
                HttpContext.Current.Session[LoginID] = _token;     //Webapi默认是不支持Session的,所以我们需要在Global加载时候添加对Session的支持,在Global.asax里面重写Application_PostAuthorizeRequest,不然运行调用会直接异常
                return Json(new { ret = 1, data = _token, msg = "登录成功!" });
            } else {
                return Json(new { ret = 0, data = "", msg = "用户名密码错误" });
            }
        }


        [HttpGet]
        public object Loginout() {
            if (!string.IsNullOrEmpty(LoginID)) {  
                HttpContext.Current.Session[LoginID] = "";     //Webapi默认是不支持Session的,所以我们需要在Global加载时候添加对Session的支持,在Global.asax里面重写Application_PostAuthorizeRequest,不然运行调用会直接异常
                LoginID = "";
                return Json(new { ret = 1, data = "", msg = "退出成功!" });
            } else {
                return Json(new { ret = 0, data = "", msg = "你还没登录呢!" });
            }
        }
    }

6、现在就可以写Api
都继承BaseApiController这个控制器的方法上面需要验证的都要加上验证的注解,我是整个控制都要就直接写在类上面了,随便写一个举举例子

public class AreaController : BaseApiController {

        [TokenCheckFilter]
        public object GetAllAreas() {
            if (System.DateTime.Now.Second%2==0) {  
                return Json(new { ret = 0, data = "", msg = "秒数是偶数!" });
            } else {
                return Json(new { ret = 1, data = "", msg = "秒数是基数!" });
            }
        }
    }

Asp.Net WebApi Token验证 权限验证

Asp.Net WebApi Token验证 权限验证

Asp.Net WebApi Token验证 权限验证

这里贴一个调用的代码:

HttpClient bb = new HttpClient();
            //获取端口
            HttpContent httpContent = new StringContent("");
            httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
            
             var dl = bb.GetAsync("http://localhost:63828/api/Login/login?uName=admin&uPassword=admin888").Result.Content.ReadAsStringAsync().Result;
            var token = JsonConvert.DeserializeObject<Result>(dl);
           
            for (var i=0;i<100;i++)
            {
                
                var ret = bb.GetAsync("http://localhost:63828/api/Cnblog/GetAllArtic").Result.Content.ReadAsStringAsync().Result;
            }
相关标签: WebApi