ASP.NET MVC API 接口验证

项目中有一个留言消息接口,接收其他系统的留言和展示留言,参考了网上的一些API验证方法,发现使用通用权限管理系统提供的验证方法最完美(http://www.cnblogs.com/jirigala/p/5506022.html)。

下面将实现的完整思路共享

1、WebApiConfig全局处理

    /// <summary>
    /// WebApiConfig 
    /// 路由基础配置。
    /// 
    /// 
    /// 修改记录
    /// 
    ///        2016.11.01 版本:2.0 宋彪 对日期格式进行统一处理。
    ///        2016.10.30 版本:2.0 宋彪 解决json序列化时的循环引用问题。
    ///        2016.10.28 版本:2.0 宋彪 回传响应格式 $format 支持。
    ///        2016.09.01 版本:1.0 宋彪   创建。
    /// 
    /// 版本:1.0
    /// 
    /// <author>
    ///        <name>宋彪</name>
    ///        <date>2016.09.01</date>
    /// </author> 
    /// </summary>
    public static class WebApiConfig
    {
        /// <summary>
        /// 注册全局配置服务
        /// </summary>
        /// <param name="config"></param>
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            //强制https访问
            //config.Filters.Add(new ForceHttpsAttribute());
            // 统一回传格式
            config.Filters.Add(new ApiResultAttribute());
            // 发生异常时处理
            config.Filters.Add(new ApiErrorHandleAttribute());
            // ToKen身份验证过滤器 更方便 不需要在这里了 具有改标签的就会自动检查
            //config.Filters.Add(new ApiAuthFilterAttribute());
            // 解决json序列化时的循环引用问题
            config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
            //对日期格式进行统一处理
            config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(
            new IsoDateTimeConverter()
            {
                DateTimeFormat = "yyyy-MM-dd hh:mm:ss"
            }
            );

            // Web API routes 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            // 干掉XML序列化器
            //config.Formatters.Remove(config.Formatters.XmlFormatter);
            //在请求的Url加上 ?$format=xml,便可以指定响应格式
            config.Formatters.XmlFormatter.AddQueryStringMapping("$format", "xml", "application/xml");
            config.Formatters.JsonFormatter.AddQueryStringMapping("$format", "json", "application/json");
        }
    }

2、身份验证过滤器

    using DotNet.Business;
    using DotNet.Utilities;
    using DotNet.Tracking.API.Common;

    /// <summary>
    /// ApiAuthFilterAttribute
    /// 身份验证过滤器,具有ApiAuthFilterAttribute标签属性的方法会自动检查
    /// 
    /// 
    /// 修改纪录
    /// 
    /// 2016-10-11 版本:1.0 SongBiao 创建文件。   
    /// 
    /// <author>
    ///     <name>SongBiao</name>
    ///     <date>2016-10-11</date>
    /// </author>
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class ApiAuthFilterAttribute : AuthorizationFilterAttribute
    {
        /// <summary>
        /// 未授权时的提示信息
        /// </summary>
        private const string UnauthorizedMessage = "请求未授权,拒绝访问。";

        /// <summary>
        /// 权限进入
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            base.OnAuthorization(actionContext);
            // 允许匿名访问
            if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0)  
            {
                return;
            }

            string systemCode = APIOperateContext.Current.SystemCode;
            string permissionCode = APIOperateContext.Current.PermissionCode;
            string appKey = APIOperateContext.Current.AppKey;
            string appSecret = APIOperateContext.Current.AppSecret;            
            if (string.IsNullOrWhiteSpace(appKey) || string.IsNullOrWhiteSpace(appSecret))
            {
                //未验证(登录)的用户, 而且是非匿名访问,则转向登录页面  
                //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                //actionContext.Response.Content = new StringContent("<p>Unauthorized</p>", Encoding.UTF8, "text/html");
                var response = actionContext.Response= actionContext.Response?? new HttpResponseMessage();
                response.StatusCode = HttpStatusCode.Unauthorized;
                BaseResult result = new BaseResult
                {
                    Status = false,
                    StatusMessage = UnauthorizedMessage
                };
                response.Content = new StringContent(result.ToJson(), Encoding.UTF8, "application/json");
            }
            else
            {
                // 检查 AppKey 和 AppSecret
                BaseResult result = BaseServicesLicenseManager.CheckService(appKey, appSecret, false, 0, 0, systemCode, permissionCode);
                if (!result.Status)
                {
                    var response = actionContext.Response = actionContext.Response?? new HttpResponseMessage();
                    response.Content = new StringContent(result.ToJson(), Encoding.UTF8, "application/json");
                }
            }
                     
        }
    }

