让你的系统更安全——权限校验

最近在作系统权限这一块,第一次发文记录分享一下,不足之处还请多多指教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,前端再根据状态码去作相应处理就行了.

若是有更好的处理方式,还请教我一下,谢谢

相关文章
相关标签/搜索