SSM项目中自定义Shiro权限过滤器实现多个权限的或操作

一.提出问题

因为在本项目中使用了Shiro来实现用户身份认证,权限授权,加密,会话管理等,简单说就是对页面访问权限进行控制。

Shiro权限控制是在用户登录时会在Realm中添加该用户的权限信息,在登录的时候会根据请求的url和相关的权限做映射。在用户请求具体url时,会根据url获得对应的权限,在到过滤器中做权限的校验。

然而我在页面权限配置过程中发现Shiro本身自带的权限过滤器只能实现权限之间的与操作,示例如下:

<!-- 权限配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 限制权限访问 -->
                /page/**=authc,perms[common,system]
                /page/settingEdit=authc,perms[system]
                /**=authc
            </value>
        </property>

上述配置的本意是使得 /page/** 下的url只需common,system这两个权限之中任意一个即可访问,而/page/settingEdit则需要system权限进行访问,达到页面访问权限控制的效果。

但由于上面的perms是Shiro自带的PermissionsAuthorizationFilter中的权限校验规则。

public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        //将配置转换为字符串数组
        String[] perms = (String[])((String[])mappedValue);
        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {
            if (perms.length == 1) {
                if (!subject.isPermitted(perms[0])) {
                    isPermitted = false;
                }
            } else if (!subject.isPermittedAll(perms)) {
                isPermitted = false;
            }
        }

        return isPermitted;
    }

导致实际上/page/**下的url需要两个权限同时具备方可成功访问,于是我们需要自定义一个权限过滤器来替代Shiro原本的权限过滤器。

二.解决问题--自定义Shiro权限过滤器

1.新建一个NewPermissionsAuthorizationFilter类

package com.shiro;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class NewPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = this.getSubject(request, response);

        if (subject.getPrincipal() == null) {
            this.saveRequestAndRedirectToLogin(request, response);
        } else {
            // 强转为HTTP
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse)response;
            // 获取响应头中ajax
            String header = req.getHeader("X-Requested-With");
            // 如果是ajax请求
            if ("XMLHttpRequest".equals(header)){
                // 设置响应头
                resp.setContentType("application/json;charset=UTF-8");
                resp.getWriter().print(false);
            }
            String unauthorizedUrl = this.getUnauthorizedUrl();
            if (StringUtils.hasText(unauthorizedUrl)) {
                WebUtils.issueRedirect(request, response, unauthorizedUrl);
            } else {
                WebUtils.toHttp(response).sendError(401);
            }
        }


        return false;
    }
    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        String[] perms = (String[]) ((String[]) mappedValue);
        boolean isPermitted = true;
        if (perms != null && perms.length > 0) {
            if (perms.length == 1) {
                if (!isOneOfPermitted(perms[0], subject)) {
                    isPermitted = false;
                }
            } else if (!isAllPermitted(perms,subject)) {
                isPermitted = false;
            }
        }
        return isPermitted;
    }

    /**
     * 以“,”分割的权限为并列关系的权限控制,分别对每个权限字符串进行“|”分割解析
     * 若并列关系的权限有一个不满足则返回false
     *
     * @param permStrArray 以","分割的权限集合
     * @param subject      当前用户的登录信息
     * @return 是否拥有该权限
     */
    private boolean isAllPermitted(String[] permStrArray, Subject subject) {
        boolean isPermitted = true;
        for (int index = 0, len = permStrArray.length; index < len; index++) {
            if (!isOneOfPermitted(permStrArray[index], subject)) {
                isPermitted = false;
            }
        }
        return isPermitted;
    }

    /**
     * 判断以“|”分割的权限有一个满足的就返回true,表示权限的或者关系
     *
     * @param permStr 权限数组种中的一个字符串
     * @param subject 当前用户信息
     * @return 是否有权限
     */
    private boolean isOneOfPermitted(String permStr, Subject subject) {
        boolean isPermitted = false;
        String[] permArr = permStr.split("\\|");
        if (permArr.length > 0) {
            for (int index = 0, len = permArr.length; index < len; index++) {
                if (subject.isPermitted(permArr[index])) {
                    isPermitted = true;
                }
            }
        }
        return isPermitted;
    }

}

2.在Shiro的配置文件中注入bean

<!--自定义权限过滤器-->
    <bean id="newPermissionsAuthorizationFilter" class="com.shiro.NewPermissionsAuthorizationFilter"/>

3.在shiroFilter中添加配置

<!--配置shiro自定义过滤器-->
        <property name="filters">
            <map>
                <!--自定义过滤器名字newPerms-->
                <entry key="newPerms" value-ref="newPermissionsAuthorizationFilter"/>
            </map>
        </property>

最终效果:

  • 保持“,”表示多个权限的并列关系,每个“,”分割的字符串可能是多个“或”权限的集合,再用“|”分割字符串,“|”需要转义。即在原有的基础上增加了权限的或操作。

还有最后一步:

4.在页面权限配置中使用自定义权限过滤器newPerms

<!-- 权限配置 -->
        <property name="filterChainDefinitions">
            <value>
                <!-- 限制权限访问 -->
                /page/**=authc,newPerms[common|system]
                /page/settingEdit=authc,newPerms[system]
                /**=authc
            </value>
        </property>

三.总结 

 只要多动手多动脑,没有什么问题是解决不了的。

  参考文章:SpringBoot-Shiro自定义过滤器实现配置多个权限

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chefng-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值