3、统一回传格式

    /// <summary>
    /// ApiResultAttribute
    /// 统一回传格式
    /// 
    /// 修改纪录
    /// 
    /// 2016-10-31 版本:1.0 宋彪 创建文件。
    /// 
    /// <author>
    ///     <name>宋彪</name>
    ///     <date>2016-10-31</date>
    /// </author>
    /// </summary>
    public class ApiResultAttribute : ActionFilterAttribute
    {
        /// <summary>
        /// 重写回传的处理
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            // 快件跟踪接口传的是format,不用走这里
            if (actionExecutedContext.Request.Properties.ContainsKey("format"))
            {
                // 若发生例外则不在这边处理 在异常中处理 ApiErrorHandleAttribute
                if (actionExecutedContext.Exception != null)
                    return;
                base.OnActionExecuted(actionExecutedContext);
                var result = new ApiResultModel();
                // 取得由 API 返回的状态码
                result.Status = actionExecutedContext.ActionContext.Response.StatusCode;
                // 取得由 API 返回的资料
                result.Data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result;
                // 重新封装回传格式
                actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.Status, result);
            }
        }
    }

4、全局异常处理

    using DotNet.Utilities;
    using DotNet.Tracking.API.Common;
    using DotNet.Tracking.API.Controllers;
    using DotNet.Tracking.API.Models;

    /// <summary>
    /// ApiErrorHandleAttribute
    /// 全局异常处理
    /// 
    /// 修改纪录
    /// 
    /// 2016-10-31 版本:1.0 宋彪 创建文件。
    /// 
    /// <author>
    ///     <name>宋彪</name>
    ///     <date>2016-10-31</date>
    /// </author>
    /// </summary>

    public class ApiErrorHandleAttribute : System.Web.Http.Filters.ExceptionFilterAttribute
    {
        /// <summary>
        /// 异常统一处理
        /// </summary>
        /// <param name="actionExecutedContext"></param>
        public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
        {
            base.OnException(actionExecutedContext);
            // 取得发生例外时的错误讯息
            var errorMessage = actionExecutedContext.Exception.Message;

            // 异常记录
            string parameters = APIOperateContext.GetRequestParameters();
            NLogHelper.Trace(actionExecutedContext.Exception, BaseSystemInfo.SystemCode + " ApiErrorHandleAttribute OnException 完整的请求地址及参数 : " + parameters);
            // 2016-11-01 加入异常邮件提醒
            NLogHelper.InfoMail(actionExecutedContext.Exception, BaseSystemInfo.SystemCode + " ApiErrorHandleAttribute OnException 完整的请求地址及参数 : " + parameters);

            var result = new ApiResultModel()
            {
                Status = HttpStatusCode.BadRequest,
                ErrorMessage = errorMessage
            };
            // 重新打包回传的讯息
            actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.Status, result);
        }
    }

