zoukankan      html  css  js  c++  java
  • SpringBoot+Shiro学习(七):Filter过滤器管理

    SpringBoot+Shiro学习(七):Filter过滤器管理

    96 
    Hiwayz 
     0.5 2018.09.06 19:09* 字数 1070 阅读 5922评论 1

    先从我们写的一个自定义Filter来看:

    public class RoleOrFilter extends AuthorizationFilter {
        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
            Subject subject = getSubject(request,response);
            String[] roles = (String[])mappedValue;
            if(roles == null || roles.length ==0){
                return true;
            }
            for(String role:roles){
                if(subject.hasRole(role)){
                    return true;
                }
            }
            return false;
        }
    }
    

    我们写的自定义RoleOrFilter表示有多个角色要求时,只需要满足一个即可通过。
    我们顺着RoleOrFilter的实现,了解他的父类


     
    image.png

    NameableFilter
    NameableFilter给Filter起个名字,如果没有设置默认就是FilterName;还记得之前的如authc吗?当我们组装拦截器链时会根据这个名字找到相应的拦截器实例;

    OncePerRequestFilter
    OncePerRequestFilter用于防止多次执行Filter的;也就是说一次请求只会走一次拦截器链;另外提供enabled属性,表示是否开启该拦截器实例,默认enabled=true表示开启,如果不想让某个拦截器工作,可以设置为false即可。

    AdviceFilter
    AdviceFilter提供了AOP风格的支持,类似于SpringMVC中的Interceptor

    1.  boolean preHandle(ServletRequest request, ServletResponse response) throws Exception  
    2.  void postHandle(ServletRequest request, ServletResponse response) throws Exception  
    3.  void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception;   
    

    preHandler:类似于AOP中的前置增强;在拦截器链执行之前执行;如果返回true则继续拦截器链;否则中断后续的拦截器链的执行直接返回;进行预处理(如基于表单的身份验证、授权)
    postHandle:类似于AOP中的后置返回增强;在拦截器链执行完成后执行;进行后处理(如记录执行时间之类的);
    afterCompletion:类似于AOP中的后置最终增强;即不管有没有异常都会执行;可以进行清理资源(如接触Subject与线程的绑定之类的);

    PathMatchingFilter

    PathMatchingFilter提供了基于Ant风格的请求路径匹配功能及拦截器参数解析的功能,如“roles[admin,user]”自动根据“,”分割解析到一个路径参数配置并绑定到相应的路径:

    1.  boolean pathsMatch(String path, ServletRequest request)  
    2.  boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception   
    

    pathsMatch:该方法用于path与请求路径进行匹配的方法;如果匹配返回true
    onPreHandle:在preHandle中,当pathsMatch匹配一个路径后,会调用opPreHandler方法并将路径绑定参数配置传给mappedValue;然后可以在这个方法中进行一些验证(如角色授权),如果验证失败可以返回false中断流程;默认返回true;也就是说子类可以只实现onPreHandle即可,无须实现preHandle。如果没有path与请求路径匹配,默认是通过的(即preHandle返回true)。

    AccessControlFilter

    AccessControlFilter提供了访问控制的基础功能;比如是否允许访问/当访问拒绝时如何处理等:

    1.  abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  
    2.  boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  
    3.  abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;   
    

    isAccessAllowed:表示是否允许访问;mappedValue就是[urls]配置中拦截器参数部分,如果允许访问返回true,否则false;
    onAccessDenied:表示当访问拒绝时是否已经处理了;如果返回true表示需要继续处理;如果返回false表示该拦截器实例已经处理了,将直接返回即可。

    onPreHandle会自动调用这两个方法决定是否继续处理:

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

    另外AccessControlFilter还提供了如下方法用于处理如登录成功后/重定向到上一个请求:

    1.  void setLoginUrl(String loginUrl) //身份验证时使用,默认/login.jsp  
    2.  String getLoginUrl()  
    3.  Subject getSubject(ServletRequest request, ServletResponse response) //获取Subject实例  
    4.  boolean isLoginRequest(ServletRequest request, ServletResponse response)//当前请求是否是登录请求  
    5.  void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException //将当前请求保存起来并重定向到登录页面  
    6.  void saveRequest(ServletRequest request) //将请求保存起来,如登录成功后再重定向回该请求  
    7.  void redirectToLogin(ServletRequest request, ServletResponse response) //重定向到登录页面   
    

    比如基于表单的身份验证就需要使用这些功能。
    到此基本的拦截器就完事了,如果我们想进行访问访问的控制就可以继承AccessControlFilter;如果我们要添加一些通用数据我们可以直接继承PathMatchingFilter。

    AuthorizationFilter
    AuthorizationFilter实现了AccessControlFilter的onAccessDenied方法

    //访问拒绝才会进来此方法
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
            Subject subject = getSubject(request, response);
            if (subject.getPrincipal() == null) {
                // 如果未登录,保存当前页面,重定向到登录页面
                saveRequestAndRedirectToLogin(request, response);
            } else {
                //匿名访问地址
                String unauthorizedUrl = getUnauthorizedUrl();
                if (StringUtils.hasText(unauthorizedUrl)) {
                    //如果匿名访问地址存在,则跳转去匿名访问地址
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    //不存在则返回404
                    WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
                }
            }
            return false;
        }
    

    ShiroFilterFactoryBean
    当我们写好了自定义Filter后,如何在Shiro中使用它呢?在config类中注入ShiroFilter的bean

         /**
         * ShiroFilter主要配置
          * @param securityManager
         * @return
         */
        @Bean
        public ShiroFilterFactoryBean shiroFilter (SecurityManager securityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            //自定义拦截器
            Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
            filtersMap.put("roleOrFilter", roleOrFilter());
            shiroFilterFactoryBean.setFilters(filtersMap);
    
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
            //注意过滤器配置顺序 不能颠倒
            //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl
            filterChainDefinitionMap.put("/logout", "logout");
            // 配置不会被拦截的链接 顺序判断
            //filterChainDefinitionMap.put("/hello", "anon");
            filterChainDefinitionMap.put("/ajaxLogin", "anon");
            filterChainDefinitionMap.put("/testRole", "anon");
            filterChainDefinitionMap.put("/**", "roleOrFilter[admin,admin1]");
            //自动跳去登录的地址
            shiroFilterFactoryBean.setLoginUrl("/login");
            //上面提到的匿名地址
            //shiroFilterFactoryBean.setUnauthorizedUrl();
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    

    这样我们在访问对应的请求时,就会先调用shiro的filter。

    默认拦截器名拦截器类说明(括号里的表示默认值)
    身份验证相关的    
    authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter 基于表单的拦截器;如“/**=authc”,如果没有登录会跳到相应的登录页面登录;主要属性:usernameParam:表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名(rememberMe); loginUrl:登录页面地址(/login.jsp);successUrl:登录成功后的默认重定向地址; failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure);
    authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter Basic HTTP身份验证拦截器,主要属性: applicationName:弹出登录框显示的信息(application);
    logout org.apache.shiro.web.filter.authc.LogoutFilter 退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/)
    user org.apache.shiro.web.filter.authc.UserFilter 用户拦截器,用户已经身份验证/记住我登录的都可;
    anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon”
    授权相关的    
    roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 角色授权拦截器,验证用户是否拥有所有角色;主要属性: loginUrl:登录页面地址(/login.jsp);unauthorizedUrl:未授权后重定向的地址;示例“/admin/**=roles[admin]”
    perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 权限授权拦截器,验证用户是否拥有所有权限;属性和roles一样;示例“/user/**=perms["user:create"]”
    port org.apache.shiro.web.filter.authz.PortFilter 端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样
    rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter rest风格拦截器,自动根据请求方法构建权限字符串(GET=read, POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create)构建权限字符串;示例“/users=rest[user]”,会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll);
    ssl org.apache.shiro.web.filter.authz.SslFilter SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;

    自定义filter异常

    此外,我在查找资料的时候,看到一个问题Shiro 自定义 filter 匹配异常,问题主要说在使用自定义filter定义的时候,使用@Bean模式注入自定义filter之后,会导致自定义filter的调用在shiroFilter的前面,具体解决方案可以点开原文看。
    我在我本地测试后发现没有问题,可能是新版本已经修复了这个bug。当前版本1.3.2

    小礼物走一走,来简书关注我

    赞赏支持
     
     日记本
    Web note ad 1
    登录 后发表评论
     
    昕云爸爸
    2楼 · 2019.04.27 21:51

    为什么加到ShiroFilterFactoryBean 中了,就能使用这个filter了?

     
     
    被以下专题收入,发现更多相似内容
    Shiro
    springboot
    推荐阅读更多精彩内容
     
  • 相关阅读:
    保持URL不变和数字验证
    centOS ftp key?
    本地环境测试二级域名
    linux 解决You don't have permission to access 问题
    php smarty section loop
    php header Cannot modify header information headers already sent by ... 解决办法
    linux部分命令
    Linux 里面的文件操作权限说明
    用IT网络和安全专业人士视角来裁剪云的定义
    SQL Server 2008 R2炫酷报表"智"作有方
  • 原文地址:https://www.cnblogs.com/leigepython/p/10883094.html
Copyright © 2011-2022 走看看