mvc 与 前端的数据交互安全机制

43 篇文章 1 订阅

通过一些代码过滤验证,与前端使用交互token实现限制访问端,避免使用非正常工具访问,应可满足一些等保需求。
控制器代码:

using Dcampus.Controllers;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http.Results;
using System.Web.Mvc;
using Common;

namespace Dcampus.Areas.Admin_Areas.Controllers
{
    /// <summary>
    /// 新学生端,所有方法只支持post方式
    /// </summary>
    public class StunController : BaseController
    {
        #region 学生端返回数据封装

        /// <summary>
        /// 交互token字典,key=用户token,value=当前交互token
        /// </summary>
        private static Dictionary<string, Vobj> StuTokenDic = new Dictionary<string, Vobj>();

        /// <summary>
        /// 用户访问对象
        /// </summary>
        protected class Vobj
        {
            /// <summary>
            /// 身份token
            /// </summary>
            public string token { get; set; }

            private Queue<string> cts = new Queue<string>();
            /// <summary>
            /// 交互token
            /// </summary>
            public Queue<string> changeToken { get { return cts; } set { cts = value; } }
            /// <summary>
            /// 访问次数
            /// </summary>
            public int vnums { get; set; }
            /// <summary>
            /// 最后访问时间
            /// </summary>
            public DateTime lastTime { get; set; }
            /// <summary>
            /// 禁止访问
            /// </summary>
            public bool disv { get; set; }
            /// <summary>
            /// 最后访问的路径
            /// </summary>
            public string lastUrl { get; set; }

            public Vobj(string token, string lastToken, DateTime lastTime, string lastUrl)
            {
                this.token = token;
                this.changeToken.Enqueue(lastToken);
                this.lastTime = lastTime;
                this.lastUrl = lastUrl;
            }

        }

        protected JsonResult Msg(AjaxStatu status, object data = null, bool sendtoken = true)
        {
            Hashtable hs = new Hashtable();
            hs.Add("status", status);
            hs.Add("data", data);

            try
            {
                if (sendtoken)
                {
                    hs.Add("ctoken", GetChangeToken());
                }
            }
            catch (Exception e)
            {
                hs["status"] = AjaxStatu.err;
                hs["data"] = string.Format("{0},请联系管理员,并提供当前使用场景截图。", e.Message);
            }
            return Json(hs);
        }

        protected JsonResult ActFunc(Action func, bool chktoken = true)
        {
            if (chktoken)
            {
                try
                {
                    ChkChangeToken();//先检查交互数据token
                }
                catch (Exception e)
                {
                    return Msg(AjaxStatu.err, e.Message, false);
                }
            }
            try
            {
                func();
                return Msg(AjaxStatu.ok);
            }
            catch (Exception e)
            {
                return Msg(AjaxStatu.err, e.Message);
            }
        }

        protected JsonResult ActFunc(Func<object> func, bool chktoken = true)
        {
            if (chktoken)
            {
                try
                {
                    ChkChangeToken();//先检查交互数据token
                }
                catch (Exception e)
                {
                    return Msg(AjaxStatu.err, e.Message, false);
                }
            }
            try
            {
                object data = func();
                return Msg(AjaxStatu.ok, data);
            }
            catch (Exception e)
            {
                return Msg(AjaxStatu.err, e.Message);
            }
        }

        /// <summary>
        /// 获取交互token
        /// </summary>
        /// <returns></returns>
        private string GetChangeToken()
        {
            //未登录
            if (Session["stutoken"] == null)
            {
                throw new Exception("未登录");
            }
            string token = Session["stutoken"].ToString();
            //未找到用户token
            if (!StuTokenDic.ContainsKey(token))
            {
                throw new Exception("请使用合法的访问方式,原因为一");
            }
            //交互token为空
            if (StuTokenDic[token] == null)
            {
                throw new Exception("交互数据出现异常");
            }
            //一定时间段内操作超过一定次数
            var last = StuTokenDic[token];
            if (last.disv)
            {
                throw new Exception("已禁止访问,请退出登录后,再重新登录");
            }
            TimeSpan ts = DateTime.Now - last.lastTime;
            int secs = 5;//访问间隔 5 秒
            int nums = 5;//访问次数 5
            if (Request.Url.AbsolutePath == last.lastUrl)
            {
                if (ts.Seconds <= secs)
                {
                    if (last.vnums >= nums)
                    {
                        last.disv = true;
                        StuTokenDic[token] = last;//禁止访问
                        throw new Exception("请使用合法的访问方式,原因为二");
                    }
                    else
                    {
                        last.vnums++;
                    }
                }
                else
                {
                    last.lastTime = DateTime.Now;
                    last.vnums = 0;
                }
            }
            else
            {
                last.lastTime = DateTime.Now;
                last.vnums = 0;
            }
            last.lastUrl = Request.Url.AbsolutePath;
            string lasttoken = DataAll.Dal.Common.CreateToken();
            last.changeToken.Enqueue(lasttoken);

            while (last.changeToken.Count > 20)
            {
                last.changeToken.Dequeue();
            }

            StuTokenDic[token] = last;

            return lasttoken;
        }

