c# webapi 过滤器token、sign认证、访问日志
1、token认证
服务端登录成功后分配token字符串。记录缓存服务器,可设置有效期
var token = Guid.NewGuid().ToString().Replace("-", ""); var expire = DateTime.Now.AddHours(2); var timespan = ( expire- DateTime.Now); var key = string.Format("login-{0}", apiRm.Result.UserID); RedisCacheHelper.SetCacheByKey<string>(key, JsonHelper.ToJson(apiRm.Result), timespan);
通过header传入token后进行服务端认证有效性
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' --header 'token: 1000-e0622f06a9a842a5b79a5295e6d4b235' -d
在controller或action可设置属性是否要验证token
controller:[RoutePrefix("api/Out"), OperateTrack, AuthToken(AuthTypeEnum.Driver)]
或
action:[HttpPost, Route("GetOutInfo"),AuthToken(AuthTypeEnum.Driver)]
读取过滤器传过来的信息:
var user = ControllerContext.RouteData.Values["user"];
var user1 = HttpContext.Current.User;
创建AuthTokenAttribute继承AuthorizeAttribute
public class AuthTokenAttribute : AuthorizeAttribute { public AuthTypeEnum VerifyAuth { get; set; } public AuthTokenAttribute() { this.VerifyAuth = AuthTypeEnum.Common; } public AuthTokenAttribute(AuthTypeEnum verifyAuth) { this.VerifyAuth = verifyAuth; } protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext) { var request = actionContext.Request; if(VerifyAuth== AuthTypeEnum.Driver) { var rm= AuthDriver(actionContext); if (!rm.IsSuccess) return false; } return true; } protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext) { StringBuilder sbMsg = new StringBuilder(); if (VerifyAuth == AuthTypeEnum.Driver) { var rm = AuthDriver(actionContext); if (!rm.IsSuccess) sbMsg.Append(rm.Message); } var content = JsonConvert.SerializeObject(new ResultApiModel { IsSuccess = false, Message = sbMsg.ToString() + ",验证失败,状态:" + (int)HttpStatusCode.Unauthorized, Code = ((int)HttpStatusCode.Unauthorized).ToString() }); actionContext.Response = new HttpResponseMessage { Content = new StringContent(content, Encoding.UTF8, "application/json"), StatusCode = HttpStatusCode.Unauthorized }; } private ResultApiModel AuthDriver(System.Web.Http.Controllers.HttpActionContext actionContext) { //todo 验证token //向action传值,在action中可以使用:var user = ControllerContext.RouteData.Values["user"];获取到 actionContext.ControllerContext.RouteData.Values["user"] = v; SetPrincipal(new UserPrincipal<int>(tokenV)); return ResultApiModel.Create(true); } public static void SetPrincipal(IPrincipal principal) { Thread.CurrentPrincipal = principal; //每次都重新覆盖user,避免不同用户对不同action的访问 if (HttpContext.Current != null) { HttpContext.Current.User = principal; } } } public enum AuthTypeEnum { Common=0, Driver=1 }
IPrincipal:
public class UserIdentity<TKey> : IIdentity { public UserIdentity(IUser<TKey> user) { if (user != null) { IsAuthenticated = true; UserID = user.UserID; LoginNo = user.LoginNo.ToString(); Name = user.LoginNo.ToString(); UserName = user.UserName; RoleCode = user.RoleCode; token = user.token; } } public string AuthenticationType { get { return "CustomAuthentication"; } } public TKey UserID { get; private set; } public bool IsAuthenticated { get; private set; } public string LoginNo { get; private set; } public string Name { get; private set; } public string UserName { get; private set; } public string RoleCode { get; private set; } public string token { get; private set; } } public class UserPrincipal<TKey> : IPrincipal { public UserPrincipal(UserIdentity<TKey> identity) { Identity = identity; } public UserPrincipal(IUser<TKey> user) : this(new UserIdentity<TKey>(user)) { } /// <summary> /// /// </summary> public UserIdentity<TKey> Identity { get; private set; } IIdentity IPrincipal.Identity { get { return Identity; } } bool IPrincipal.IsInRole(string role) { throw new NotImplementedException(); } } public interface IUser<T> { /// <summary> /// 用户id /// </summary> T UserID { get; set; } /// <summary> /// 登录账号 /// </summary> string LoginNo { get; set; } /// <summary> /// 用户名称 /// </summary> string UserName { get; set; } /// <summary> /// 角色编号 /// </summary> string RoleCode { get; set; } /// <summary> /// 登录后分配token /// </summary> string token { get; set; } }
2、验证签名:
约定签名规则
controller或action增加属性验证
[AuthSign(AuthSignTypeEnum.Common)]
创建AuthSignAttribute继承AuthorizeAttribute
public class AuthSignAttribute : AuthorizeAttribute { public AuthSignTypeEnum AuthSignType { get; set; } public AuthSignAttribute() { this.AuthSignType = AuthSignTypeEnum.Common; } public AuthSignAttribute(AuthSignTypeEnum authSignType) { this.AuthSignType = authSignType; } /// <summary> /// 公共请求主体数据 /// </summary> private string CommonRequestBodyData { get; set; } /// <summary> /// 权限验证 /// </summary> /// <param name="actionContext"></param> /// <returns></returns> protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext) { var request = actionContext.Request; var requestBodyData = StreamHelper.GetStream2String(request.Content.ReadAsStreamAsync().Result); if (AuthSignType == AuthSignTypeEnum.Common) { CommonRequestBodyData = requestBodyData.TrimStart("data=".ToCharArray()); var urlParam = GetUrlParam(actionContext); if (!urlParam.IsSuccess) return false; var rm = AuthSignCommon(urlParam.Result, CommonRequestBodyData); if (!rm.IsSuccess) return false; } return true; } private ResultApiModel AuthSignCommon(CommonRequestApiModel request, string requestBodyData) { //todo 验证signreturn ResultApiModel.Create(true); }/// <summary> /// 处理未授权的请求 /// </summary> /// <param name="actionContext"></param> protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext) { StringBuilder sbMsg = new StringBuilder(); if (AuthSignType == AuthSignTypeEnum.Common) { //todo 处理验证失败信息 } var content = JsonConvert.SerializeObject(new ResultApiModel { IsSuccess = false, Message = sbMsg.ToString() + " 签名验证失败,状态:" + HttpStatusCode.Unauthorized }); actionContext.Response = new HttpResponseMessage { Content = new StringContent(content, Encoding.UTF8, "application/json"), StatusCode = HttpStatusCode.Unauthorized }; } } /// <summary> /// 签名类型 /// </summary> public enum AuthSignTypeEnum { Common = 0 }
3、访问日志:
controller或action增加属性
[RoutePrefix("api/Out"), OperateTrack, AuthToken(AuthTypeEnum.Driver)]
不需要日志可以[NoLog]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)] public class NoLogAttribute : Attribute { }
继承:ActionFilterAttribute
public class OperateTrackAttribute : ActionFilterAttribute { /// <summary> /// 自定义参数 /// </summary> public string msg { get; set; } public OperateTrackAttribute() { } /// <summary> /// 初始化时填入类的说明 /// </summary> /// <param name="message"></param> public OperateTrackAttribute(string message) { msg = message; } private static readonly string key = "enterTime"; public override Task OnActionExecutingAsync(System.Web.Http.Controllers.HttpActionContext actionContext, CancellationToken cancellationToken) { if (SkipLogging(actionContext)) { return base.OnActionExecutingAsync(actionContext, cancellationToken); } //记录进入请求的时间 actionContext.Request.Properties[key] = DateTime.Now.ToBinary(); return base.OnActionExecutingAsync(actionContext, cancellationToken); } /// <summary> /// 在请求执行完后 记录请求的数据以及返回数据 /// </summary> /// <param name="actionExecutedContext"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken) { object beginTime = null; if (actionExecutedContext.Request.Properties.TryGetValue(key, out beginTime)) { DateTime time = DateTime.FromBinary(Convert.ToInt64(beginTime)); HttpRequest request = HttpContext.Current.Request; string token = request.Headers["token"]; WebApiActionLogModel apiActionLog = new WebApiActionLogModel { Id = Guid.NewGuid(), //获取action名称 actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName, //获取Controller 名称 controllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName, //获取action开始执行的时间 enterTime = time, //获取执行action的耗时 costTime = (DateTime.Now - time).TotalMilliseconds, navigator = request.UserAgent, token = token, //获取用户token userId = getUserByToken(token), //获取访问的ip ip = request.UserHostAddress, userHostName = request.UserHostName, urlReferrer = request.UrlReferrer != null ? request.UrlReferrer.AbsoluteUri : "", browser = request.Browser.Browser + " - " + request.Browser.Version + " - " + request.Browser.Type, //获取request提交的参数 paramaters = StreamHelper.GetStream2String(actionExecutedContext.Request.Content.ReadAsStreamAsync().Result), //获取response响应的结果 executeResult = StreamHelper.GetStream2String(actionExecutedContext.Response.Content.ReadAsStreamAsync().Result), comments = msg, RequestUri = request.Url.AbsoluteUri }; //记debug Log.DefaultLogDebug(string.Format("actionExecutedContext {0} 请求:{1}", apiActionLog.controllerName + "/" + apiActionLog.actionName, JsonHelper.ToJson(apiActionLog))); } return base.OnActionExecutedAsync(actionExecutedContext, cancellationToken); } /// <summary> /// 获取当前登录用户的id /// </summary> /// <param name="token"></param> /// <returns></returns> public static string getUserByToken(string token) { UserIdentity<int> u = HttpContext.Current.User.Identity as UserIdentity<int>; if (u == null) return "未登录用户" + token; return u.LoginNo.ToString(); } /// <summary> /// 判断类和方法头上的特性是否要进行Action拦截 /// </summary> /// <param name="actionContext"></param> /// <returns></returns> private static bool SkipLogging(System.Web.Http.Controllers.HttpActionContext actionContext) { return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() || actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any(); } }