shiro filter + redis

shiro可以与web进行集成,通过一个ShiroFilter入口来拦截需要安全控制的URL。ShiroFilter通过aop对servlet的filter进行增强,因此先调用shiroFilter对请求进行第一步过滤。我们可以自定义拦截器,在拦截器中调用subject.login()方法进行用户身份验证,通过身份验证的则放行。通过给controller的接口添加requirePermission(),对登录用户的权限进行检查,看是否满足接口权限的要求,满足了才允许使用某接口。

URL书写规则:
url = 拦截器[参数]
  • anon拦截器:匿名访问,不需要登录即可访问
  • authc拦截器:需要身份认证通过后才能访问
  • roles[admin]拦截器:需要admin角色授权才能访问
url路径支持通配符,注意通配符匹配不包括目录分隔符 “/”:
  • ?:匹配一个字符,如”/admin?” 将匹配 / admin1、admin2,但不匹配 / admin 或 / admin123;
  • *:匹配零个或多个字符串,如 / admin * 将匹配 / admin、/admin123,但不匹配 / admin/1;
  • :匹配路径中的零个或多个路径,如 / admin/ 将匹配 / admin/a 或 / admin/a/b。
url模式匹配顺序

按照在配置中的声明顺序,即从头开始使用第一个匹配的url模式对应的拦截器,组成一条由url顺序规定的拦截器链。注意,如果先声明的url包含了后声明的url,那么后面的url就要使用前面url对应的拦截器。

拦截器链

Shiro 对 Servlet 容器的 FilterChain 进行了代理,即 ShiroFilter 在继续 Servlet 容器的 Filter 链的执行之前,通过 ProxiedFilterChain 对 Servlet 容器的 FilterChain 进行了代理;即先走 Shiro 自己的 Filter 体系,然后才会委托给 Servlet 容器的 FilterChain 进行 Servlet 容器级别的 Filter 链执行;Shiro 的 ProxiedFilterChain 执行流程:1、先执行 Shiro 自己的 Filter 链;2、再执行 Servlet 容器的 Filter 链(即原始的 Filter)。

swagger 放行
    	 filterChainDefinitionMap.put("/redis/login", "anon");
        // swagger 默认通过
        filterChainDefinitionMap.put("/v2/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/captcha.jpg", "anon");
        filterChainDefinitionMap.put("/csrf","anon");

        filterChainDefinitionMap.put("/redis/user/*","token,authc");
        filterChainDefinitionMap.put("/**","token,authc");
如何判断请求能否和指定的url pattern匹配

我们进入ShiroFilterFactoryBean,找到createInstance()中的PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();,进入后找到:
在这里插入图片描述在这里插入图片描述
这里的pathMatches()方法对我们定义的pattern和请求path进行匹配,如果不确定url pattern写的是否正确可以来这里检查一下。

自定义过滤器

自定义过滤器继承自抽象类AccessControlFilter,需要重写isAccessAllowed()onAccessAllowed()两个抽象方法,AccessControlFilter中定义了onPreHandle()方法如下:

public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return this.isAccessAllowed(request, response, mappedValue) || this.onAccessDenied(request, response, mappedValue);
    }

或连接符左侧如果返回true,默认请求允许访问,可以去filter链上找下一个过滤器处理;如果左侧返回false,执行右侧方法,最终返回true则继续拦截器链的执行,直接返回false表示自己已经处理了。

在shiroConfig配置文件中,我们通过setFilter()方法将自定义拦截器传给shiroFilterFactoryBean,并配置拦截url和对应过滤器。启动应用后,根据访问的url对应的过滤器进行过滤操作,因为我们自定义的过滤器中做了subject.login()登录认证操作,因此通过自定义过滤器的请求都是已经登录了的请求,未登录请求是无法通过过滤器的。

shiro授权流程

调用subject.checkRole()或者后端授权注解@RequiresPermissions()时,shiro需要判断subject是否有某角色或权限,首先会到SecurityManager中:
在这里插入图片描述
之后到ModularRealmAuthorizer
在这里插入图片描述
流转到 AuthorizingRealm 执行授权:
在这里插入图片描述
进入getAuthorizationInfo()获取用户的权限:

protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            return null;
        } else {
            AuthorizationInfo info = null;
            if (log.isTraceEnabled()) {
                log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
            }

// 拿到缓存实例
            Cache<Object, AuthorizationInfo> cache = this.getAvailableAuthorizationCache();
            Object key;
            if (cache != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
                }
// 调用缓存获取用户拥有的权限信息
                key = this.getAuthorizationCacheKey(principals);
                info = (AuthorizationInfo)cache.get(key);
                if (log.isTraceEnabled()) {
                    if (info == null) {
                        log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
                    } else {
                        log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
                    }
                }
            }
			// 缓存没拿到用户的权限信息,调用自定义customRealm的doget方法获取安全数据源中的权限信息
            if (info == null) {
                info = this.doGetAuthorizationInfo(principals);
                if (info != null && cache != null) {
                    if (log.isTraceEnabled()) {
                        log.trace("Caching authorization info for principals: [" + principals + "].");
                    }

                    key = this.getAuthorizationCacheKey(principals);
                    // 拿到之后存入缓存
                    cache.put(key, info);
                }
            }

            return info;
        }
    }

最后用用户所拥有的权限和接口所需要的权限进行对比,循环匹配,匹配成功说明该用户拥有权限,否则抛出subject does not have permission异常:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值