[关闭]
@xuxuzhaozhao 2017-12-21T08:19:49.000000Z 字数 6417 阅读 465

WebApi 前后端分离

webapi


AspNet WebApi 前后端分离,由于以后的ERP项目都将采用单页模式,索性完成前后端分离,其中最重要的就是授权相关的与之前采用Forms授权不同.
现在的授权模式将采用OWIN OAuth的方式,前台将登录获取token,然后将token放置在请求头.

1、使用Nuget包管理器安装稳定的最新的如下包:

  1. 1. Microsoft.AspNet.WebApi.Owin;
  2. 2. Microsoft.Owin.Host.SystemWeb;
  3. 3. Microsoft.AspNet.Identity.Owin;
  4. 4. Microsoft.Owin.Cors;

2、在webapi项目的根文件夹下创建OWIN Startup类

  1. using Microsoft.Owin;
  2. using Microsoft.Owin.Security.OAuth;
  3. using Owin;
  4. using System;
  5. using System.Web.Http;
  6. [assembly: OwinStartup(typeof(WebApi令牌身份验证.Startup))]
  7. namespace WebApi令牌身份验证
  8. {
  9. public class Startup
  10. {
  11. public void Configuration(IAppBuilder app)
  12. {
  13. HttpConfiguration config = new HttpConfiguration();
  14. ConfigureOAuth(app);
  15. WebApiConfig.Register(config);
  16. app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
  17. app.UseWebApi(config);
  18. }
  19. public void ConfigureOAuth(IAppBuilder app)
  20. {
  21. OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
  22. {
  23. AllowInsecureHttp = true,
  24. TokenEndpointPath = new PathString("/token"),
  25. AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
  26. Provider = new SimpleAuthorizationServerProvider()
  27. };
  28. app.UseOAuthAuthorizationServer(OAuthServerOptions);
  29. app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
  30. }
  31. }
  32. }

3、根目录中新建Providers文件夹=>新建AuthorizationServerProvider.cs

  1. using Microsoft.Owin.Security.OAuth;
  2. using System.Security.Claims;
  3. using System.Threading.Tasks;
  4. using YiCheng.Common.DAL;
  5. namespace YiCheng.Web.Api.Providers
  6. {
  7. /// <summary>
  8. /// 授权access_token
  9. /// </summary>
  10. public class YAuthorizationServerProvider : OAuthAuthorizationServerProvider
  11. {
  12. public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
  13. {
  14. context.Validated();
  15. }
  16. /// <summary>
  17. /// 返回Token令牌
  18. /// </summary>
  19. /// <param name="context"></param>
  20. /// <returns></returns>
  21. public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
  22. {
  23. context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
  24. //验证码(暂未实现)
  25. //var validateCode = context.Scope[0];
  26. //对用户名、密码进行数据校验
  27. var sql = $"SELECT COUNT(1) FROM 用户表 WHERE 账号 = @用户名 AND 密码 = @密码;";
  28. var result = YDBHelper.ExecuteScalar2(sql, new
  29. {
  30. 用户名 = context.UserName,
  31. 密码 = context.Password
  32. }).ToString();
  33. if (int.Parse(result) <= 0)
  34. {
  35. context.SetError("登录错误.", "用户名或者密码错误!");
  36. return;
  37. }
  38. var identity = new ClaimsIdentity(context.Options.AuthenticationType);
  39. identity.AddClaim(new Claim("yuser", context.UserName));
  40. context.Validated(identity);
  41. }
  42. }
  43. }