5、接口操作的上下文

    using DotNet.Business;
    using DotNet.Model;
    using DotNet.Utilities;

    /// <summary>
    /// APIOperateContext
    /// 接口操作的上下文
    /// 跟上下文有关的一些通用的东西放在这里处理
    /// 
    /// 修改纪录
    /// 
    /// 2016-10-31 版本:1.0 宋彪 创建文件。
    /// 
    /// <author>
    ///     <name>宋彪</name>
    ///     <date>2016-10-31</date>
    /// </author>
    /// </summary>
    public class APIOperateContext
    {
        /// <summary>
        /// 获取当前 操作上下文 (为每个处理浏览器请求的服务器线程 单独创建 操作上下文)
        /// </summary>
        public static APIOperateContext Current
        {
            get
            {
                APIOperateContext oContext = CallContext.GetData(typeof(APIOperateContext).Name) as APIOperateContext;
                if (oContext == null)
                {
                    oContext = new APIOperateContext();
                    CallContext.SetData(typeof(APIOperateContext).Name, oContext);
                }
                return oContext;
            }
        }

        #region Http上下文 及 相关属性
        /// <summary>
        /// Http上下文
        /// </summary>
        public HttpContext ContextHttp
        {
            get
            {
                return HttpContext.Current;
            }
        }

        /// <summary>
        /// 输出对象
        /// </summary>
        public HttpResponse Response
        {
            get
            {
                return ContextHttp.Response;
            }
        }

        /// <summary>
        /// 请求对象
        /// </summary>
        public HttpRequest Request
        {
            get
            {
                return ContextHttp.Request;
            }
        }

        /// <summary>
        /// Session对象
        /// </summary>
        System.Web.SessionState.HttpSessionState Session
        {
            get
            {
                return ContextHttp.Session;
            }
        }
        #endregion

        /// <summary>
        /// 获取全部请求参数,get和post的 简化版
        /// </summary>
        public static string GetRequestParameters()
        {
            string query = HttpContext.Current.Request.Url.Query;
            NameValueCollection nvc;
            string baseUrl;
            ParseUrl(query, out baseUrl, out nvc);
            List<string> list = new List<string>() { };
            foreach (var key in nvc.AllKeys)
            {
                list.Add(key + "=" + nvc[key]);
            }

            var form = HttpContext.Current.Request.Form;
            foreach (var key in form.AllKeys)
            {
                list.Add(key + "=" + form[key]);
            }
            string result = HttpContext.Current.Request.Url.AbsoluteUri + "?" + string.Join("&", list);

            return result;
        }

        /// <summary>
        /// 分析 url 字符串中的参数信息
        /// 针对get请求的
        /// </summary>
        /// <param name="url">输入的 URL</param>
        /// <param name="baseUrl">输出 URL 的基础部分</param>
        /// <param name="nvc">输出分析后得到的 (参数名,参数值) 的集合</param>
        public static void ParseUrl(string url, out string baseUrl, out NameValueCollection nvc)
        {
            if (url == null)
            {
                throw new ArgumentNullException("url");
            }
            nvc = new NameValueCollection();
            baseUrl = "";
            if (url == "")
            {
                return;
            }
            int questionMarkIndex = url.IndexOf('?');
            if (questionMarkIndex == -1)
            {
                baseUrl = url;
                return;
            }
            baseUrl = url.Substring(0, questionMarkIndex);
            if (questionMarkIndex == url.Length - 1)
            {
                return;
            }
            string ps = url.Substring(questionMarkIndex + 1);
            // 开始分析参数对  
            Regex re = new Regex(@"(^|&)?(\w+)=([^&]+)(&|$)?", RegexOptions.Compiled);
            MatchCollection mc = re.Matches(ps);
            foreach (Match m in mc)
            {
                nvc.Add(m.Result("$2").ToLower(), m.Result("$3"));
            }
        }

        /// <summary>
        /// 系统编号
        /// </summary>
        public string SystemCode
        {
            get
            {
                return Request["systemCode"] ?? "Base";
            }
        }

        /// <summary>
        /// 权限编号
        /// </summary>
        public string PermissionCode
        {
            get
            {
                return Request["permissionCode"];
            }
        }

        /// <summary>
        /// 访问接口的应用传来AppKey
        /// </summary>
        public string AppKey
        {
            get
            {
                return Request["appKey"];
            }
        }

        /// <summary>
        /// 访问接口的应用传来AppSecret
        /// </summary>
        public string AppSecret
        {
            get
            {
                return Request["appSecret"];
            }
        }

        private BaseUserInfo _userInfo = null;
        /// <summary>
        /// 获取当前用户
        /// 通过接口AppKey和AppSecret获取的用户
        /// </summary>
        /// <returns></returns>
        public BaseUserInfo UserInfo
        {
            get
            {
                BaseUserInfo userInfo = null;
                BaseUserEntity userEntity = BaseUserManager.GetObjectByCodeByCache(AppKey);
                if (userEntity != null)
                {
                    if (BaseServicesLicenseManager.CheckServiceByCache(userEntity.Id, AppSecret))
                    {
                        userInfo = new BaseUserInfo();
                        userInfo.Id = userEntity.Id;
                        userInfo.RealName = userEntity.RealName;
                        userInfo.UserName = userEntity.UserName;
                        userInfo.IPAddress = Utilities.GetIPAddress(true);
                    }
                }
                return userInfo;
            }
        }

        #region 业务库连接
        /// <summary>
        /// 业务库连接
        /// </summary>
        public static IDbHelper BusinessDbHelper
        {
            get
            {
                return DbHelperFactory.GetHelper(BaseSystemInfo.BusinessDbType, BaseSystemInfo.BusinessDbConnection);
            }
        }
        #endregion

        #region 用户中心库连接
        /// <summary>
        /// 用户中心库连接
        /// </summary>
        public static IDbHelper UserCenterDbHelper
        {
            get
            {
                return DbHelperFactory.GetHelper(BaseSystemInfo.UserCenterDbType, BaseSystemInfo.UserCenterDbConnection);
            }
        }
        #endregion


    }

