首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络技术 > 网络基础 >

apache shiro与spring调整、动态filterChainDefinitions、以及认证、授权

2013-07-09 
apache shiro与spring整合、动态filterChainDefinitions、以及认证、授权apache shiro是一个安全认证框架,和s

apache shiro与spring整合、动态filterChainDefinitions、以及认证、授权

apache shiro是一个安全认证框架,和spring security相比,在于他使用了比较简洁易懂的认证和授权方式。其提供的native-session(即把用户认证后的授权信息保存在其自身提供Session中)机制,这样就可以和HttpSession、EJB Session Bean的基于容器的Session脱耦,到和客户端应用、Flex应用、远程方法调用等都可以使用它来配置权限认证。 在exit-web-framework里的vcs-admin例子用到该框架,具体使用说明可以参考官方帮助文档。在这里主要讲解如何与spring结合、动态创建filterchaindefinitions、以及认证、授权、和缓存处理。

apache shiro 结合spring

Shiro 拥有对Spring Web 应用程序的一流支持。在Web 应用程序中,所有Shiro 可访问的万恶不请求必须通过一个主要的Shiro 过滤器。该过滤器本身是极为强大的,允许临时的自定义过滤器链基于任何URL 路径表达式执行。 在Shiro 1.0 之前,你不得不在Spring web 应用程序中使用一个混合的方式,来定义Shiro 过滤器及所有它在web.xml中的配置属性,但在Spring XML 中定义SecurityManager。这有些令人沮丧,由于你不能把你的配置固定在一个地方,以及利用更为先进的Spring 功能的配置能力,如PropertyPlaceholderConfigurer 或抽象bean 来固定通用配置。现在在Shiro 1.0 及以后版本中,所有Shiro 配置都是在Spring XML 中完成的,用来提供更为强健的Spring 配置机制。以下是如何在基于Spring web 应用程序中配置Shiro: web.xml:

<!-- Spring ApplicationContext配置文件的路径,可使用通配符,多个路径用,号分隔 此参数用于后面的Spring Context Loader --><context-param>    <param-name>contextConfigLocation</param-name>    <param-value>        classpath*:/applicationContext-shiro.xml    </param-value></context-param><!-- shiro security filter --><filter>    <!-- 这里的filter-name要和spring的applicationContext-shiro.xml里的            org.apache.shiro.spring.web.ShiroFilterFactoryBean的bean name相同 -->    <filter-name>shiroSecurityFilter</filter-name>    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>    <init-param>        <param-name>targetFilterLifecycle</param-name>        <param-value>true</param-value>    </init-param></filter><filter-mapping>    <filter-name>shiroSecurityFilter</filter-name>    <url-pattern>/*</url-pattern></filter-mapping>

applicationContext-shiro.xml文件中,定义web支持的SecurityManager和"shiroSecurityFilter"bean将会被web.xml 引用。

<bean id="shiroSecurityFilter" ref="securityManager" />    <!-- 要求登录时的链接 -->    <property name="loginUrl" value="/login.jsp" />    <!-- 登陆成功后要跳转的连接 -->    <property name="successUrl" value="/index.jsp" />    <!-- 未授权时要跳转的连接 -->    <property name="unauthorizedUrl" value="/unauthorized.jsp" />    <!-- shiro连接约束配置 -->    <propery name="filterChainDefinitions">        <value>            /login = authc            /logout = logout            /resource/** = anon        </value>    </property></bean><bean id="securityManager" depends-on="lifecycleBeanPostProcessor"/><bean ref="securityManager"/></bean>

动态创建filterchaindefinitions

有时,在某些系统想通过读取数据库来定义org.apache.shiro.spring.web.ShiroFilterFactoryBean的filterChainDefinitions。这样能够通过操作界面或者维护后台来管理系统的链接。

在shrio与spring集成好了以后,调试源码的高人可能已经注意到。项目启动时,shrio通过自己的org.apache.shiro.spring.web.ShiroFilterFactoryBean类的filterChainDefinitions(授权规则定义)属性转换为一个filterChainDefinitionMap,转换完成后交给ShiroFilterFactoryBean保管。ShiroFilterFactoryBean根据授权(AuthorizationInfo类)后的信息去判断哪些链接能访问哪些链接不能访问。filterChainDefinitionMap里面的键就是链接URL,值就是存在什么条件才能访问该链接,如perms、roles。filterChainDefinitionMap是一个Map,shiro扩展出一个Map的子类Ini.Section

现在有一张表的描述实体类,以及数据访问:

@Entity@Table(name="TB_RESOURCE")public class Resource implements Serializable {    //主键id    @Id    private String id;    //action url    private String value;    //shiro permission;    private String permission;    //------------------Getter/Setter---------------------//}

@Repositorypublic class ResourceDao extends BasicHibernateDao<Resource, String> {}

通过该类可以知道permission字段和value就是filterChainDefinitionMap的键/值,用spring FactoryBean接口的实现getObject()返回Section给filterChainDefinitionMap即可

public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section>{    @Autowired    private ResourceDao resourceDao;    private String filterChainDefinitions;    /**     * 默认premission字符串     */    public static final String PREMISSION_STRING="perms["{0}"]";    public Section getObject() throws BeansException {        //获取所有Resource        List<Resource> list = resourceDao.getAll();        Ini ini = new Ini();        //加载默认的url        ini.load(filterChainDefinitions);        Ini.Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME);        //循环Resource的url,逐个添加到section中。section就是filterChainDefinitionMap,        //里面的键就是链接URL,值就是存在什么条件才能访问该链接        for (Iterator<Resource> it = list.iterator(); it.hasNext();) {            Resource resource = it.next();            //如果不为空值添加到section中            if(StringUtils.isNotEmpty(resource.getValue()) && StringUtils.isNotEmpty(resource.getPermission())) {                section.put(resource.getValue(),  MessageFormat.format(PREMISSION_STRING,resource.getPermission()));            }        }        return section;    }    /**     * 通过filterChainDefinitions对默认的url过滤定义     *      * @param filterChainDefinitions 默认的url过滤定义     */    public void setFilterChainDefinitions(String filterChainDefinitions) {        this.filterChainDefinitions = filterChainDefinitions;    }    public Class<?> getObjectType() {        return this.getClass();    }    public boolean isSingleton() {        return false;    }}

定义好了chainDefinitionSectionMetaSource后修改applicationContext-shiro.xml文件

<bean id="chainDefinitionSectionMetaSource" ref="securityManager" />    <property name="loginUrl" value="/login.jsp" />    <property name="successUrl" value="/index.jsp" />    <property name="unauthorizedUrl" value="/unauthorized.jsp" />    <!-- shiro连接约束配置,在这里使用自定义的动态获取资源类 -->    <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" /></bean><bean id="securityManager" ref="securityManager" />    <property name="loginUrl" value="/login.jsp" />    <property name="successUrl" value="/index.jsp" />    <property name="unauthorizedUrl" value="/unauthorized.jsp" />    <!-- shiro连接约束配置,在这里使用自定义的动态获取资源类 -->    <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" /></bean><bean id="shiroDataBaseRealm" value="MD5" />        </bean>    </property></bean><bean id="securityManager" ref="shiroDataBaseRealm" /></bean><bean id="lifecycleBeanPostProcessor" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:cache="http://www.springframework.org/schema/cache"   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">    <!-- 使用缓存annotation 配置 -->    <cache:annotation-driven cache-manager="ehCacheManager" /></beans>

@Cacheable和**@CacheEvict**来对缓存进行操作

@Cacheable:负责将方法的返回值加入到缓存中

@CacheEvict:负责清除缓存

/**声明了一个名为 persons 的缓存区域,当调用该方法时,Spring 会检查缓存中是否存在 personId 对应的值。*/@Cacheable("persons")public Person profile(Long personId) { ... }/**指定多个缓存区域。Spring 会一个个的检查,一旦某个区域存在指定值时则返回*/@Cacheable({"persons", "profiles"})public Person profile(Long personId) { ... }</code></pre><pre><code>/**清空所有缓存*/@CacheEvict(value="persons",allEntries=true)public Person profile(Long personId, Long groundId) { ... }/**或者根据条件决定是否缓存*/@CacheEvict(value="persons", condition="personId > 50")public Person profile(Long personId) { ... }

在shiro里面会有授权缓存。可以通过AuthorizingRealm类中指定缓存名称。就是authorizationCacheName属性。当shiro为用户授权一次之后将会把所有授权信息都放进缓存中。现在有个需求。当在更新用户或者删除资源和更新资源的时候,要刷新一下shiro的授权缓存,给shiro重新授权一次。因为当更新用户或者资源时,很有可能已经把用户本身已有的资源去掉。不给用户访问。所以。借助spring的缓存工厂和shiro的缓存能够很好的实现这个需求。

将applicationContext-shiro.xml文件添加缓存

<bean id="chainDefinitionSectionMetaSource" >        <value>            /login = authc            /logout = logout            /resource/** = anon        </value>    </property></bean><bean id="shiroSecurityFilter" ref="securityManager" />    <property name="loginUrl" value="/login.jsp" />    <property name="successUrl" value="/index.jsp" />    <property name="unauthorizedUrl" value="/unauthorized.jsp" />    <!-- shiro连接约束配置,在这里使用自定义的动态获取资源类 -->    <property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" /></bean><bean id="shiroDataBaseRealm" value="MD5" />        </bean>    </property>    <property name="authorizationCacheName" value="shiroAuthorizationCache" /></bean><bean id="securityManager" ref="shiroDataBaseRealm" />    <property name="cacheManager" ref="cacheManager" /></bean><bean id="lifecycleBeanPostProcessor" /><!-- spring对ehcache的缓存工厂支持 --><bean id="ehCacheManagerFactory" value="classpath:ehcache.xml" />    <property name="shared" value="false" /></bean><!-- spring对ehcache的缓存管理 --><bean id="ehCacheManager" ref="ehCacheManagerFactory"></property></bean><!-- shiro对ehcache的缓存管理直接使用spring的缓存工厂 --><bean id="cacheManager" ref="ehCacheManagerFactory" /></bean>

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?><ehcache>      <!--            maxElementsInMemory为缓存对象的最大数目,             eternal设置是否永远不过期,            timeToIdleSeconds对象处于空闲状态的最多秒数,            timeToLiveSeconds对象处于缓存状态的最多秒数      -->    <diskStore path="java.io.tmpdir"/>    <cache name="shiroAuthorizationCache" maxElementsInMemory="300" eternal="false" timeToLiveSeconds="600" overflowToDisk="false"/></ehcache>

public class UserDao extends BasicHibernateDao<User, String> {    public User getUserByUsername(String username) {        return findUniqueByProperty("username", username);    }    @CacheEvict(value="shiroAuthorizationCache",allEntries=true)    public void saveUser(User entity) {        save(entity);    }}

@Repositorypublic class ResourceDao extends BasicHibernateDao<Resource, String> {    @CacheEvict(value="shiroAuthorizationCache",allEntries=true)    public void saveResource(Resource entity) {        save(entity);    }    @CacheEvict(value="shiroAuthorizationCache",allEntries=true)    public void deleteResource(Resource entity) {        delete(entity);    }}

当userDao或者reaourceDao调用了相应带有缓存注解的方法,都会将AuthorizingRealm类中的缓存去掉。那么就意味着 shiro在用户访问链接时要重新授权一次。

整个apache shiro的使用,vcs admin项目和vcs admin jpa中都会有例子。可以参考showcase/vcs_admin或者showcase/vcs_admin_jpa项目中的例子去理解。。

热点排行