讓你的系統更安全——權限校驗

最近在作系統權限這一塊,第一次發文記錄分享一下,不足之處還請多多指教css

如今咱們作的Web系統,基本都須要權限管理,一是方便用戶管理:不一樣用戶展現不一樣的功能菜單;二是方便咱們維護:有些菜單是系統管理員操做的.html

權限基礎數據表

  1. 用戶表
  2. 角色表
  3. 用戶角色表
  4. 菜單表
  5. 角色菜單表

權限校驗分爲兩塊

  1. 前端校驗
  2. 後端校驗

前端權限校驗

前端校驗只是爲了提高用戶體驗,並不能真正的攔截請求 前端

流程
  1. 成功登錄系統後,服務端會返回一個session/token,而後存儲在前端
  2. 用戶獲取系統菜單:菜單列表、菜單對應的按鈕,一樣存儲到storage
  3. 根據權限加載頁面菜單 & 根據必定規則刪除掉沒有權限的按鈕

菜單基本json結構以下:redis

{
    name:'權限中心',
    permissionCode:"permission",
    isButton:false,
    children:[
        {
            name:'角色管理',
            permissionCode:'role',
            isButton:false,
            children:[
                {
                    name:'角色添加',
                    permissionCode:'role-add',
                    isButton:true
                },
                ...
            ]
        }
    ]
}
複製代碼
菜單列表加載

直接就能夠過濾掉沒有權限的菜單列表,較爲簡單json

<ul>
    <li v-for="menu in menus">
        <a @click="addPage(menu)">{{menu.name}}</a>
    </li>
</ul>
複製代碼
按鈕權限

首先肯定好頁面元素的屬性規則後端

如給須要權限校驗的元素添加一個自定義屬性permission,屬性值賦予規定的權限編碼瀏覽器

//角色頁面
<button @click="addRole" permission="role-add">添加</button>
複製代碼

刪除掉沒有權限的元素(按鈕、Tab...)安全

permission.jsbash

//1.獲取到當前頁面權限的元素集合  permissionElements
//2.進行權限檢查,將沒有權限的元素直接給移除掉
function checkPermission(){
    $("[permission]").each(function(ele){
        var permissionCode = ele.getAttribute('permission');
        if(!permissionElements.first("this.permissionCode=="+permissionCode)){
            item.parentNode.removeChild(item);
        }
    })
}
//體驗好一點,能夠添加一個css,先將頁面須要權限校驗的元素給隱藏起來
[permission] { display:none; }
//校驗完畢後再顯示出來
$("[permission]").each...
    .css('display','inline-block')
    
//每次進入頁面就調用了一次,進行權限檢查
複製代碼

如上jq爲主的項目,若是總體使用Vue框架來寫會方便許多了session

到這裏,前端的權限基本完成了,用戶能夠看到不一樣的菜單和按鈕了。

這裏算完成了權限嗎,能夠提升系統安全性嗎

普通用戶雖然看不到角色菜單列表了,可是在瀏覽器地址欄輸入/role/index.html 仍是同樣進入了角色頁面,仍是能夠看到數據,進行各類操做

真正的作到安全,必須得服務端校驗,前面提到了前端的權限校驗僅僅是爲了提高用戶體驗

後端權限校驗

後端使用.net core & redis完成權限的管理,這裏作基本代碼展現

咱們獲取到的菜單是樹形結構,這裏我把全部的菜單權限編碼抽取出來放在一個集合裏面,這樣校驗的時候取值就很容易了

這裏使用Redisset數據結構存儲,避免權限編碼重複

var menuList = GetMenus();
string permissionKey = $"user:{CurrentUser.Id}:permissions";
RedisHelper.SAdd(permissionKey,menuList.Select(m => m.PermissionCode).ToArray());
複製代碼
自定義權限特性
public class PermissionAttribute : Attribute
{
    public string PermissionCode { get; set; }
    public PermissionAttribute(string permissionCode)
    {
        PermissionCode = permissionCode;
    }
}
複製代碼
在須要權限校驗的Api上添加特性
[HttpGet]
[Route("role/add")]
[Permission("role-add")]
public ResponseBase AddRole(){
    ...
}
複製代碼
添加公共的攔截器Filter,驗證全部請求
public class MyAuthFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        ...其餘校驗
        
        #region 權限校驗
        var isCheckPermission = controllerActionDescriptor.MethodInfo.GetCustomAttributes(true)
                   .Any(a => a.GetType().Equals(typeof(PermissionAttribute)));
        if (isCheckPermission)
        {
            var permissionAttribute = controllerActionDescriptor.MethodInfo.CustomAttributes
                .FirstOrDefault(c => c.AttributeType == typeof(PermissionAttribute));
            if (permissionAttribute != null)
            {
                string permissionCode = permissionAttribute.ConstructorArguments[0].Value.ToString();
                string[] codes = RedisHelper.SMembers($"user:{User.Id}:permissions");
                if (!codes.Contains(permissionCode))
                {
                    context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                    //需驗證tokne是否過時
                    context.Result = new JsonResult("403沒有權限訪問資源");
                }
            }
        }
        #endregion
    }
}
複製代碼

到這裏基本已經完成啦,權限校驗不經過,Http狀態碼會返回403,前端再根據狀態碼去作相應處理就行了.

若是有更好的處理方式,還請教我一下,謝謝

相關文章
相關標籤/搜索