@xuxuzhaozhao
2017-12-21T08:19:49.000000Z
字数 6417
阅读 465
webapi
AspNet WebApi 前后端分离,由于以后的ERP项目都将采用单页模式,索性完成前后端分离,其中最重要的就是授权相关的与之前采用Forms授权不同.
现在的授权模式将采用OWIN OAuth的方式,前台将登录获取token,然后将token放置在请求头.
1、使用Nuget包管理器安装稳定的最新的如下包:
1. Microsoft.AspNet.WebApi.Owin;
2. Microsoft.Owin.Host.SystemWeb;
3. Microsoft.AspNet.Identity.Owin;
4. Microsoft.Owin.Cors;
2、在webapi项目的根文件夹下创建OWIN Startup类
using Microsoft.Owin;
using Microsoft.Owin.Security.OAuth;
using Owin;
using System;
using System.Web.Http;
[assembly: OwinStartup(typeof(WebApi令牌身份验证.Startup))]
namespace WebApi令牌身份验证
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
}
3、根目录中新建Providers文件夹=>新建AuthorizationServerProvider.cs
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using System.Threading.Tasks;
using YiCheng.Common.DAL;
namespace YiCheng.Web.Api.Providers
{
/// <summary>
/// 授权access_token
/// </summary>
public class YAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
/// <summary>
/// 返回Token令牌
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
//验证码(暂未实现)
//var validateCode = context.Scope[0];
//对用户名、密码进行数据校验
var sql = $"SELECT COUNT(1) FROM 用户表 WHERE 账号 = @用户名 AND 密码 = @密码;";
var result = YDBHelper.ExecuteScalar2(sql, new
{
用户名 = context.UserName,
密码 = context.Password
}).ToString();
if (int.Parse(result) <= 0)
{
context.SetError("登录错误.", "用户名或者密码错误!");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("yuser", context.UserName));
context.Validated(identity);
}
}
}
4、根目录中新建Filters文件夹=>新建YAuthorizeFilterAttribute.cs
继承AuthorizeAttribute,重写其中的授权方法,及对应的处理未授权的方法
//=====================================================
// Copyright © 2017-2018 xuxuzhaozhao
// All rights reserved
// GUID: 7b80855a-8d31-4dc1-b9b7-72a5b2d29536
// CLR版本: 4.0.30319.42000
// 新建项输入的名称: AxuzerFilter
// 机器名称: WIN10-20170502F
// 当前登录用户名: xuxuzhaozhao
//======================================================
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Claims;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Http;
using System.Web.Http.Controllers;
using YiCheng.Business.Model;
using YiCheng.Common.DAL;
using YiCheng.Common.Pagination;
using YiCheng.Common.Tools;
using YiCheng.Model.OutputModel;
// ReSharper disable All
namespace YiCheng.Web.Api
{
/// <summary>
/// 权限管理筛选器
/// </summary>
public class YAuthorizeFilterAttribute : AuthorizeAttribute
{
private readonly string SUPERADMIN = ConfigurationManager.AppSettings["SUPERADMIN"];
/// <summary>
/// 身份验证
/// </summary>
/// <param name="actionContext"></param>
public override void OnAuthorization(HttpActionContext actionContext)
{
var username = "";
var url = actionContext.RequestContext.Url.Request.RequestUri.AbsolutePath.ToUpper();
username = HttpContext.Current.User.Identity.Name;
#region 前后端彻底分离,使用OAuth来进行授权(2个小时过期时间)
if (string.IsNullOrEmpty(username))
{
var x = actionContext.RequestContext.Principal.Identity;
var identity = x as ClaimsIdentity;
username = identity?.Claims.Where(t => t.Type == "yuser").Select(t => t.Value).SingleOrDefault();
}
#endregion
//统一约定,当前页面
var currentPage = HttpContext.Current.Request["currentPage"]?.ToUpper();
//统一约定,订单编号
var orderId = HttpContext.Current.Request["orderId"];
if (!string.IsNullOrEmpty(username))
{
var user = new YUser(username);
HttpContext.Current.User = user;
if (IsAuth(url, user, currentPage, orderId))
{
base.IsAuthorized(actionContext);//为此请求授权
}
else
{
HandleUnauthorizedRequest(actionContext);
}
}
//如果取不到身份验证信息,并且不允许匿名访问,则返回未验证401
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);
}
}
/// <summary>
/// 校验该用户是否有权操作此控制器
/// </summary>
/// <param name="url"></param>
/// <param name="user"></param>
/// <param name="currentPage"></param>
/// <param name="orderId"></param>
/// <returns></returns>
private bool IsAuth(string url, YUser user, string currentPage, string orderId)
{
if (user.角色.Contains(SUPERADMIN)) return true;
var flag = true;
//查询该账号是否有权限请求这个url
using (var db = new BaoJiaDb())
{
//该Url是否允许匿名访问
var 匿名url = $"SELECT TOP 1 匿名访问 FROM 请求Url表 WHERE UPPER(请求Url) = @请求Url";
var t = YDBHelper.ExecuteScalar2(匿名url, new { 请求Url = url })?.ToString();
if (string.IsNullOrEmpty(t)) throw new YException($"当前页面:{currentPage?.ToLower()},url:{url?.ToLower()},没有加到请求Url字典表中,请检查!", 98);
var 匿名访问 = bool.Parse(t);
if (匿名访问) return true;
var sql1 = $@"SELECT COUNT(1) FROM 请求权限管理表 ua
LEFT JOIN dbo.请求Url表 u ON ua.请求UrlGUID = u.GUID
WHERE UPPER(u.请求Url) = @请求Url AND ua.种类 = '角色' AND UPPER(u.页面名) = @页面名
AND ua.角色 IN ({YTools.Brackets(user.角色)}) AND ua.企业名称 = @企业名称";
var 条件1 = int.Parse(YDBHelper.ExecuteScalar2(sql1, new
{
请求Url = url,
页面名 = currentPage,
企业名称 = user.企业名称
}).ToString()) > 0;
var sql2 = $@"SELECT COUNT(1) FROM 请求权限管理表 ua
LEFT JOIN dbo.请求Url表 u ON ua.请求UrlGUID = u.GUID
WHERE UPPER(u.请求Url) = @请求Url AND ua.种类 = '账号' AND UPPER(u.页面名) = @页面名
AND ua.角色 = @账号 AND ua.企业名称 = @企业名称";
var 条件2 = int.Parse(YDBHelper.ExecuteScalar2(sql1, new
{
请求Url = url,
账号 = user.账号,
页面名 = currentPage,
企业名称 = user.企业名称
}).ToString()) > 0;
//只要该账号所属的其中一个角色允许访问该url,则该账号就可访问
if (flag == true && (条件1 || 条件2))
return true;
}
throw new YException($"当前页面:{currentPage?.ToLower()}的url{url?.ToLower()},未授权不可访问", 98);
}
/// <summary>
/// 处理未授权请求
/// </summary>
/// <param name="filterContext"></param>
protected override void HandleUnauthorizedRequest(HttpActionContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
var response = filterContext.Response = filterContext.Response ?? new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Forbidden;
//Result类 可自行创建,具体为返回的content信息
var content = new YResult();
content.Failure(99, "未授权用户或已过期请重新登录.");
content.Data = "/";
response.Content = new StringContent(Json.Encode(content), Encoding.UTF8, "application/json");
}
}
}