精华内容
下载资源
问答
  • shiro动态URL权限控制

    2018-04-14 10:43:21
    在Spring中整合shiro,使用shiro动态URL权限控制学习教程
  • Springboot+Shiro 基于URL 动态控制权限

    万次阅读 2018-08-13 19:21:57
     权限控制有 注解的方式,jsp shiro标签的方式,还有url 动态控制的方式。这里我使用最后一种方式来控制权限 思路: 0.利用 PathMatchingFilter 拦截器 1.根据用户名 来查询角色, 2.根据角色查询权限 3.获取...

    前言:

       权限控制有 注解的方式,jsp shiro标签的方式,还有url 动态控制的方式。这里我使用最后一种方式来控制权限

    思路:

    0.利用  PathMatchingFilter 拦截器

    1.根据用户名 来查询角色,

    2.根据角色查询权限

    3.获取请求的url 

    4判断 根据用户名查询的权限 是否包括 请求的url

    5.如果包括 则 放行,不包括重定向到 未授权界面

    package com.example.springboot.shiro.core.shiro.filter;
    
    import com.example.springboot.shiro.common.utils.SpringContextUtil;
    import com.example.springboot.shiro.core.shiro.token.TokenManager;
    import com.example.springboot.shiro.user.entity.Upermission;
    import com.example.springboot.shiro.user.service.LoginService;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.PathMatchingFilter;
    import org.apache.shiro.web.util.WebUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.util.List;
    
    /**
     *  权限 拦截策略
     */
    public class URLPathMatchingFilter extends PathMatchingFilter {
        @Autowired
        LoginService loginService;
    
        @Override
        protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    
              if (loginService==null){
                  loginService= SpringContextUtil.getContext().getBean(LoginService.class);
              }
            //请求的url
            String requestURL = getPathWithinApplication(request);
            System.out.println("请求的url :"+requestURL);
            Subject subject = SecurityUtils.getSubject();
            if (!subject.isAuthenticated()){
                // 如果没有登录, 直接返回true 进入登录流程
                return  true;
            }
            String email = TokenManager.getEmail();
            List<Upermission> permissions = loginService. upermissions(email);
    
            boolean hasPermission = false;
            for (Upermission url : permissions) {
    
                  if (url.getUrl().equals(requestURL)){
                      hasPermission = true;
                      break;
                  }
            }
            if (hasPermission){
                return true;
            }else {
                UnauthorizedException ex = new UnauthorizedException("当前用户没有访问路径" + requestURL + "的权限");
                subject.getSession().setAttribute("ex",ex);
                WebUtils.issueRedirect(request, response, "/unauthorized");
                return false;
    
            }
    
        }
    }
    

     配置 : ShiroConfiguration(shiro 配置类) 

    package com.example.springboot.shiro.core.shiro.config;
    
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import com.example.springboot.shiro.core.shiro.filter.KickoutSessionControlFilter;
    import com.example.springboot.shiro.core.shiro.filter.SessionControlInterceptor;
    import com.example.springboot.shiro.core.shiro.filter.SessionFilter;
    import com.example.springboot.shiro.core.shiro.filter.URLPathMatchingFilter;
    import com.example.springboot.shiro.core.shiro.token.SampleRealm;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.codec.Base64;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.crazycake.shiro.RedisManager;
    import org.crazycake.shiro.RedisSessionDAO;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.crazycake.shiro.RedisCacheManager;
    import org.springframework.data.redis.core.RedisTemplate;
    
    import javax.servlet.Filter;
    
    @Configuration    //Shiro配置类
    public class ShiroConfiguration {
        @Value("${spring.redis.host}")
        private String host;
    
        @Value("${spring.redis.port}")
    
        private int port;
        @Value("${spring.redis.timeout}")
        private int timeout;
    
        @Bean
        public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
    
        /**
         * ShiroFilterFactoryBean 处理拦截资源文件问题。
         * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
         * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
         * <p>
         * Filter Chain定义说明
         * 1、一个URL可以配置多个Filter,使用逗号分隔
         * 2、当设置多个过滤器时,全部验证通过,才视为通过
         * 3、部分过滤器可指定参数,如perms,roles
         */
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            System.out.println("ShiroConfiguration.shirFilter()");
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    
            // 必须设置 SecurityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            shiroFilterFactoryBean.setLoginUrl("/login");
            // 登录成功后要跳转的链接(没用,在js中跳转了)
            shiroFilterFactoryBean.setSuccessUrl("/index");
            //未授权界面
            shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
            //拦截器.
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
            //自定义拦截器
            Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
            //限制同一帐号同时在线的个数。
            filtersMap.put("kickout", kickoutSessionControlFilter());
            //访问权限配置
            filtersMap.put("requestURL", getURLPathMatchingFilter());
    
            shiroFilterFactoryBean.setFilters(filtersMap);
           /* 配置映射关系*/
            //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/index", "authc");
            filterChainDefinitionMap.put("/css/**", "anon");
            filterChainDefinitionMap.put("/js/**", "anon");
            filterChainDefinitionMap.put("/updateSelf", "authc");
            filterChainDefinitionMap.put("/updatePswd", "authc");
            filterChainDefinitionMap.put("/mypermission", "authc");
            filterChainDefinitionMap.put("/kickout", "anon");
            filterChainDefinitionMap.put("/list", "authc");
            filterChainDefinitionMap.put("/online", "authc");
            filterChainDefinitionMap.put("/role", "authc");
            filterChainDefinitionMap.put("/Roleassignment", "authc");
            filterChainDefinitionMap.put("/permissionlist", "authc");
            filterChainDefinitionMap.put("/PermissionAssignment", "authc");
    
            /*加入自定义过滤器*/
            filterChainDefinitionMap.put("/**", "kickout");
            //下面的配置路径 都需要在上面配置 authc 否则访问不到filter
            filterChainDefinitionMap.put("/online","requestURL");
            filterChainDefinitionMap.put("/list", "requestURL");
            filterChainDefinitionMap.put("/role", "requestURL");
            filterChainDefinitionMap.put("/Roleassignment", "requestURL");
            filterChainDefinitionMap.put("/permissionlist", "requestURL");
            filterChainDefinitionMap.put("/PermissionAssignment", "requestURL");
    
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
    
        }
    
        /**
         *   访问 权限 拦截器
         * @return
         */
        public URLPathMatchingFilter getURLPathMatchingFilter() {
            return new URLPathMatchingFilter();
        }
    
        /**
         * 自定义域
         *
         * @return
         */
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //设置realm.
            securityManager.setRealm(getDatabaseRealm());
    
            // 自定义缓存实现 使用redis
            securityManager.setCacheManager(cacheManager());
            securityManager.setSessionManager(sessionManager());
            //注入记住我管理器;
            securityManager.setRememberMeManager(rememberMeManager());
            return securityManager;
        }
    
        /**
         * 授权&认证
         *
         * @return
         */
        @Bean
        public SampleRealm getDatabaseRealm() {
            SampleRealm myShiroRealm = new SampleRealm();
            System.out.println("myShiroRealm");
            myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return myShiroRealm;
        }
    
    
        /**
         * 凭证匹配器
         * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
         * 所以我们需要修改下doGetAuthenticationInfo中的代码;
         * )
         *
         * @return
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher() {
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
    
            hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
            //  hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5(md5(""));
    
            return hashedCredentialsMatcher;
        }
    
    
        /**
         * 开启shiro aop注解支持.
         * 使用代理方式;所以需要开启代码支持;
         *
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
        /**
         * cacheManager 缓存 redis实现
         * 使用的是shiro-redis开源插件
         *
         * @return
         */
        public RedisCacheManager cacheManager() {
            RedisCacheManager redisCacheManager = new RedisCacheManager();
            redisCacheManager.setRedisManager(redisManager());
            return redisCacheManager;
        }
    
    
        /**
         * 配置shiro redisManager
         * 使用的是shiro-redis开源插件
         *
         * @return
         */
        public RedisManager redisManager() {
            RedisManager redisManager = new RedisManager();
            redisManager.setHost(host);
            redisManager.setPort(port);
            redisManager.setExpire(1800);// 配置缓存过期时间
            redisManager.setTimeout(timeout);
            // redisManager.setPassword(password);
            return redisManager;
        }
    
        /**
         * Session Manager
         * 使用的是shiro-redis开源插件
         */
        @Bean(name = "sessionManager")
        public DefaultWebSessionManager sessionManager() {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setSessionDAO(redisSessionDAO());
            return sessionManager;
        }
    
        /**
         * RedisSessionDAO shiro sessionDao层的实现 通过redis
         * 使用的是shiro-redis开源插件
         */
        @Bean
        public RedisSessionDAO redisSessionDAO() {
            RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    
            redisSessionDAO.setRedisManager(redisManager());
            return redisSessionDAO;
        }
    
        /**
         * cookie管理对象;记住我功能
         *
         * @return
         */
        public CookieRememberMeManager rememberMeManager() {
            CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
            cookieRememberMeManager.setCookie(rememberMeCookie());
            //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
            cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
            return cookieRememberMeManager;
        }
    
        /**
         * cookie对象;
         *
         * @return
         */
        public SimpleCookie rememberMeCookie() {
            //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
            SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
            //<!-- 记住我cookie生效时间30天 ,单位秒;-->
            simpleCookie.setMaxAge(2592000);
            return simpleCookie;
        }
    
        /**
         * 限制同一账号登录同时登录人数控制
         *
         * @return
         */
        public KickoutSessionControlFilter kickoutSessionControlFilter() {
            KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
            //使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;
            //这里我们还是用之前shiro使用的redisManager()实现的cacheManager()缓存管理
            //也可以重新另写一个,重新配置缓存时间之类的自定义缓存属性
            kickoutSessionControlFilter.setCacheManager(cacheManager());
            //用于根据会话ID,获取会话进行踢出操作的;
            kickoutSessionControlFilter.setSessionManager(sessionManager());
            //是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;踢出顺序。
            kickoutSessionControlFilter.setKickoutAfter(false);
            //同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
            kickoutSessionControlFilter.setMaxSession(1);
            //被踢出后重定向到的地址;
            kickoutSessionControlFilter.setKickoutUrl("kickout");
            return kickoutSessionControlFilter;
        }
    }

    未授权 异常捕获:

    package com.example.springboot.shiro.common.exception;
    
    import org.apache.shiro.authz.UnauthorizedException;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.servlet.ModelAndView;
    
    /**
     *   未授权异常 捕获
     */
    @ControllerAdvice
    public class DefaultExceptionHandler {
        @ExceptionHandler({UnauthorizedException.class})
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
            ModelAndView mv = new ModelAndView();
            mv.addObject("ex", e);
            mv.setViewName("unauthorized");
            return mv;
        }
    }

     

     

     

     

    展开全文
  • 用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。...本章将介绍如何在Shiro中完成动态URL权限控制。   本章代码基于

    用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。Shiro也支持类似的机制,不过需要稍微改造下来满足实际需求。不过在Shiro中,更多的是通过AOP进行分散的权限控制,即方法级别的;而通过URL进行权限控制是一种集中的权限控制。本章将介绍如何在Shiro中完成动态URL权限控制。

     

    本章代码基于《第十六章 综合实例》,请先了解相关数据模型及基本流程后再学习本章。

     

    表及数据SQL

    请运行shiro-example-chapter19/sql/ shiro-schema.sql 表结构

    请运行shiro-example-chapter19/sql/ shiro-schema.sql 数据

     

    实体

    具体请参考com.github.zhangkaitao.shiro.chapter19包下的实体。 

    Java代码  收藏代码
    1. public class UrlFilter implements Serializable {  
    2.     private Long id;  
    3.     private String name; //url名称/描述  
    4.     private String url; //地址  
    5.     private String roles; //所需要的角色,可省略  
    6.     private String permissions; //所需要的权限,可省略  
    7. }   

    表示拦截的URL和角色/权限之间的关系,多个角色/权限之间通过逗号分隔,此处还可以扩展其他的关系,另外可以加如available属性表示是否开启该拦截。

     

    DAO

    具体请参考com.github.zhangkaitao.shiro.chapter19.dao包下的DAO接口及实现。

     

    Service

    具体请参考com.github.zhangkaitao.shiro.chapter19.service包下的Service接口及实现。  

    Java代码  收藏代码
    1. public interface UrlFilterService {  
    2.     public UrlFilter createUrlFilter(UrlFilter urlFilter);  
    3.     public UrlFilter updateUrlFilter(UrlFilter urlFilter);  
    4.     public void deleteUrlFilter(Long urlFilterId);  
    5.     public UrlFilter findOne(Long urlFilterId);  
    6.     public List<UrlFilter> findAll();  
    7. }  

    基本的URL拦截的增删改查实现。 

     

    Java代码  收藏代码
    1. @Service  
    2. public class UrlFilterServiceImpl implements UrlFilterService {  
    3.     @Autowired  
    4. private ShiroFilerChainManager shiroFilerChainManager;  
    5.   
    6.     @Override  
    7.     public UrlFilter createUrlFilter(UrlFilter urlFilter) {  
    8.         urlFilterDao.createUrlFilter(urlFilter);  
    9.         initFilterChain();  
    10.         return urlFilter;  
    11.     }  
    12.     //其他方法请参考源码  
    13.     @PostConstruct  
    14.     public void initFilterChain() {  
    15.         shiroFilerChainManager.initFilterChains(findAll());  
    16.     }  
    17. }   

    UrlFilterServiceImpl在进行新增、修改、删除时会调用initFilterChain来重新初始化Shiro的URL拦截器链,即同步数据库中的URL拦截器定义到Shiro中。此处也要注意如果直接修改数据库是不会起作用的,因为只要调用这几个Service方法时才同步。另外当容器启动时会自动回调initFilterChain来完成容器启动后的URL拦截器的注册。

      

    ShiroFilerChainManager 

    Java代码  收藏代码
    1. @Service  
    2. public class ShiroFilerChainManager {  
    3.     @Autowired private DefaultFilterChainManager filterChainManager;  
    4.     private Map<String, NamedFilterList> defaultFilterChains;  
    5.     @PostConstruct  
    6.     public void init() {  
    7.         defaultFilterChains =   
    8.           new HashMap<String, NamedFilterList>(filterChainManager.getFilterChains());  
    9.     }  
    10.     public void initFilterChains(List<UrlFilter> urlFilters) {  
    11.         //1、首先删除以前老的filter chain并注册默认的  
    12.         filterChainManager.getFilterChains().clear();  
    13.         if(defaultFilterChains != null) {  
    14.             filterChainManager.getFilterChains().putAll(defaultFilterChains);  
    15.         }  
    16.         //2、循环URL Filter 注册filter chain  
    17.         for (UrlFilter urlFilter : urlFilters) {  
    18.             String url = urlFilter.getUrl();  
    19.             //注册roles filter  
    20.             if (!StringUtils.isEmpty(urlFilter.getRoles())) {  
    21.                 filterChainManager.addToChain(url, "roles", urlFilter.getRoles());  
    22.             }  
    23.             //注册perms filter  
    24.             if (!StringUtils.isEmpty(urlFilter.getPermissions())) {  
    25.                 filterChainManager.addToChain(url, "perms", urlFilter.getPermissions());  
    26.             }  
    27.         }  
    28.     }  
    29. }   

    1、init:Spring容器启动时会调用init方法把在spring配置文件中配置的默认拦截器保存下来,之后会自动与数据库中的配置进行合并。

    2、initFilterChains:UrlFilterServiceImpl会在Spring容器启动或进行增删改UrlFilter时进行注册URL拦截器到Shiro。

     

    拦截器及拦截器链知识请参考《第八章 拦截器机制》,此处再介绍下Shiro拦截器的流程:

    AbstractShiroFilter //如ShiroFilter/ SpringShiroFilter都继承该Filter

       doFilter //Filter的doFilter

         doFilterInternal //转调doFilterInternal

           executeChain(request, response, chain) //执行拦截器链

             FilterChain chain = getExecutionChain(request, response, origChain) //使用原始拦截器链获取新的拦截器链

               chain.doFilter(request, response) //执行新组装的拦截器链

     

    getExecutionChain(request, response, origChain) //获取拦截器链流程

           FilterChainResolver resolver = getFilterChainResolver(); //获取相应的FilterChainResolver

           FilterChain resolved = resolver.getChain(request, response, origChain); //通过FilterChainResolver根据当前请求解析到新的FilterChain拦截器链

     

    默认情况下如使用ShiroFilterFactoryBean创建shiroFilter时,默认使用PathMatchingFilterChainResolver进行解析,而它默认是根据当前请求的URL获取相应的拦截器链,使用Ant模式进行URL匹配;默认使用DefaultFilterChainManager进行拦截器链的管理。

     

    PathMatchingFilterChainResolver默认流程:

    Java代码  收藏代码
    1. public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {  
    2.     //1、首先获取拦截器链管理器  
    3.     FilterChainManager filterChainManager = getFilterChainManager();  
    4.     if (!filterChainManager.hasChains()) {  
    5.         return null;  
    6.     }  
    7.     //2、接着获取当前请求的URL(不带上下文)  
    8.     String requestURI = getPathWithinApplication(request);  
    9.     //3、循环拦截器管理器中的拦截器定义(拦截器链的名字就是URL模式)  
    10.     for (String pathPattern : filterChainManager.getChainNames()) {  
    11.         //4、如当前URL匹配拦截器名字(URL模式)  
    12.         if (pathMatches(pathPattern, requestURI)) {  
    13.             //5、返回该URL模式定义的拦截器链  
    14.             return filterChainManager.proxy(originalChain, pathPattern);  
    15.         }  
    16.     }  
    17.     return null;  
    18. }   

    默认实现有点小问题:

    如果多个拦截器链都匹配了当前请求URL,那么只返回第一个找到的拦截器链;后续我们可以修改此处的代码,将多个匹配的拦截器链合并返回。

     

    DefaultFilterChainManager内部使用Map来管理URL模式-拦截器链的关系;也就是说相同的URL模式只能定义一个拦截器链,不能重复定义;而且如果多个拦截器链都匹配时是无序的(因为使用map.keySet()获取拦截器链的名字,即URL模式)。

     

    FilterChainManager接口: 

    Java代码  收藏代码
    1. public interface FilterChainManager {  
    2.     Map<String, Filter> getFilters(); //得到注册的拦截器  
    3.     void addFilter(String name, Filter filter); //注册拦截器  
    4.     void addFilter(String name, Filter filter, boolean init); //注册拦截器  
    5.     void createChain(String chainName, String chainDefinition); //根据拦截器链定义创建拦截器链  
    6.     void addToChain(String chainName, String filterName); //添加拦截器到指定的拦截器链  
    7.     void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException; //添加拦截器(带有配置的)到指定的拦截器链  
    8.     NamedFilterList getChain(String chainName); //获取拦截器链  
    9.     boolean hasChains(); //是否有拦截器链  
    10.     Set<String> getChainNames(); //得到所有拦截器链的名字  
    11.     FilterChain proxy(FilterChain original, String chainName); //使用指定的拦截器链代理原始拦截器链  
    12. }   

    此接口主要三个功能:注册拦截器,注册拦截器链,对原始拦截器链生成代理之后的拦截器链,比如  

    Java代码  收藏代码
    1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    2. ……  
    3.     <property name="filters">  
    4.         <util:map>  
    5.             <entry key="authc" value-ref="formAuthenticationFilter"/>  
    6.             <entry key="sysUser" value-ref="sysUserFilter"/>  
    7.         </util:map>  
    8.     </property>  
    9.     <property name="filterChainDefinitions">  
    10.         <value>  
    11.             /login = authc  
    12.             /logout = logout  
    13.             /authenticated = authc  
    14.             /** = user,sysUser  
    15.         </value>  
    16.     </property>  
    17. </bean>   

    filters属性定义了拦截器;filterChainDefinitions定义了拦截器链;如/**就是拦截器链的名字;而user,sysUser就是拦截器名字列表。

     

    之前说过默认的PathMatchingFilterChainResolver和DefaultFilterChainManager不能满足我们的需求,我们稍微扩展了一下:

      

    CustomPathMatchingFilterChainResolver 

    Java代码  收藏代码
    1. public class CustomPathMatchingFilterChainResolver  
    2.              extends PathMatchingFilterChainResolver {  
    3.   private CustomDefaultFilterChainManager customDefaultFilterChainManager;  
    4.   public void setCustomDefaultFilterChainManager(  
    5.         CustomDefaultFilterChainManager customDefaultFilterChainManager) {  
    6.       this.customDefaultFilterChainManager = customDefaultFilterChainManager;  
    7.       setFilterChainManager(customDefaultFilterChainManager);  
    8.   }  
    9.   
    10.   public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {  
    11.       FilterChainManager filterChainManager = getFilterChainManager();  
    12.       if (!filterChainManager.hasChains()) {  
    13.           return null;  
    14.       }  
    15.       String requestURI = getPathWithinApplication(request);  
    16.       List<String> chainNames = new ArrayList<String>();  
    17.       for (String pathPattern : filterChainManager.getChainNames()) {  
    18.         if (pathMatches(pathPattern, requestURI)) {  
    19.         chainNames.add(pathPattern);  
    20.         }  
    21.       }  
    22.       if(chainNames.size() == 0) {  
    23.         return null;  
    24.       }  
    25.       return customDefaultFilterChainManager.proxy(originalChain, chainNames);  
    26.   }  
    27. }   

    和默认的PathMatchingFilterChainResolver区别是,此处得到所有匹配的拦截器链,然后通过调用CustomDefaultFilterChainManager.proxy(originalChain, chainNames)进行合并后代理。

     

    CustomDefaultFilterChainManager    

    Java代码  收藏代码
    1. public class CustomDefaultFilterChainManager extends DefaultFilterChainManager {  
    2.     private Map<String, String> filterChainDefinitionMap = null;  
    3.     private String loginUrl;  
    4.     private String successUrl;  
    5.     private String unauthorizedUrl;  
    6.     public CustomDefaultFilterChainManager() {  
    7.         setFilters(new LinkedHashMap<String, Filter>());  
    8.         setFilterChains(new LinkedHashMap<String, NamedFilterList>());  
    9.         addDefaultFilters(true);  
    10.     }  
    11.     public Map<String, String> getFilterChainDefinitionMap() {  
    12.         return filterChainDefinitionMap;  
    13.     }  
    14.     public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {  
    15.         this.filterChainDefinitionMap = filterChainDefinitionMap;  
    16.     }  
    17.     public void setCustomFilters(Map<String, Filter> customFilters) {  
    18.         for(Map.Entry<String, Filter> entry : customFilters.entrySet()) {  
    19.             addFilter(entry.getKey(), entry.getValue(), false);  
    20.         }  
    21. }  
    22.     public void setDefaultFilterChainDefinitions(String definitions) {  
    23.         Ini ini = new Ini();  
    24.         ini.load(definitions);  
    25.         Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);  
    26.         if (CollectionUtils.isEmpty(section)) {  
    27.             section = ini.getSection(Ini.DEFAULT_SECTION_NAME);  
    28.         }  
    29.         setFilterChainDefinitionMap(section);  
    30.     }  
    31.     public String getLoginUrl() {  
    32.         return loginUrl;  
    33.     }  
    34.     public void setLoginUrl(String loginUrl) {  
    35.         this.loginUrl = loginUrl;  
    36.     }  
    37.     public String getSuccessUrl() {  
    38.         return successUrl;  
    39.     }  
    40.     public void setSuccessUrl(String successUrl) {  
    41.         this.successUrl = successUrl;  
    42.     }  
    43.     public String getUnauthorizedUrl() {  
    44.         return unauthorizedUrl;  
    45.     }  
    46.     public void setUnauthorizedUrl(String unauthorizedUrl) {  
    47.         this.unauthorizedUrl = unauthorizedUrl;  
    48.     }  
    49.     @PostConstruct  
    50.     public void init() {  
    51.         Map<String, Filter> filters = getFilters();  
    52.         if (!CollectionUtils.isEmpty(filters)) {  
    53.             for (Map.Entry<String, Filter> entry : filters.entrySet()) {  
    54.                 String name = entry.getKey();  
    55.                 Filter filter = entry.getValue();  
    56.                 applyGlobalPropertiesIfNecessary(filter);  
    57.                 if (filter instanceof Nameable) {  
    58.                     ((Nameable) filter).setName(name);  
    59.                 }  
    60.                 addFilter(name, filter, false);  
    61.             }  
    62.         }  
    63.         Map<String, String> chains = getFilterChainDefinitionMap();  
    64.         if (!CollectionUtils.isEmpty(chains)) {  
    65.             for (Map.Entry<String, String> entry : chains.entrySet()) {  
    66.                 String url = entry.getKey();  
    67.                 String chainDefinition = entry.getValue();  
    68.                 createChain(url, chainDefinition);  
    69.             }  
    70.         }  
    71.     }  
    72.     protected void initFilter(Filter filter) {  
    73.         //ignore   
    74.     }  
    75.   
    76.     public FilterChain proxy(FilterChain original, List<String> chainNames) {  
    77.         NamedFilterList configured = new SimpleNamedFilterList(chainNames.toString());  
    78.         for(String chainName : chainNames) {  
    79.             configured.addAll(getChain(chainName));  
    80.         }  
    81.         return configured.proxy(original);  
    82.     }  
    83.     private void applyGlobalPropertiesIfNecessary(Filter filter) {  
    84.         applyLoginUrlIfNecessary(filter);  
    85.         applySuccessUrlIfNecessary(filter);  
    86.         applyUnauthorizedUrlIfNecessary(filter);  
    87.     }  
    88.     private void applyLoginUrlIfNecessary(Filter filter) {  
    89.         //请参考源码  
    90.     }  
    91.     private void applySuccessUrlIfNecessary(Filter filter) {  
    92.         //请参考源码  
    93.     }  
    94.     private void applyUnauthorizedUrlIfNecessary(Filter filter) {  
    95.         //请参考源码  
    96.     }  
    97. }   

    1、CustomDefaultFilterChainManager:调用其构造器时,会自动注册默认的拦截器;

    2、loginUrl、successUrl、unauthorizedUrl:分别对应登录地址、登录成功后默认跳转地址、未授权跳转地址,用于给相应拦截器的;

    3、filterChainDefinitionMap:用于存储如ShiroFilterFactoryBean在配置文件中配置的拦截器链定义,即可以认为是默认的静态拦截器链;会自动与数据库中加载的合并;

    4、setDefaultFilterChainDefinitions:解析配置文件中传入的字符串拦截器链配置,解析为相应的拦截器链;

    5、setCustomFilters:注册我们自定义的拦截器;如ShiroFilterFactoryBean的filters属性;

    6、init:初始化方法,Spring容器启动时会调用,首先其会自动给相应的拦截器设置如loginUrl、successUrl、unauthorizedUrl;其次根据filterChainDefinitionMap构建默认的拦截器链;

    7、initFilter:此处我们忽略实现initFilter,因为交给spring管理了,所以Filter的相关配置会在Spring配置中完成;

    8、proxy:组合多个拦截器链为一个生成一个新的FilterChain代理。

     

    Web层控制器 

    请参考com.github.zhangkaitao.shiro.chapter19.web.controller包,相对于第十六章添加了UrlFilterController用于UrlFilter的维护。另外,移除了控制器方法上的权限注解,而是使用动态URL拦截进行控制。

     

    Spring配置——spring-config-shiro.xml   

    Java代码  收藏代码
    1. <bean id="filterChainManager"   
    2.     class="com.github.zhangkaitao.shiro.spring.CustomDefaultFilterChainManager">  
    3.     <property name="loginUrl" value="/login"/>  
    4.     <property name="successUrl" value="/"/>  
    5.     <property name="unauthorizedUrl" value="/unauthorized.jsp"/>  
    6.     <property name="customFilters">  
    7.         <util:map>  
    8.             <entry key="authc" value-ref="formAuthenticationFilter"/>  
    9.             <entry key="sysUser" value-ref="sysUserFilter"/>  
    10.         </util:map>  
    11.     </property>  
    12.     <property name="defaultFilterChainDefinitions">  
    13.         <value>  
    14.             /login = authc  
    15.             /logout = logout  
    16.             /unauthorized.jsp = authc  
    17.             /** = user,sysUser  
    18.         </value>  
    19.     </property>  
    20. </bean>   

    filterChainManager是我们自定义的CustomDefaultFilterChainManager,注册相应的拦截器及默认的拦截器链。 

    Java代码  收藏代码
    1. <bean id="filterChainResolver"   
    2.     class="com.github.zhangkaitao.shiro.spring.CustomPathMatchingFilterChainResolver">  
    3.     <property name="customDefaultFilterChainManager" ref="filterChainManager"/>  
    4. </bean>   

    filterChainResolver是自定义的CustomPathMatchingFilterChainResolver,使用上边的filterChainManager进行拦截器链的管理。 

    Java代码  收藏代码
    1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    2.     <property name="securityManager" ref="securityManager"/>  
    3. </bean>   

    shiroFilter不再定义filters及filterChainDefinitions,而是交给了filterChainManager进行完成。 

    Java代码  收藏代码
    1. <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">  
    2.     <property name="targetObject" ref="shiroFilter"/>  
    3.     <property name="targetMethod" value="setFilterChainResolver"/>  
    4.     <property name="arguments" ref="filterChainResolver"/>  
    5. </bean>   

    最后把filterChainResolver注册给shiroFilter,其使用它进行动态URL权限控制。

     

    其他配置和第十六章一样,请参考第十六章。

     

    测试

    1、首先执行shiro-data.sql初始化数据。

    2、然后再URL管理中新增如下数据: 

    3、访问http://localhost:8080/chapter19/user时要求用户拥有aa角色,此时是没有的所以会跳转到未授权页面;

    4、添加aa角色然后授权给用户,此时就有权限访问http://localhost:8080/chapter19/user。

     

    实际项目可以在此基础上进行扩展。

    展开全文
  • 用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。...本章将介绍如何在Shiro中完成动态URL权限控制。 本章代码基于《第十六章...

    用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。Shiro也支持类似的机制,不过需要稍微改造下来满足实际需求。不过在Shiro中,更多的是通过AOP进行分散的权限控制,即方法级别的;而通过URL进行权限控制是一种集中的权限控制。本章将介绍如何在Shiro中完成动态URL权限控制。

     

    本章代码基于《第十六章 综合实例》,请先了解相关数据模型及基本流程后再学习本章。

     

    表及数据SQL

    请运行shiro-example-chapter19/sql/ shiro-schema.sql 表结构

    请运行shiro-example-chapter19/sql/ shiro-schema.sql 数据

     

    实体

    具体请参考com.github.zhangkaitao.shiro.chapter19包下的实体。 

    Java代码  收藏代码
    1. public class UrlFilter implements Serializable {  
    2.     private Long id;  
    3.     private String name; //url名称/描述  
    4.     private String url; //地址  
    5.     private String roles; //所需要的角色,可省略  
    6.     private String permissions; //所需要的权限,可省略  
    7. }   

    表示拦截的URL和角色/权限之间的关系,多个角色/权限之间通过逗号分隔,此处还可以扩展其他的关系,另外可以加如available属性表示是否开启该拦截。

     

    DAO

    具体请参考com.github.zhangkaitao.shiro.chapter19.dao包下的DAO接口及实现。

     

    Service

    具体请参考com.github.zhangkaitao.shiro.chapter19.service包下的Service接口及实现。  

    Java代码  收藏代码
    1. public interface UrlFilterService {  
    2.     public UrlFilter createUrlFilter(UrlFilter urlFilter);  
    3.     public UrlFilter updateUrlFilter(UrlFilter urlFilter);  
    4.     public void deleteUrlFilter(Long urlFilterId);  
    5.     public UrlFilter findOne(Long urlFilterId);  
    6.     public List<UrlFilter> findAll();  
    7. }  

    基本的URL拦截的增删改查实现。 

     

    Java代码  收藏代码
    1. @Service  
    2. public class UrlFilterServiceImpl implements UrlFilterService {  
    3.     @Autowired  
    4. private ShiroFilerChainManager shiroFilerChainManager;  
    5.   
    6.     @Override  
    7.     public UrlFilter createUrlFilter(UrlFilter urlFilter) {  
    8.         urlFilterDao.createUrlFilter(urlFilter);  
    9.         initFilterChain();  
    10.         return urlFilter;  
    11.     }  
    12.     //其他方法请参考源码  
    13.     @PostConstruct  
    14.     public void initFilterChain() {  
    15.         shiroFilerChainManager.initFilterChains(findAll());  
    16.     }  
    17. }   

    UrlFilterServiceImpl在进行新增、修改、删除时会调用initFilterChain来重新初始化Shiro的URL拦截器链,即同步数据库中的URL拦截器定义到Shiro中。此处也要注意如果直接修改数据库是不会起作用的,因为只要调用这几个Service方法时才同步。另外当容器启动时会自动回调initFilterChain来完成容器启动后的URL拦截器的注册。

      

    ShiroFilerChainManager 

    Java代码  收藏代码
    1. @Service  
    2. public class ShiroFilerChainManager {  
    3.     @Autowired private DefaultFilterChainManager filterChainManager;  
    4.     private Map<String, NamedFilterList> defaultFilterChains;  
    5.     @PostConstruct  
    6.     public void init() {  
    7.         defaultFilterChains =   
    8.           new HashMap<String, NamedFilterList>(filterChainManager.getFilterChains());  
    9.     }  
    10.     public void initFilterChains(List<UrlFilter> urlFilters) {  
    11.         //1、首先删除以前老的filter chain并注册默认的  
    12.         filterChainManager.getFilterChains().clear();  
    13.         if(defaultFilterChains != null) {  
    14.             filterChainManager.getFilterChains().putAll(defaultFilterChains);  
    15.         }  
    16.         //2、循环URL Filter 注册filter chain  
    17.         for (UrlFilter urlFilter : urlFilters) {  
    18.             String url = urlFilter.getUrl();  
    19.             //注册roles filter  
    20.             if (!StringUtils.isEmpty(urlFilter.getRoles())) {  
    21.                 filterChainManager.addToChain(url, "roles", urlFilter.getRoles());  
    22.             }  
    23.             //注册perms filter  
    24.             if (!StringUtils.isEmpty(urlFilter.getPermissions())) {  
    25.                 filterChainManager.addToChain(url, "perms", urlFilter.getPermissions());  
    26.             }  
    27.         }  
    28.     }  
    29. }   

    1、init:Spring容器启动时会调用init方法把在spring配置文件中配置的默认拦截器保存下来,之后会自动与数据库中的配置进行合并。

    2、initFilterChains:UrlFilterServiceImpl会在Spring容器启动或进行增删改UrlFilter时进行注册URL拦截器到Shiro。

     

    拦截器及拦截器链知识请参考《第八章 拦截器机制》,此处再介绍下Shiro拦截器的流程:

    AbstractShiroFilter //如ShiroFilter/ SpringShiroFilter都继承该Filter

       doFilter //Filter的doFilter

         doFilterInternal //转调doFilterInternal

           executeChain(request, response, chain) //执行拦截器链

             FilterChain chain = getExecutionChain(request, response, origChain) //使用原始拦截器链获取新的拦截器链

               chain.doFilter(request, response) //执行新组装的拦截器链

     

    getExecutionChain(request, response, origChain) //获取拦截器链流程

           FilterChainResolver resolver = getFilterChainResolver(); //获取相应的FilterChainResolver

           FilterChain resolved = resolver.getChain(request, response, origChain); //通过FilterChainResolver根据当前请求解析到新的FilterChain拦截器链

     

    默认情况下如使用ShiroFilterFactoryBean创建shiroFilter时,默认使用PathMatchingFilterChainResolver进行解析,而它默认是根据当前请求的URL获取相应的拦截器链,使用Ant模式进行URL匹配;默认使用DefaultFilterChainManager进行拦截器链的管理。

     

    PathMatchingFilterChainResolver默认流程:

    Java代码  收藏代码
    1. public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {  
    2.     //1、首先获取拦截器链管理器  
    3.     FilterChainManager filterChainManager = getFilterChainManager();  
    4.     if (!filterChainManager.hasChains()) {  
    5.         return null;  
    6.     }  
    7.     //2、接着获取当前请求的URL(不带上下文)  
    8.     String requestURI = getPathWithinApplication(request);  
    9.     //3、循环拦截器管理器中的拦截器定义(拦截器链的名字就是URL模式)  
    10.     for (String pathPattern : filterChainManager.getChainNames()) {  
    11.         //4、如当前URL匹配拦截器名字(URL模式)  
    12.         if (pathMatches(pathPattern, requestURI)) {  
    13.             //5、返回该URL模式定义的拦截器链  
    14.             return filterChainManager.proxy(originalChain, pathPattern);  
    15.         }  
    16.     }  
    17.     return null;  
    18. }   

    默认实现有点小问题:

    如果多个拦截器链都匹配了当前请求URL,那么只返回第一个找到的拦截器链;后续我们可以修改此处的代码,将多个匹配的拦截器链合并返回。

     

    DefaultFilterChainManager内部使用Map来管理URL模式-拦截器链的关系;也就是说相同的URL模式只能定义一个拦截器链,不能重复定义;而且如果多个拦截器链都匹配时是无序的(因为使用map.keySet()获取拦截器链的名字,即URL模式)。

     

    FilterChainManager接口: 

    Java代码  收藏代码
    1. public interface FilterChainManager {  
    2.     Map<String, Filter> getFilters(); //得到注册的拦截器  
    3.     void addFilter(String name, Filter filter); //注册拦截器  
    4.     void addFilter(String name, Filter filter, boolean init); //注册拦截器  
    5.     void createChain(String chainName, String chainDefinition); //根据拦截器链定义创建拦截器链  
    6.     void addToChain(String chainName, String filterName); //添加拦截器到指定的拦截器链  
    7.     void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException; //添加拦截器(带有配置的)到指定的拦截器链  
    8.     NamedFilterList getChain(String chainName); //获取拦截器链  
    9.     boolean hasChains(); //是否有拦截器链  
    10.     Set<String> getChainNames(); //得到所有拦截器链的名字  
    11.     FilterChain proxy(FilterChain original, String chainName); //使用指定的拦截器链代理原始拦截器链  
    12. }   

    此接口主要三个功能:注册拦截器,注册拦截器链,对原始拦截器链生成代理之后的拦截器链,比如  

    Java代码  收藏代码
    1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    2. ……  
    3.     <property name="filters">  
    4.         <util:map>  
    5.             <entry key="authc" value-ref="formAuthenticationFilter"/>  
    6.             <entry key="sysUser" value-ref="sysUserFilter"/>  
    7.         </util:map>  
    8.     </property>  
    9.     <property name="filterChainDefinitions">  
    10.         <value>  
    11.             /login = authc  
    12.             /logout = logout  
    13.             /authenticated = authc  
    14.             /** = user,sysUser  
    15.         </value>  
    16.     </property>  
    17. </bean>   

    filters属性定义了拦截器;filterChainDefinitions定义了拦截器链;如/**就是拦截器链的名字;而user,sysUser就是拦截器名字列表。

     

    之前说过默认的PathMatchingFilterChainResolver和DefaultFilterChainManager不能满足我们的需求,我们稍微扩展了一下:

      

    CustomPathMatchingFilterChainResolver 

    Java代码  收藏代码
    1. public class CustomPathMatchingFilterChainResolver  
    2.              extends PathMatchingFilterChainResolver {  
    3.   private CustomDefaultFilterChainManager customDefaultFilterChainManager;  
    4.   public void setCustomDefaultFilterChainManager(  
    5.         CustomDefaultFilterChainManager customDefaultFilterChainManager) {  
    6.       this.customDefaultFilterChainManager = customDefaultFilterChainManager;  
    7.       setFilterChainManager(customDefaultFilterChainManager);  
    8.   }  
    9.   
    10.   public FilterChain getChain(ServletRequest request, ServletResponse response, FilterChain originalChain) {  
    11.       FilterChainManager filterChainManager = getFilterChainManager();  
    12.       if (!filterChainManager.hasChains()) {  
    13.           return null;  
    14.       }  
    15.       String requestURI = getPathWithinApplication(request);  
    16.       List<String> chainNames = new ArrayList<String>();  
    17.       for (String pathPattern : filterChainManager.getChainNames()) {  
    18.         if (pathMatches(pathPattern, requestURI)) {  
    19.         chainNames.add(pathPattern);  
    20.         }  
    21.       }  
    22.       if(chainNames.size() == 0) {  
    23.         return null;  
    24.       }  
    25.       return customDefaultFilterChainManager.proxy(originalChain, chainNames);  
    26.   }  
    27. }   

    和默认的PathMatchingFilterChainResolver区别是,此处得到所有匹配的拦截器链,然后通过调用CustomDefaultFilterChainManager.proxy(originalChain, chainNames)进行合并后代理。

     

    CustomDefaultFilterChainManager    

    Java代码  收藏代码
    1. public class CustomDefaultFilterChainManager extends DefaultFilterChainManager {  
    2.     private Map<String, String> filterChainDefinitionMap = null;  
    3.     private String loginUrl;  
    4.     private String successUrl;  
    5.     private String unauthorizedUrl;  
    6.     public CustomDefaultFilterChainManager() {  
    7.         setFilters(new LinkedHashMap<String, Filter>());  
    8.         setFilterChains(new LinkedHashMap<String, NamedFilterList>());  
    9.         addDefaultFilters(true);  
    10.     }  
    11.     public Map<String, String> getFilterChainDefinitionMap() {  
    12.         return filterChainDefinitionMap;  
    13.     }  
    14.     public void setFilterChainDefinitionMap(Map<String, String> filterChainDefinitionMap) {  
    15.         this.filterChainDefinitionMap = filterChainDefinitionMap;  
    16.     }  
    17.     public void setCustomFilters(Map<String, Filter> customFilters) {  
    18.         for(Map.Entry<String, Filter> entry : customFilters.entrySet()) {  
    19.             addFilter(entry.getKey(), entry.getValue(), false);  
    20.         }  
    21. }  
    22.     public void setDefaultFilterChainDefinitions(String definitions) {  
    23.         Ini ini = new Ini();  
    24.         ini.load(definitions);  
    25.         Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);  
    26.         if (CollectionUtils.isEmpty(section)) {  
    27.             section = ini.getSection(Ini.DEFAULT_SECTION_NAME);  
    28.         }  
    29.         setFilterChainDefinitionMap(section);  
    30.     }  
    31.     public String getLoginUrl() {  
    32.         return loginUrl;  
    33.     }  
    34.     public void setLoginUrl(String loginUrl) {  
    35.         this.loginUrl = loginUrl;  
    36.     }  
    37.     public String getSuccessUrl() {  
    38.         return successUrl;  
    39.     }  
    40.     public void setSuccessUrl(String successUrl) {  
    41.         this.successUrl = successUrl;  
    42.     }  
    43.     public String getUnauthorizedUrl() {  
    44.         return unauthorizedUrl;  
    45.     }  
    46.     public void setUnauthorizedUrl(String unauthorizedUrl) {  
    47.         this.unauthorizedUrl = unauthorizedUrl;  
    48.     }  
    49.     @PostConstruct  
    50.     public void init() {  
    51.         Map<String, Filter> filters = getFilters();  
    52.         if (!CollectionUtils.isEmpty(filters)) {  
    53.             for (Map.Entry<String, Filter> entry : filters.entrySet()) {  
    54.                 String name = entry.getKey();  
    55.                 Filter filter = entry.getValue();  
    56.                 applyGlobalPropertiesIfNecessary(filter);  
    57.                 if (filter instanceof Nameable) {  
    58.                     ((Nameable) filter).setName(name);  
    59.                 }  
    60.                 addFilter(name, filter, false);  
    61.             }  
    62.         }  
    63.         Map<String, String> chains = getFilterChainDefinitionMap();  
    64.         if (!CollectionUtils.isEmpty(chains)) {  
    65.             for (Map.Entry<String, String> entry : chains.entrySet()) {  
    66.                 String url = entry.getKey();  
    67.                 String chainDefinition = entry.getValue();  
    68.                 createChain(url, chainDefinition);  
    69.             }  
    70.         }  
    71.     }  
    72.     protected void initFilter(Filter filter) {  
    73.         //ignore   
    74.     }  
    75.   
    76.     public FilterChain proxy(FilterChain original, List<String> chainNames) {  
    77.         NamedFilterList configured = new SimpleNamedFilterList(chainNames.toString());  
    78.         for(String chainName : chainNames) {  
    79.             configured.addAll(getChain(chainName));  
    80.         }  
    81.         return configured.proxy(original);  
    82.     }  
    83.     private void applyGlobalPropertiesIfNecessary(Filter filter) {  
    84.         applyLoginUrlIfNecessary(filter);  
    85.         applySuccessUrlIfNecessary(filter);  
    86.         applyUnauthorizedUrlIfNecessary(filter);  
    87.     }  
    88.     private void applyLoginUrlIfNecessary(Filter filter) {  
    89.         //请参考源码  
    90.     }  
    91.     private void applySuccessUrlIfNecessary(Filter filter) {  
    92.         //请参考源码  
    93.     }  
    94.     private void applyUnauthorizedUrlIfNecessary(Filter filter) {  
    95.         //请参考源码  
    96.     }  
    97. }   

    1、CustomDefaultFilterChainManager:调用其构造器时,会自动注册默认的拦截器;

    2、loginUrl、successUrl、unauthorizedUrl:分别对应登录地址、登录成功后默认跳转地址、未授权跳转地址,用于给相应拦截器的;

    3、filterChainDefinitionMap:用于存储如ShiroFilterFactoryBean在配置文件中配置的拦截器链定义,即可以认为是默认的静态拦截器链;会自动与数据库中加载的合并;

    4、setDefaultFilterChainDefinitions:解析配置文件中传入的字符串拦截器链配置,解析为相应的拦截器链;

    5、setCustomFilters:注册我们自定义的拦截器;如ShiroFilterFactoryBean的filters属性;

    6、init:初始化方法,Spring容器启动时会调用,首先其会自动给相应的拦截器设置如loginUrl、successUrl、unauthorizedUrl;其次根据filterChainDefinitionMap构建默认的拦截器链;

    7、initFilter:此处我们忽略实现initFilter,因为交给spring管理了,所以Filter的相关配置会在Spring配置中完成;

    8、proxy:组合多个拦截器链为一个生成一个新的FilterChain代理。

     

    Web层控制器 

    请参考com.github.zhangkaitao.shiro.chapter19.web.controller包,相对于第十六章添加了UrlFilterController用于UrlFilter的维护。另外,移除了控制器方法上的权限注解,而是使用动态URL拦截进行控制。

     

    Spring配置——spring-config-shiro.xml   

    Java代码  收藏代码
    1. <bean id="filterChainManager"   
    2.     class="com.github.zhangkaitao.shiro.spring.CustomDefaultFilterChainManager">  
    3.     <property name="loginUrl" value="/login"/>  
    4.     <property name="successUrl" value="/"/>  
    5.     <property name="unauthorizedUrl" value="/unauthorized.jsp"/>  
    6.     <property name="customFilters">  
    7.         <util:map>  
    8.             <entry key="authc" value-ref="formAuthenticationFilter"/>  
    9.             <entry key="sysUser" value-ref="sysUserFilter"/>  
    10.         </util:map>  
    11.     </property>  
    12.     <property name="defaultFilterChainDefinitions">  
    13.         <value>  
    14.             /login = authc  
    15.             /logout = logout  
    16.             /unauthorized.jsp = authc  
    17.             /** = user,sysUser  
    18.         </value>  
    19.     </property>  
    20. </bean>   

    filterChainManager是我们自定义的CustomDefaultFilterChainManager,注册相应的拦截器及默认的拦截器链。 

    Java代码  收藏代码
    1. <bean id="filterChainResolver"   
    2.     class="com.github.zhangkaitao.shiro.spring.CustomPathMatchingFilterChainResolver">  
    3.     <property name="customDefaultFilterChainManager" ref="filterChainManager"/>  
    4. </bean>   

    filterChainResolver是自定义的CustomPathMatchingFilterChainResolver,使用上边的filterChainManager进行拦截器链的管理。 

    Java代码  收藏代码
    1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    2.     <property name="securityManager" ref="securityManager"/>  
    3. </bean>   

    shiroFilter不再定义filters及filterChainDefinitions,而是交给了filterChainManager进行完成。 

    Java代码  收藏代码
    1. <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">  
    2.     <property name="targetObject" ref="shiroFilter"/>  
    3.     <property name="targetMethod" value="setFilterChainResolver"/>  
    4.     <property name="arguments" ref="filterChainResolver"/>  
    5. </bean>   

    最后把filterChainResolver注册给shiroFilter,其使用它进行动态URL权限控制。

     

    其他配置和第十六章一样,请参考第十六章。

     

    测试

    1、首先执行shiro-data.sql初始化数据。

    2、然后再URL管理中新增如下数据: 

    3、访问http://localhost:8080/chapter19/user时要求用户拥有aa角色,此时是没有的所以会跳转到未授权页面;

    4、添加aa角色然后授权给用户,此时就有权限访问http://localhost:8080/chapter19/user。

     

    实际项目可以在此基础上进行扩展。

     

    展开全文
  • shiro动态控制url资源

    2018-04-09 11:43:00
    所谓动态控制url就是url权限控制不是手动写死在配置文件中,而是根据数据库的变化而变化。 表结构: user2:用户表 t_role:角色表 t_user_role:用户角色表 t_privilege:权限资源表 t_r...

    怎么利用shiro权限动态控制每个url资源呢?主要包括jsp(html)页面、action的url访问,而静态资源和登录资源则可直接访问。

    所谓动态控制url就是url的权限控制不是手动写死在配置文件中,而是根据数据库的变化而变化。

    表结构:

    user2:用户表

    t_role:角色表

    t_user_role:用户角色表

    t_privilege:权限资源表

    t_role_privilege:角色权限资源表

    shiro动态控制url资源:

    applicationContext-shiro.xml配置chainDefinitionSectionMetaSource:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    
        <description>apache shiro配置</description>
        
        <bean id="chainDefinitionSectionMetaSource" class="com.test.www.web.filter.ChainDefinitionSectionMetaSource">
            <property name="filterChainDefinitions">
                   <value>
                       <!-- 静态资源允许访问 -->
                    <!-- /** = anon -->
                    <!-- /app/** = anon
                    /assets/** = anon -->
                    <!-- 登录页允许访问 -->
                       <!--  /user/login.htm = anon -->
                    <!-- /login.jsp = anon  -->
                    <!-- /*.jsp = anon -->
                    <!-- /js/**=anon -->
                    <!-- 登录页面 -->
                    /index2.jsp = anon
                    /js/** = anon
                    /css/** = anon
                    /images/** = anon
                    /assets/** = anon
                    /test/loginAdmin.html=anon
                    /logout=logout  <!-- 这才是对退出的配置 --> 
                    <!-- /test/add.html = perms["user:add"] -->      
                    <!-- /test/add.html = perms["/test/add.html"] -->
                    <!-- 其他资源需要认证 -->
                    <!-- /** = authc -->
                    <!-- /logout=logout -->  <!-- 这才是对退出的配置,不能放在/** = authc的后面 --> 
                    
                </value>
            </property>
        </bean>
        
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="filters">
                <map>
                    <entry key="logout" value-ref="logoutFilter"/>
                </map>
            </property>
            <property name="securityManager" ref="securityManager"/>
           <!--  <property name="loginUrl" value="/index2.jsp"/>
            <property name="successUrl" value="/main/main.htm"/>
            <property name="unauthorizedUrl" value="/page/401.htm"/> -->
            <!-- shiro判断是否登录,没有登录则跳转到登录页面,loginUrl对应登录页面的路径 -->
            <property name="loginUrl" value="/index2.jsp"/>
            <property name="unauthorizedUrl" value="/page/401.htm"/><!-- /page/401.htm -->
            
            <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" />
        </bean>
    
        <!-- 缓存管理器 使用Ehcache实现 -->
        <!-- 
        <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager" p:cacheManagerConfigFile="/WEB-INF/conf/ehcache-shiro.xml">
        </bean>
         -->
        <!-- 会话DAO -->
        <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO"/>
    
        <!-- 会话管理器 -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <property name="sessionDAO" ref="sessionDAO"/>
        </bean>
    
        <!-- 安全管理器 -->
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
            <property name="realms">
                <list>
                    <ref bean="securityRealm"/>
                </list>
            </property>
            <!-- cacheManager,集合spring缓存工厂 -->
            <!-- <property name="cacheManager" ref="shiroEhcacheManager" />
            <property name="sessionManager" ref="sessionManager" /> -->
        </bean>
    
        <!-- Shiro生命周期处理器 -->
        <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    </beans>

    ChainDefinitionSectionMetaSource.java:

    package com.test.www.web.filter;
    
    import java.text.MessageFormat;
    import java.util.List;
    
    import javax.annotation.Resource;
    
    import org.apache.commons.lang3.StringUtils;
    import org.apache.shiro.config.Ini;
    import org.apache.shiro.config.Ini.Section;
    import org.springframework.beans.factory.FactoryBean;
    
    import com.test.www.web.entity.user.TPrivilege;
    import com.test.www.web.service.user.TPrivilegeService;
    
    public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section>{
    
    public static final String PREMISSION_STRING="perms[\"{0}\"]";
        
        private String filterChainDefinitions;
        
        /*private PrivilegeDao privilegeDao;
    
        public PrivilegeDao getPrivilegeDao() {
            return privilegeDao;
        }*/
        
        @Resource
        private TPrivilegeService tPrivilegeService;
        
        public String getFilterChainDefinitions() {
            return filterChainDefinitions;
        }
        
        @Resource
        public void setFilterChainDefinitions(String filterChainDefinitions) {
            String fiter="";//改正后的url配置
            /*List<Privilege> list = privilegeDao.getAll();
            for (Iterator<Privilege> it = list.iterator(); it.hasNext();) {
                Privilege privilege = it.next();
                if(!StringUtils.isEmpty(privilege.getUrl())) {
                    fiter+="/"+privilege.getUrl()+" = authc," +MessageFormat.format(PREMISSION_STRING,privilege.getPerms()) +"\n";
                }//追加beans.xml中已经有的过滤
            }*/
            
            List<TPrivilege> tPrivilegeList = tPrivilegeService.selectAllPrivileges();
            if(tPrivilegeList!=null && tPrivilegeList.size()>0){
                for (TPrivilege tPrivilege : tPrivilegeList) {
                    if(!StringUtils.isEmpty(tPrivilege.getUrl())) {
                        fiter += tPrivilege.getUrl()+" = authc," +MessageFormat.format(PREMISSION_STRING,tPrivilege.getUrl()) +"\n";
                    }//追加beans.xml中已经有的过滤
                }
            }
            
            //对url拦截
            fiter += "/**"+" = authc" +"\n";
            //fiter+="/test/add.html"+" = authc," +MessageFormat.format(PREMISSION_STRING,"/test/add.html") +"\n";
            //fiter+="/js/**"+" = authc," +MessageFormat.format(PREMISSION_STRING,"/js/**") +"\n";
            //fiter+="/js/**"+" = " +MessageFormat.format(PREMISSION_STRING,"/js/**") +"\n";
            System.out.println(filterChainDefinitions+fiter);
            this.filterChainDefinitions = filterChainDefinitions+fiter;
        }
    
        /*@Resource
        public void setPrivilegeDao(PrivilegeDao privilegeDao) {
            this.privilegeDao = privilegeDao;
        }*/
        
        public Section getObject(){
            Ini ini = new Ini();//网上好多都是在这里配置URL的。但是发现是错误的。
            ini.load(filterChainDefinitions);
            Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
            return section;
        }
    
        public Class<?> getObjectType() {
            return this.getClass();
        }
    
        public boolean isSingleton() {
            return false;
        }
    
    }

    服务器启动的时候会执行ChainDefinitionSectionMetaSource类,加载系统权限资源表所有的url到filterChainDefinitions中(在filterChainDefinitions后追加),从而实现对所有url资源的权限控制的配置:

    这个类就是用来在filterChainDefinitions中追加url资源权限控制的。


    注意:

    /** = authc应放在最后,否则会影响追加的权限资源的控制。

    当访问资源的时候,例如/test/toAddUser.html,就会进入shiro授权方法中进行权限校验,如果没有权限则进入到401未授权,否则正常访问。

    SecurityRealm.java:

    package com.test.www.web.security;
    
    import java.util.List;
    
    import javax.annotation.Resource;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Component;
    
    import com.test.www.web.entity.user.TRolePrivilegeKey;
    import com.test.www.web.entity.user.TUserRoleKey;
    import com.test.www.web.entity.user.User;
    import com.test.www.web.service.user.TRolePrivilegeService;
    import com.test.www.web.service.user.TUserRoleService;
    import com.test.www.web.service.user.UserService;
    
    /**
     * 用户身份验证,授权 Realm 组件
     * 
     **/
    @Component(value = "securityRealm")
    public class SecurityRealm extends AuthorizingRealm {
        @Resource
        private UserService userService;
        @Resource
        private TUserRoleService tUserRoleService;
        @Resource
        private TRolePrivilegeService tRolePrivilegeService;
        /**
         * 权限检查
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            /*SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            String username = String.valueOf(principals.getPrimaryPrincipal());
    
            System.out.println("ssssssss");*/
            String username = principals.getPrimaryPrincipal().toString() ;
            System.out.println(username);
            
            Subject subject = SecurityUtils.getSubject();
            User user = (User)subject.getPrincipal();
            
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo() ;
            
            /*Set<String> roleName = t_userService.findRoles(username) ;
            Set<String> permissions = t_userService.findPermissions(username) ;*/
            //final List<Role> roleInfos = roleService.selectRolesByUserId(user.getUserId());
            /*for (Role role : roleInfos) {
                // 添加角色
                System.err.println(role);
                authorizationInfo.addRole(role.getRoleSign());
    
                final List<Permission> permissions = permissionService.selectPermissionsByRoleId(role.getRoleId());
                for (Permission permission : permissions) {
                    // 添加权限
                    System.err.println(permission);
                    authorizationInfo.addStringPermission(permission.getPermissionSign());
                }
            }*/
            
            List<TUserRoleKey> tUserRoleKeyList = tUserRoleService.selectRolesByUserId(user.getId());
            if(tUserRoleKeyList != null && tUserRoleKeyList.size()>0){
                for(TUserRoleKey tUserRoleKey : tUserRoleKeyList){
                    authorizationInfo.addRole(tUserRoleKey.getCode());
                    List<TRolePrivilegeKey> tRolePrivilegeKeyList = tRolePrivilegeService.selectPrivilegesByRoleId(tUserRoleKey.getRoleId());
                    for(TRolePrivilegeKey tRolePrivilegeKey : tRolePrivilegeKeyList){
                        authorizationInfo.addStringPermission(tRolePrivilegeKey.getUrl());
                    }
                }
            }
            //Set<String> roleName = new HashSet<String>();
            //Set<String> permissions = new HashSet<String>();
            //查询角色
            //roleName.add("admin");
            //根据角色查询权限
            //permissions.add("/test/add.html1"); //pub:coursecategory user:add
            //permissions.add("/js/**");
            //permissions.add("/jsp");
            //authorizationInfo.setRoles(roleName);
            //authorizationInfo.setStringPermissions(permissions);
            return authorizationInfo;
        }
    
        /**
         * 登录验证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            /* String username = String.valueOf(token.getPrincipal());
            String password = new String((char[]) token.getCredentials());
            System.out.println("aaaaaaa");
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, getName());*/
            //int i = 1/0;
            
            //获取用户账号
            //验证账号密码
            UsernamePasswordToken userToken = (UsernamePasswordToken) token;
            System.out.println("1:"+userToken.getUsername());
            User user = userService.getUserByUserName(userToken.getUsername());
            System.out.println("2");
            if (user != null){
                //将查询到的用户账号和密码存放到 authenticationInfo用于后面的权限判断。第三个参数传入realmName。
                AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getSimpleName()) ;
                return authenticationInfo ;
            }else{
                return  null ;
            }
        }
    
    }

    至此,shiro动态控制url权限资源已完成。
    说明:

    url资源(action、静态html或动态页面jsp的url)保存在数据库中。

     

    转载于:https://www.cnblogs.com/super-chao/p/8758763.html

    展开全文
  • 用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。Shiro也支持类似的机制,不过需要稍微改造下来满足实际需求。不过在Shiro中,更多...
  • Shiro中,更多的是通过AOP进行方法级别的权限控制,而通过url进行权限控制是一种集中的权限控制。 示例 拦截器:如authc、anon等字符串为一个拦截器;拦截器链:url=authc为一条拦截器链;拦截器链集合:url1=...
  • 动态URL权限控制

    2017-03-20 21:24:59
    用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。...本章将介绍如何在Shiro中完成动态URL权限控制。   本章代码基于
  • 19 动态URL权限控制

    2018-08-18 14:10:16
    用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。...本章将介绍如何在Shiro中完成动态URL权限控制。   本章代码...
  • 用过springSecurity的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。...本章将介绍如何在Shiro中完成动态URL权限控制。 本章代码基...
  • 用过Spring Security的朋友应该比较熟悉对URL进行全局的权限控制,即访问URL时进行权限匹配;如果没有权限直接跳到相应的错误页面。...本章将介绍如何在Shiro中完成动态URL权限控制。   本章代码基于
  • Shiro权限控制

    2019-03-13 22:48:57
    2、基于Apache Shiro实现登录认证和权限控制,重点shiro权限控制流程、自定义Realm对象控制系统认证和授权 4、动态系统菜单显示功能 5、对认证和授权数据进行缓存优化; 权限控制的两种方式: 一、粗粒度URL级别...
  • 1.shiro处理权限流程 1.1 从数据库查询所有权限交给shiro管理 List<Permission> permissions = permissionService.findAll(); for (Permission permission : permissions) { mp.put(permission.getUrl(), ...
  • shiro实现动态url,看到最多的是开涛的博客,其中需要自定义一个Class UrlFilter,里面包含了url roles permissions这些字段,现在有个问题是不知道这些字段中的数据是如何录入的 我想的是应该是在给角色分配权限...
  • URL拦截权限控制(基于过滤器实现)  方法注解权限控制(基于代理技术实现) (代理有静态代理和动态代理,动态代理中有JDK代理(接口代理),和ciglib代理(子类代理)) 关于代理的解释详见:...
  • 在网上看到一个最多的就是开涛的讲解,其中需要定义一个表来存储url permission roles ,现在我有一个疑问,就是这张表中这几个字段下的值是通过什么方法录入的... 希望能整体大概的说一下这个权限控制的流程
  • 其中,由于用户、角色一般由后台进行操作的动态数据,比如通过@RequiresRoles注解控制某方法的访问,因此Shiro配置一般仅包含前两项的配置。      SecurityManager的配置:  [htm
  • 是用菜单URL作为shiro的permission来管理,每一个用户分配其角色(可以有多个角色),这个系统要求必须登录才能使用,如果是对外的公开项目就不合适,shiro一般也是用在需要控制权限的项目. 每个角色分配其可以访问...

空空如也

空空如也

1 2 3
收藏数 44
精华内容 17
关键字:

shiro动态url权限控制