4、根目录中新建Filters文件夹=>新建YAuthorizeFilterAttribute.cs
继承AuthorizeAttribute,重写其中的授权方法,及对应的处理未授权的方法

  1. //=====================================================
  2. // Copyright © 2017-2018 xuxuzhaozhao
  3. // All rights reserved
  4. // GUID: 7b80855a-8d31-4dc1-b9b7-72a5b2d29536
  5. // CLR版本: 4.0.30319.42000
  6. // 新建项输入的名称: AxuzerFilter
  7. // 机器名称: WIN10-20170502F
  8. // 当前登录用户名: xuxuzhaozhao
  9. //======================================================
  10. using System.Configuration;
  11. using System.Linq;
  12. using System.Net;
  13. using System.Net.Http;
  14. using System.Security.Claims;
  15. using System.Text;
  16. using System.Web;
  17. using System.Web.Helpers;
  18. using System.Web.Http;
  19. using System.Web.Http.Controllers;
  20. using YiCheng.Business.Model;
  21. using YiCheng.Common.DAL;
  22. using YiCheng.Common.Pagination;
  23. using YiCheng.Common.Tools;
  24. using YiCheng.Model.OutputModel;
  25. // ReSharper disable All
  26. namespace YiCheng.Web.Api
  27. {
  28. /// <summary>
  29. /// 权限管理筛选器
  30. /// </summary>
  31. public class YAuthorizeFilterAttribute : AuthorizeAttribute
  32. {
  33. private readonly string SUPERADMIN = ConfigurationManager.AppSettings["SUPERADMIN"];
  34. /// <summary>
  35. /// 身份验证
  36. /// </summary>
  37. /// <param name="actionContext"></param>
  38. public override void OnAuthorization(HttpActionContext actionContext)
  39. {
  40. var username = "";
  41. var url = actionContext.RequestContext.Url.Request.RequestUri.AbsolutePath.ToUpper();
  42. username = HttpContext.Current.User.Identity.Name;
  43. #region 前后端彻底分离,使用OAuth来进行授权(2个小时过期时间)
  44. if (string.IsNullOrEmpty(username))
  45. {
  46. var x = actionContext.RequestContext.Principal.Identity;
  47. var identity = x as ClaimsIdentity;
  48. username = identity?.Claims.Where(t => t.Type == "yuser").Select(t => t.Value).SingleOrDefault();
  49. }
  50. #endregion
  51. //统一约定,当前页面
  52. var currentPage = HttpContext.Current.Request["currentPage"]?.ToUpper();
  53. //统一约定,订单编号
  54. var orderId = HttpContext.Current.Request["orderId"];
  55. if (!string.IsNullOrEmpty(username))
  56. {
  57. var user = new YUser(username);
  58. HttpContext.Current.User = user;
  59. if (IsAuth(url, user, currentPage, orderId))
  60. {
  61. base.IsAuthorized(actionContext);//为此请求授权
  62. }
  63. else
  64. {
  65. HandleUnauthorizedRequest(actionContext);
  66. }
  67. }
  68. //如果取不到身份验证信息,并且不允许匿名访问,则返回未验证401
  69. else
  70. {
  71. var attributes = actionContext
  72. .ActionDescriptor
  73. .GetCustomAttributes<AllowAnonymousAttribute>()
  74. .OfType<AllowAnonymousAttribute>();
  75. bool isAnonymous = attributes.Any(a => a is AllowAnonymousAttribute);
  76. if (isAnonymous) base.OnAuthorization(actionContext);
  77. else HandleUnauthorizedRequest(actionContext);
  78. }
  79. }
  80. /// <summary>
  81. /// 校验该用户是否有权操作此控制器
  82. /// </summary>
  83. /// <param name="url"></param>
  84. /// <param name="user"></param>
  85. /// <param name="currentPage"></param>
  86. /// <param name="orderId"></param>
  87. /// <returns></returns>
  88. private bool IsAuth(string url, YUser user, string currentPage, string orderId)
  89. {
  90. if (user.角色.Contains(SUPERADMIN)) return true;
  91. var flag = true;
  92. //查询该账号是否有权限请求这个url
  93. using (var db = new BaoJiaDb())
  94. {
  95. //该Url是否允许匿名访问
  96. var 匿名url = $"SELECT TOP 1 匿名访问 FROM 请求Url表 WHERE UPPER(请求Url) = @请求Url";
  97. var t = YDBHelper.ExecuteScalar2(匿名url, new { 请求Url = url })?.ToString();
  98. if (string.IsNullOrEmpty(t)) throw new YException($"当前页面:{currentPage?.ToLower()},url:{url?.ToLower()},没有加到请求Url字典表中,请检查!", 98);
  99. var 匿名访问 = bool.Parse(t);
  100. if (匿名访问) return true;
  101. var sql1 = $@"SELECT COUNT(1) FROM 请求权限管理表 ua
  102. LEFT JOIN dbo.请求Url表 u ON ua.请求UrlGUID = u.GUID
  103. WHERE UPPER(u.请求Url) = @请求Url AND ua.种类 = '角色' AND UPPER(u.页面名) = @页面名
  104. AND ua.角色 IN ({YTools.Brackets(user.角色)}) AND ua.企业名称 = @企业名称";
  105. var 条件1 = int.Parse(YDBHelper.ExecuteScalar2(sql1, new
  106. {
  107. 请求Url = url,
  108. 页面名 = currentPage,
  109. 企业名称 = user.企业名称
  110. }).ToString()) > 0;
  111. var sql2 = $@"SELECT COUNT(1) FROM 请求权限管理表 ua
  112. LEFT JOIN dbo.请求Url表 u ON ua.请求UrlGUID = u.GUID
  113. WHERE UPPER(u.请求Url) = @请求Url AND ua.种类 = '账号' AND UPPER(u.页面名) = @页面名
  114. AND ua.角色 = @账号 AND ua.企业名称 = @企业名称";
  115. var 条件2 = int.Parse(YDBHelper.ExecuteScalar2(sql1, new
  116. {
  117. 请求Url = url,
  118. 账号 = user.账号,
  119. 页面名 = currentPage,
  120. 企业名称 = user.企业名称
  121. }).ToString()) > 0;
  122. //只要该账号所属的其中一个角色允许访问该url,则该账号就可访问
  123. if (flag == true && (条件1 || 条件2))
  124. return true;
  125. }
  126. throw new YException($"当前页面:{currentPage?.ToLower()}的url{url?.ToLower()},未授权不可访问", 98);
  127. }
  128. /// <summary>
  129. /// 处理未授权请求
  130. /// </summary>
  131. /// <param name="filterContext"></param>
  132. protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
  133. {
  134. base.HandleUnauthorizedRequest(filterContext);
  135. var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage();
  136. response.StatusCode = HttpStatusCode.Forbidden;
  137. //Result类 可自行创建,具体为返回的content信息
  138. var content = new YResult();
  139. content.Failure(99, "未授权用户或已过期请重新登录.");
  140. content.Data = "/";
  141. response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
  142. }
  143. }
  144. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注