7、统一回传格式实体

    /// <summary>
    /// ApiResultModel
    /// 统一回传格式实体
    /// 
    /// 修改纪录
    /// 
    /// 2016-10-31 版本:1.0 宋彪 创建文件。
    /// 
    /// <author>
    ///     <name>宋彪</name>
    ///     <date>2016-10-31</date>
    /// </author>
    /// </summary>
    public class ApiResultModel
    {
        public HttpStatusCode Status { get; set; }

        //public JsonResult<T> Data { get; set; }
       public object Data { get; set; }
        public string ErrorMessage { get; set; }
    }

8、留言相关接口

    /// <summary>
    /// MessageBookController
    /// 留言相关接口
    /// 
    /// 修改纪录
    /// 
    /// 2016-10-31 版本:1.0 宋彪 创建文件。
    /// 
    /// <author>
    ///     <name>宋彪</name>
    ///     <date>2016-10-31</date>
    /// </author>
    /// </summary>
    [ApiAuthFilter]
    public class CustomerMessageController : ApiController
    {
        /// <summary>
        /// 保存单号留言信息
        /// </summary>
        /// <param name="messageBook"></param>
        /// <returns></returns>
        [HttpPost]
        //[AllowAnonymous] 不需要验证的就加这个标签
        public IHttpActionResult Add([FromBody]MsgbookCusEntity messageBook)
        {
            BaseResult baseResult = new BaseResult();
            if (string.IsNullOrWhiteSpace(messageBook.SystemFrom))
            {
                baseResult.Status = false;
                baseResult.StatusMessage = "SystemFrom参数不可为空";
            }
            else
            {
                try
                {
                    MsgbookCusManager manager = new MsgbookCusManager(APIOperateContext.BusinessDbHelper, APIOperateContext.Current.UserInfo);
                    MsgbookCusEntity model = new MsgbookCusEntity();
                    model.Id = Guid.NewGuid().ToString("N");
                    model.Message = messageBook.Message;
                    model.SendEmail = messageBook.SendEmail;
                    model.SendTelephone = messageBook.SendTelephone;
                    model.Message = messageBook.Message;
                    model.BillCode = messageBook.BillCode;
                    model.SystemFrom = messageBook.SystemFrom;
                    model.DeletionStateCode = 0;
                    manager.Add(model, false, false);

                    baseResult.Status = true;
                    baseResult.StatusMessage = "添加成功。";
                }
                catch (Exception ex)
                {
                    NLogHelper.Warn(ex, "CustomerMessageController AddBillMessage 异常");
                    baseResult.Status = false;
                    baseResult.StatusMessage = "异常:" + ex.Message;
                }
            }

            return Ok(baseResult);
        }

        /// <summary>
        /// 获取某个单号的留言
        /// </summary>
        /// <param name="billCode"></param>
        /// <returns></returns>
        [HttpGet]
        public IHttpActionResult GetList(string billCode)
        {
            JsonResult<List<MsgbookCusEntity>> jsonResult = new JsonResult<List<MsgbookCusEntity>>();
            try
            {
                MsgbookCusManager manager = new MsgbookCusManager(APIOperateContext.BusinessDbHelper, APIOperateContext.Current.UserInfo);
                List<MsgbookCusEntity> list = new List<MsgbookCusEntity>();
                list = manager.GetList<MsgbookCusEntity>(new KeyValuePair<string, object>(MsgbookCusEntity.FieldBillCode, billCode)
                , new KeyValuePair<string, object>(MsgbookCusEntity.FieldDeletionStateCode, 0));

                jsonResult.Status = true;
                jsonResult.RecordCount = list.Count;
                jsonResult.Data = list;
                jsonResult.StatusMessage = "获取成功";
            }
            catch (Exception ex)
            {
                NLogHelper.Warn(ex, "CustomerMessageController AddBillMessage 异常");
                jsonResult.Status = false;
                jsonResult.StatusMessage = "异常:" + ex.Message;
            }

            return Ok(jsonResult);
        }
    }

