asp.net web api 授权功能

1、重写授权方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Security;
using WebAPI.Models;
using WebAPI.Toolkit;

namespace WebAPI.Filter
{
    /// <summary>
    /// 授权
    /// </summary>
    public class RequestAuthorizeAttribute:AuthorizeAttribute
    {
        /// <summary>
        /// 重写授权方法,加入自定义的Ticket验证
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var isActionAnonymous = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>().Any(a => a is AllowAnonymousAttribute);
            var isControllerAnonymous = actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().OfType<AllowAnonymousAttribute>().Any(a => a is AllowAnonymousAttribute);

            //如果控制器和动作上允许匿名访问
            if (isControllerAnonymous || isActionAnonymous)
            {
                base.OnAuthorization(actionContext);
            }
            else
            {
                //从http请求的头里面获取身份验证信息,验证是否是请求发起方的ticket
                var headers = actionContext.Request.Headers;
                if ((headers.Authorization != null) && (headers.Authorization.Parameter != null))
                {
                    //解密用户ticket,并校验用户名密码是否匹配
                    if (ValidateTicket(headers))
                    {
                        base.IsAuthorized(actionContext);
                    }
                    else
                    {
                        HandleUnauthorizedRequest(actionContext);
                    }
                }
                else
                {
                    HandleUnauthorizedRequest(actionContext);
                }
            }
        }

        /// <summary>
        /// 校验用户身份
        /// </summary>
        /// <param name="headers">请求头</param>
        /// <returns></returns>
        private bool ValidateTicket(HttpRequestHeaders headers)
        {
            //解密Ticket
            var strTicket = FormsAuthentication.Decrypt(headers.Authorization.Parameter).UserData;

            //从Ticket里面获取用户名和密码
            var index = strTicket.IndexOf("&");
            string userName = strTicket.Substring(0, index);
            //string password = strTicket.Substring(index + 1);

            //获取令牌
            var token = headers.GetValues("AppId").FirstOrDefault();
            
            //根据令牌和用户名得到键
            string key = string.Format("{0}_{1}", token, userName);

            //根据缓存键拿到用户信息
            var userInfo = CacheHelper.GetCache(key);
            if (userInfo==null)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// 重写授权失败响应
        /// </summary>
        /// <param name="actionContext"></param>
        protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
        {
            base.HandleUnauthorizedRequest(actionContext);

            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden, new ResultModel { Status = false, Data = null, ErrorMessage = "您没有权限访问资源" });
        }


    }
}

2、基类控制器添加授权特性

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using WebAPI.Filter;
using System.IO;
using WebAPI.Models;
using System.Text;
using WebAPI.Toolkit;

namespace WebAPI.Controllers
{
    /// <summary>
    /// 接口基类
    /// </summary>
    [Result]
    [RequestAuthorize]
    public class BaseApiController : ApiController
    {
        /// <summary>
        /// 接口令牌
        /// </summary>
        protected string _token;
        /// <summary>
        /// 
        /// </summary>
        protected ApiClient _client;
        /// <summary>
        /// 接口配置
        /// </summary>
        public List<ApiModel> Apis
        {
            get
            {
                string path = string.Format("{0}/config.json", AppDomain.CurrentDomain.BaseDirectory);
                string result = "";
                if (File.Exists(path))
                {
                    result = File.ReadAllText(path);
                }
                return JsonConvert.DeserializeObject<List<ApiModel>>(result);
            }
        }

        /// <summary>
        /// 重写接口执行方法
        /// </summary>
        /// <param name="controllerContext">控制器上下文</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext,CancellationToken cancellationToken)
        {
            try
            {
                var header = controllerContext.Request.Headers;
                _token = header.GetValues("AppId").FirstOrDefault();//接口令牌
                string baseAddress = Apis.FirstOrDefault(a => a.Id == _token).Url;

                _client = new ApiClient(baseAddress, _token, header);
            }
            catch 
            {
                var response = new HttpResponseMessage(HttpStatusCode.OK);
                response.Content = new StringContent(JsonConvert.SerializeObject(new ResultModel() { ErrorMessage = "未经授权" }), Encoding.UTF8, "application/json");

                var source = new TaskCompletionSource<HttpResponseMessage>();
                source.SetResult(response);
                return source.Task;
            }
            return base.ExecuteAsync(controllerContext, cancellationToken);
        }

    }
}

3.登录

using AppViewModel;
using AppViewModel.System;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Security;
using WebAPI.Models;
using WebAPI.Toolkit;

