菜单权限、功能权限、按钮权限、用户工具、Shiro

Shiro是Apache的一个开源框架,是一个权限管理的框架,实现用户认证、用户授权等。

只要有用户参与一般都要有权限管理,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。

用户身份认证

用户去访问系统,系统需要验证用户身份的合法性,证明你是否是合法的用户。

最常用的用户身份验证的方法:

  • 用户名、密码方式
  • 用户名、安全密钥方式
  • 基于硬件或证书验证方法 系统验证用户身份合法,用户方可访问系统的资源。

从用户认证我们可以抽取出:

  • 主体对象(Subject):理解为用户,可能是程序,都要去访问系统的资源,系统需要对subject进行身份认证,获取方法:UserUtils.getSubject()
  • 身份信息(Principal):通常是唯一的,一个主体还有多个身份信息,但是都有一个主身份信息(Primary principal),获取方法:UserUtils.getLoginInfo()

用户权限授权

可简单理解为访问控制,在用户身份认证通过后,系统对用户访问菜单或按钮进行控制。也就是说,该用户有身份进入系统了,但他不一定能访问系统里的所有菜单或按钮,而他只能访问管理员给他分配的权限菜单或按钮。

  • Permission(权限标识):针对系统访问资源的权限标识,如:用户添加、用户修改、用户删除,判断方法:UserUtils.getSubject().isPermitted(permissions); JeeSite的权限标识在菜单管理里设置

权限管理模型

  • 用户:登录账号、密码、用户类型
  • 角色:角色名称、归属用户类型
  • 菜单:菜单名称、菜单URL、权限标识
  • 用户角色关系:用户编码、角色编码
  • 角色菜单关系:角色编码、菜单编码
  1. 【用户】<---多对多--->【角色】<---多对多--->【菜单/权限】

管理员类型:

  • 超级管理员:主要为开发者使用的最高级别管理员,主要用于开发和调试,有些修改会直接影响系统的正常运行。
  • 系统管理员:主要为客户方使用的管理员,用于一些基础数据配置,如机构、用户、权限、用户字典等,默认账号为admin。
  • 二级管理员,是由系统管理员指定的,可以分担系统管理员的工作,可以管理用户、分配菜单权限和操作权限一种特殊角色,但它仅具备系统管理员指定范围的管理数据。

菜单权重

菜单权重是指什么样的用户或管理员可以操作或访问什么级别的菜单,对菜单的权重级别进行划分,比如:比较重要敏感的菜单,只有管理员才可以拥有。如下:

  • 超级管理员可以访问二级管理员、系统管理员、超级管理员权重的菜单,但不允许访问默认权限(业务菜单);
  • 系统管理员可以访问超级管理员指定给他的系统管理员及以下权限的部分菜单;
  • 二级管理员可以访问超级或系统管理员指定给他的二级管理员及以下的部分菜单;
  • 普通用户只能访问管理员指定给他的默认权重的菜单。 菜单权重的设置,会影响角色授权菜单的时候,列出的菜单和权限列表:

  • 如果当前用户管理身份为二级管理员,则列出的是二级管理员菜单权重以下的菜单;

  • 如果当前用户管理员身份是系统管理,则列出的是系统管理员菜单权重以下的菜单;
  • 如果当前用户管理员身份是超级管理员,则列出的是超级管理员菜单权重下的菜单。 此举是为了更好的提高授权安全,不能越级授权,权限互相牵制等。

支持四种授权方式

  • 编程式:通过if/else代码块来完成。
  • 注解式:通过在执行的方法上放置相应的注解来完成,没有权限则抛出相应异常。
  • 视图页面: 在JSP/GSP页面通过相应的标签完成。
  • 基于URI拦截:根据URI匹配,决定访问权限。

编程式

  1. Subject subject = UserUtils.getSubject();
  2. subject.isAuthenticated(); // 是否身份验证授权通过
  3. subject.isPermitted(permission); // 验证权限字符串
  4. subject.isPermittedAll(permissions); // 验证权限字符串全部通过
  5. subject.hasRole(roleIdentifier); // 验证是否有角色权限

例如:

  1. if (subject.isPermitted("sys:user:edit")){
  2. System.out.pirntln("当前用户有编辑用户权限");
  3. }

注解式

在Controller的方法上指定如下注解:

  • @RequiresPermissions (value={“sys:user:view”, “sys:user:edit”}, logical= Logical.OR):表示当前 Subject 需要权限 user:view 或 user:edit。
  • @RequiresRoles(value={“admin”, “user”}, logical=Logical.AND):表示当前 Subject 需要角色 admin 和 user
  • @RequiresAuthentication:表示当前Subject已经通过login进行了身份验证;
  • @RequiresUser:表示当前 Subject 已经身份验证或者通过记住我登录的。
  • @RequiresGuest:表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。 例如:
  1. @RequiresPermissions(value="sys:user:edit")
  2. @PostMapping(value = "save")
  3. @ResponseBody
  4. public String save(@Validated User user, HttpServletRequest request) {
  5. }

注意:

  • 权限字符串命名规则:模块:功能:操作,例如:sys:user:edit
  • 权限字符串Permission:指定权限串必须和菜单中的权限标识匹配才可访问

视图页

  • 单个权限验证:${hasPermi(‘sys:user:edit’)}
  • 多个AND关系:${hasPermi(‘sys:user:view,sys:user:edit’, ‘and’)}
  • 多个OR关系:${hasPermi(‘sys:user:view,sys:user:edit’, ‘or’)} 例如:
  1. <% if(hasPermi('sys:user:edit')){ %>
  2. <a href="${ctx}/sys/user/form" class="btn btn-default btnTool"
  3. title="新增用户"><i class="fa fa-plus"></i> 新增</a>
  4. <% } %>

基于URI拦截

application.yml(4.0.x:jeesite.yml):

  1. shiro:
  2. # URI 权限过滤器定义
  3. filterChainDefinitions: |
  4. /ReportServer/** = user
  5. ${adminPath}/** = user

格式:

  1. URI地址及通配符 = 过滤器名称(支持多个,用英文逗号分隔并加双引号)

以上权限过滤器定义配置,是依照由上到下的第一次匹配优先原则,优先匹配成功的URI优先受用,支持通配符。

URI通配符:

  1. ? :匹配一个字符。
  2. * :匹配零个或多个字符串。
  3. ** :匹配路径中的零个或多个路径。

认证过滤器名称:

  • anon: 例如/a/**=anon没有参数,表示可以匿名使用。
  • authc:例如/a/sys/user/**=authc表示需要认证(登录)才能使用,没有参数
  • user:例如/a/sys/user/**=user没有参数表示必须存在用户,当登入操作时不做检查 授权过滤器名称:

  • perms:例如/a/sys/user/=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/a/sys/user/=perms["user:add:,user:modify:"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

  • roles:例如/a/sys/user/=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如/a/sys/user/=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。

相关常用工具类

1、获取当前用户:

  • UserUtils.getUser();
  • entity.getCurrentUser();
  • ${@UserUtils:getUser()} 2、获取当前用户员工:

  • EmpUtils.getEmployee();

  • ${EmpUtils.getEmployee()} 3、获取当前用户部门:

  • EmpUtils.getOffice();

  • ${EmpUtils.getOffice()} 4、获取当前用户菜单:

  • UserUtils.getMenuList();

  • UserUtils.getMenuListByParentCode(parentCode); 5、获取当前用户授权信息:

  • UserUtils.getAuthInfo(); 6、获取或设置当前用户缓存:

  • UserUtils.getCache(key);

  • UserUtils.putCache(key);