        /// <summary>
        /// 检查当前用户的交互token是否一致
        /// </summary>
        /// <returns></returns>
        private void ChkChangeToken()
        {
            //此时应已登录
            var last = StuTokenDic[Session["stutoken"].ToString()];
            string changeToken = Request.Form["ctoken"];
            if (string.IsNullOrEmpty(changeToken)) { throw new Exception("参数异常"); }
            if (!last.changeToken.Contains(changeToken))
            {
                throw new Exception("交互数据出现异常,请使用合法的访问方式");
            }
        }

        #endregion

        /// <summary>
        /// 获取当前登录用户的token
        /// </summary>
        /// <remarks>此方法不限制访问次数</remarks>
        /// <returns></returns>
        //[AllowAnonymous]
        public JsonResult getCurrent(string sid)
        {
            return ActFunc(() =>
            {
                string url = Request.Url.AbsolutePath;
                if (Session["stutoken"] != null)
                {
                    string token = Session["stutoken"].ToString();
                    if (!StuTokenDic.ContainsKey(token))
                    {
                        StuTokenDic.Add(token, new Vobj(token, DataAll.Dal.Common.CreateToken(), DateTime.Now, url));
                    }

                    return new { token = token };
                }
                else
                {
                    if (!string.IsNullOrEmpty(sid))
                    {
                        if (oc.CurrentUser != null)
                        {
                            if (oc.CurrentUser.barcode == sid)
                            {
                                string token = DataAll.Dal.Common.CreateToken();
                                Session["stutoken"] = token;
                                StuTokenDic.Add(token, new Vobj(token, DataAll.Dal.Common.CreateToken(), DateTime.Now, url));
                                return new { token = token, ctoken = StuTokenDic[token].changeToken };
                            }
                            else
                            {
                                throw new Exception("未登录");
                            }
                        }
                        else
                        {
                            throw new Exception("未登录");
                        }

                    }
                    else
                    {
                        throw new Exception("未登录");
                    }
                }
            }, false);
        }

        /// <summary>
        /// 获取用户权限菜单树
        /// </summary>
        /// <returns></returns>
        public JsonResult getPerTree()
        {
            return ActFunc(() =>
            {
                return oc.UserPermission;
            });
        }

        /// <summary>
        /// 退出登录
        /// </summary>
        /// <returns></returns>
        public void logout()
        {
            string token = Session["stutoken"].ToString();
            StuTokenDic.Remove(token);
            Session.Abandon();
        }
    }
}

mvc中的过滤器,加入对stun的免过滤判断

 public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            //       filters.Add(new HandleErrorAttribute());
            //注册过滤器
            //filters.Add(new LoginValidateAttribute());
            //filters.Add(new DataAll.Filter.LoginValidateAttribute());
            filters.Add(new LoginAttribute());
            filters.Add(new CustomErrorProcessAttribute());
        }
    }

    public class LoginAttribute : AuthorizeAttribute
    {
        #region  在过程请求授权时调用
        //
        // 摘要: 
        //     在过程请求授权时调用。
        //
        // 参数: 
        //   filterContext:
        //     筛选器上下文,它封装有关使用 System.Web.Mvc.AuthorizeAttribute 的信息。
        //
        // 异常: 
        //   System.ArgumentNullException:
        //     filterContext 参数为 null。
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            string strAreaName = "";
            if (filterContext.RouteData.DataTokens["area"] != null)
                strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower();
            string strControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower();
            string strActionName = filterContext.ActionDescriptor.ActionName.ToLower();
            string strHttpMethod = filterContext.HttpContext.Request.HttpMethod;

            //新学生端跳过验证
            if (strControllerName == "stun")
            {
                return;
            }

            var actionAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AllowAnonymousAttribute), true) as IEnumerable<AllowAnonymousAttribute>;
            if (actionAnonymous != null && actionAnonymous.Any())
            {
                return;
            }

            /**
             * 如果请求的区域包含area并且area的名称等于Admin_Areas
             * 那么就进行权限验证 
             * */
            if (filterContext.RouteData.DataTokens.Keys.Contains("area"))
            {
                /**
                   * 验证用户是否登录
                   * */
                if (filterContext.HttpContext.Request.Cookies.Get(OperContext.LOGINID) == null || string.IsNullOrEmpty(filterContext.HttpContext.Request.Cookies.Get(OperContext.LOGINID).Value))
                {
                    #region 开发期自动登录
                    string isdevelop = System.Configuration.ConfigurationManager.AppSettings["isdevelop"];
                    if (!string.IsNullOrEmpty(isdevelop))
                    {
                        DataAll.Dal.Users.UserImpl.Login("wyl", "1234", "wyl");
                        return;
                    }
                    else
                    {
                        filterContext.Result = new BaseController().Redirect("/AdminLogin/Login?msg=noLogin1", filterContext.ActionDescriptor, AjaxStatu.nologin);
                    }
                    #endregion
                    return;
                }
                if (!Model_sys_users.IsLogin())
                {
                    string isdevelop = System.Configuration.ConfigurationManager.AppSettings["isdevelop"];
                    if (!string.IsNullOrEmpty(isdevelop))
                    {
                        DataAll.Dal.Users.UserImpl.Login("wyl", "1234", "wyl");
                        return;
                    }
                    else
                    {
                        //如果没有登录那么就跳转到登录页面
                        filterContext.Result = new BaseController().Redirect("/AdminLogin/Login?msg=noLogin2", filterContext.ActionDescriptor, AjaxStatu.nologin);
                    }
                }
                else
                {
                    List<string> roles = (List<string>)HttpContext.Current.Session["userRole"];
                    string limstr = System.Configuration.ConfigurationManager.AppSettings["stulimit"];

                    if (!string.IsNullOrEmpty(limstr))
                    {
                        string[] lims = limstr.Split('|');
                        if (roles != null)
                        {
                            if (roles.Count == 1)
                            {
                                if (roles[0].ToLower() == "student")
                                {
                                    bool allow = false;
                                    foreach (string c in lims)
                                    {
                                        if (c.ToLower() == strControllerName.ToLower())
                                        {
                                            allow = true;
                                            break;
                                        }
                                    }
                                    if (!allow)
                                    {
                                        filterContext.Result = new BaseController().Redirect("/AdminLogin/Login?msg=noPermission", filterContext.ActionDescriptor, AjaxStatu.noperm);
                                        return;
                                    }
                                }
                            }
                        }
                    }

                    //设置滑动时间
                    Model_sys_users.SetNewTime();
                    //if (Model_USERS.LoginName == "admin")
                    //   return;
                    /**
                     * Action方法本身及它所属控制器都没有定义Skip特性
                     * 那么就可以进行权限验证
                     * */
                    if (!filterContext.ActionDescriptor.AttributeExists<Common.Attributes.SkipAttribute>(false)
                        && !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(Common.Attributes.SkipAttribute), false))
                    {
                        //验证该登录用户是否有访问该页面的权限
                        HttpMethod httpMethod = strHttpMethod.ToLower().Equals("get") ? HttpMethod.Get
                            : strHttpMethod.ToLower().Equals("post") ? HttpMethod.Post : HttpMethod.HEAD;
                        if (Model_sys_module.IsCheckPermission(strAreaName, strControllerName, strActionName))//需验证
                            if (!Model_sys_module.HasPermission(strAreaName, strControllerName, strActionName))
                            {
                                filterContext.Result = new BaseController().Redirect("/AdminLogin/Login?msg=noPermission", filterContext.ActionDescriptor, AjaxStatu.noperm);
                            }
                    }
                }
            }

        }
        #endregion
    }