namespace WebAPI.Controllers.System
{
    /// <summary>
    /// 用户信息
    /// </summary>
    public class UserController : BaseApiController
    {
        #region 登录
        
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="viewModel">登录实体</param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        public UserViewModel Login([FromBody]LoginViewModel viewModel)
        {
            var result = _client.Post(viewModel, "api/SysUser/Login");

            string ticket = string.Empty;
            string key = string.Format("{0}_{1}", _token, viewModel.UserName);

            var userTicket = CacheHelper.GetCache(key);
            if (userTicket == null)
            {
                //生成票据,通常是对用户名和密码进行编码,此处通过用户名和用户名编码进行混淆
                var formTicket = new FormsAuthenticationTicket(0, viewModel.UserName, DateTime.Now, DateTime.Now.AddHours(8), true, string.Format("{0}&{1}", viewModel.UserName, viewModel.UserName), FormsAuthentication.FormsCookiePath);
                //对票据进行加密
                ticket = FormsAuthentication.Encrypt(formTicket);
                CacheHelper.SetCache(key, ticket, DateTime.Now.AddHours(8));
            }
            else
            {
                ticket = userTicket.ToString();
            }

            var userModel = new UserViewModel
            {
                Id = result.Data.Id,
                OrgId = result.Data.所属机构Id,
                Name = result.Data.真实姓名,
                Account = result.Data.用户名,
                Token = ticket
            };

            //获取App用户主题
            var userTheme = _client.Get(string.Format("api/SysUserClientConfig/GetByUid?Uid={0}", userModel.Id));
            userModel.ThemeName = (userTheme.Data == null) ? "" : userTheme.Data.ThemeName;

            GetUserPosts(userModel);

            GetUserModules(userModel);

            return userModel;
        }

        /// <summary>
        /// 获取用户岗位列表
        /// </summary>
        /// <param name="userModel"></param>
        private void GetUserPosts(UserViewModel userModel)
        {
            var postResult = _client.Get(string.Format("api/SysBasicPost/GetByUid?Uid={0}", userModel.Id));
            if (postResult.Data == null)
            {
                return;
            }
            foreach (var item in postResult.Data)
            {
                userModel.UserPosts.Add(new PostViewModel() { Id = item.Id, Name = item.岗位名称 });
            }
        }

        /// <summary>
        /// 获取用户模块列表
        /// </summary>
        /// <param name="userModel"></param>
        private void GetUserModules(UserViewModel userModel)
        {
            if (userModel.UserPosts.Count < 1 || userModel.UserPosts.Count > 1)
            {
                return;
            }

            //默认如果当前用户只有一个岗位就加载用户的模块列表
            var result = _client.Get(string.Format("api/SysModule/Get?Gid={0}&Uid={1}&Pid={2}", userModel.OrgId, userModel.Id, userModel.UserPosts.FirstOrDefault().Id));
            if (result.Data == null)
            {
                return;
            }

            var viewModels = new List<ModuleViewModel>();
            foreach (var item in result.Data)
            {
                viewModels.Add(new ModuleViewModel()
                {
                    Id = item.Id,
                    PId = item.Pid,
                    Name = item.别名,
                    Url = item.默认入口页面,
                    IconUrl = item.图标URL
                });
            }

            userModel.UserModules = viewModels.Where(a => a.PId == null).ToList();
            if (userModel.UserModules == null || userModel.UserModules.Count < 1)
            {
                return;
            }

            //包装模块列表
            foreach (var item in userModel.UserModules)
            {
                item.Childs = viewModels.Where(a => a.PId == item.Id).ToList();
            }
        }

        #endregion

        /// <summary>
        /// 获取用户列表
        /// </summary>
        /// <param name="queryModel">机构Id、岗位Id</param>
        /// <returns></returns>
        [HttpPost]
        public List<UserViewModel> GetUsers([FromBody]QueryBaseModel queryModel)
        {
            var result = _client.Get(string.Format("api/SysUser/GetByGidAndPid?Gid={0}&Pid={1}", queryModel.OrgId, queryModel.PostId));
            if (result.Data==null)
            {
                return null;
            }

            var viewModels = new List<UserViewModel>();
            foreach (var item in result.Data)
            {
                viewModels.Add(new UserViewModel(){ Id = item.Id, Name = item.真实姓名 });
            }
            return viewModels;
        }

        /// <summary>
        /// 获取用户详情
        /// </summary>
        /// <param name="userId">用户Id</param>
        /// <returns></returns>
        [HttpPost]
        public UserViewModel GetUserInfo([FromBody]int userId)
        {
            var result = _client.Get(string.Format("api/SysUser/GetById?Id={0}", userId));
            if (result.Data == null)
            {
                return null;
            }

            var viewModel = new UserViewModel()
            {
                Id = result.Data.Id,
                Name = result.Data.真实姓名,
                Tel = result.Data.手机,
                Sex = result.Data.性别,
                Email = result.Data.Email
            };
            return viewModel;
        }


    }
}

登录参考基础认证的方式,但是为了和网上基础认证做区别没有对用户名和密码进行加密,而是针对用户名和用户名进行加密,加密的核心我认为是打破常规为原则。

如果某个接口不需要授权,则控制器或动作上方添加 [AllowAnonymous] 特性。

注意点:如果重写授权方法没有处理好匿名特性的逻辑,会导致不该认证的接口,打上匿名特性也照样走认证流程。

 

posted @ 2018-11-09 15:59  江宁织造  阅读(1070)  评论(0编辑  收藏  举报