9、接口调用方法

        /// <summary>
        /// 测试留言接口调用
        /// </summary>
        /// <returns></returns>
        public ActionResult AddCustomerMessage()
        {
            string url = "http://192.168.1.88:808/api/CustomerMessage/Add?";
            WebClient webClient = new WebClient();
            NameValueCollection postValues = new NameValueCollection();

            postValues.Add("Message", "填写您的留言内容吧");
            postValues.Add("SendEmail", "youemail@qq.com");
            postValues.Add("SendTelephone", "021-60375335");
            postValues.Add("Code", "661137858");
            postValues.Add("AppKey", "wssavbcn");
            postValues.Add("AppSecret", "350e66b1e6564b0a817163erwwwwe8");
            postValues.Add("SystemFrom", "官网");

            byte[] responseArray = webClient.UploadValues(url, postValues);
            string response = Encoding.UTF8.GetString(responseArray);

            return Content(response);
        }

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MVC WebApi 用户权限验证及授权DEMO 前言:Web 用户的身份验证,及页面操作权限验证是B/S系统的基础功能,一个功能复杂的业务应用系统,通过角色授权来控制用户访问,本文通过Form认证,Mvc的Controller基类及Action的权限验证来实现Web系统登录,Mvc前端权限校验以及WebApi服务端的访问校验功能。 1 Web Form认证介绍 Web应用的访问方式因为是基于浏览器的Http地址请求,所以需要验证用户身份的合法性。目前常见的方式是Form认证,其处理逻辑描述如下: 1) 用户首先要在登录页面输入用户名和密码,然后登录系统,获取合法身份的票据,再执行后续业务处理操作; 2) 用户在没有登录的情况下提交Http页面访问请求,如果该页面不允许匿名访问,则直接跳转到登录页面; 3) 对于允许匿名访问的页面请求,系统不做权限验证,直接处理业务数据,并返回给前端; 4) 对于不同权限要求的页面Action操作,系统需要校验用户角色,计算权限列表,如果请求操作在权限列表中,则正常访问,如果不在权限列表中,则提示“未授权的访问操作”到异常处理页面。 2 WebApi 服务端Basic 方式验证 WebApi服务端接收访问请求,需要做安全验证处理,验证处理步骤如下: 1) 如果是合法的Http请求,在Http请求头中会有用户身份的票据信息,服务端会读取票据信息,并校验票据信息是否完整有效,如果满足校验要求,则进行业务数据的处理,并返回给请求发起方; 2) 如果没有票据信息,或者票据信息不是合法的,则返回“未授权的访问”异常消息给前端,由前端处理此异常。 3 登录及权限验证流程 1) 用户打开浏览器,并在地址栏中输入页面请求地址,提交; 2) 浏览器解析Http请求,发送到Web服务器;Web服务器验证用户请求,首先判断是否有登录的票据信息; 3) 用户没有登录票据信息,则跳转到登录页面; 4) 用户输入用户名和密码信息; 5) 浏览器提交登录表单数据给Web服务器; 6) Web服务需要验证用户名和密码是否匹配,发送api请求api服务器; 7) api用户账户服务根据用户名,读取存储在数据库中的用户资料,判断密码是否匹配; 7.1)如果用户名和密码不匹配,则提示密码错误等信息,然该用户重新填写登录资料; 7.2)如果验证通过,则保存用户票据信息; 8) 接第3步,如果用户有登录票据信息,则跳转到用户请求的页面; 9) 验证用户对当前要操作的页面或页面元素是否有权限操作,首先需要发起api服务请求,获取用户的权限数据; 10). api用户权限服务根据用户名,查找该用户的角色信息,并计算用户权限列表,封装为Json数据并返回; 11). 当用户有权限操作页面或页面元素时,跳转到页面,并由页面Controller提交业务数据处理请求api服务器; 如果用户没有权限访问该页面或页面元素时,则显示“未授权的访问操作”,跳转到系统异常处理页面。 12). api业务服务处理业务逻辑,并将结果以Json 数据返回; 13). 返回渲染后的页面给浏览器前端,并呈现业务数据到页面; 14). 用户填写业务数据,或者查找业务数据; 15). 当填写或查找完业务数据后,用户提交表单数据; 16). 浏览器脚本提交get,post等请求给web服务器,由web服务器再次解析请求操作,重复步骤2的后续流程; 17). 当api服务器验证用户身份是,没有可信用户票据,系统提示“未授权的访问操作”,跳转到系统异常处理页面。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值