vue中的路由验证,放在app.vue中的mounted中

mounted() {
    //刷新浏览器时清除缓存
    tools.delCookie(tools.utoken);
    tools.delCookie(tools.ctoken);

    //重新获取token
    this.axios.post("/admin_areas/stun/getCurrent").then(response => {
      tools.chkResult(
        response.data,
        function() {
          tools.setCookie(tools.utoken, response.data.data.token);
          tools.setCookie(tools.ctoken, response.data.ctoken);
          tools.post("/admin_areas/stun/test1", null, function(res) {
            console.log(res);
          });
          tools.post("/admin_areas/stun/getpertree", null, function(res) {
            console.log(res);
          });
        },
        function() {
          window.location.href = "/adminlogin/login";
        }
      );
    });

    // this.axios.post("/admin_areas/res/gettitle").then(response => {
    //   this.title = response.data;
    // });
  },

tools.vue中封装cookie和post方法,post方法中给参数加入ctoken值,并用chkResult来接收返回值取出ctoken写入cookie


<script>
import axios from 'axios'
import qs from 'qs'

const utoken = "edustu";
const ctoken = "dbtoken";

function getCookie(name) {
  var arr,
    reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
  if ((arr = document.cookie.match(reg))) return unescape(arr[2]);
  else return null;
}

function setCookie(name, value) {
  document.cookie = name + "=" + escape(value) + ";";
}

function delCookie(name) {
  var date = new Date();
  date.setTime(date.getTime() - 1);
  var delValue = getCookie(name);
  if (!!delValue) {
    document.cookie = name + "=" + delValue + ";expires=" + date.toGMTString();
  }
}

function chkResult(data, funok, funerr) {
  if (data.data.status == 1) {
    setCookie(ctoken, data.data.ctoken);//写入cookie
    if (funok) {
      funok();
    }
  } else {
    if (funerr) {
      funerr();
    } else {
      if (data.data.indexOf("未登录") >= 0  || data.data.indexOf("原因为一")>=0 )   {
        window.location.href="/adminlogin/login"
      } else {
        alert(data.data);
      }
    }
  }
}

function post(url,p,func){
  if(!p){
    p={};
  }
  p.ctoken=getCookie(ctoken);
  axios.post(url,qs.stringify(p),{
        headers: {
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'
          }
      }).then(response => {
      func(response);
    });
}

export default {
  utoken,
  ctoken,
  getCookie,
  setCookie,
  delCookie,
  chkResult,
  post
};
</script>

这个方案还有弊端,并不完善,慎用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值