Shiro 的組件都是 JavaBean/POJO 式的組件,所以非常容易使用 Spring 進(jìn)行組件管理,可以非常方便的從 ini 配置遷移到 Spring 進(jìn)行管理,且支持 JavaSE 應(yīng)用及 Web 應(yīng)用的集成。
在示例之前,需要導(dǎo)入 shiro-spring 及 spring-context 依賴,具體請參考 pom.xml。
spring-beans.xml 配置文件提供了基礎(chǔ)組件如 DataSource、DAO、Service 組件的配置。
spring-shiro.xml 提供了普通 JavaSE 獨(dú)立應(yīng)用的 Spring 配置:
<!-- 緩存管理器 使用Ehcache實(shí)現(xiàn) -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
<!-- 憑證匹配器 -->
<bean id="credentialsMatcher" class="
com.github.zhangkaitao.shiro.chapter12.credentials.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="2"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<!-- Realm實(shí)現(xiàn) -->
<bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter12.realm.UserRealm">
<property name="userService" ref="userService"/>
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="true"/>
<property name="authenticationCachingEnabled" value="true"/>
<property name="authenticationCacheName" value="authenticationCache"/>
<property name="authorizationCachingEnabled" value="true"/>
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
<!-- 會(huì)話ID生成器 -->
<bean id="sessionIdGenerator"
class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
<!-- 會(huì)話DAO -->
<bean id="sessionDAO"
class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
<!-- 會(huì)話驗(yàn)證調(diào)度器 -->
<bean id="sessionValidationScheduler"
class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
<property name="sessionValidationInterval" value="1800000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- 會(huì)話管理器 -->
<bean id="sessionManager" class="org.apache.shiro.session.mgt.DefaultSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionDAO" ref="sessionDAO"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.mgt.DefaultSecurityManager">
<property name="realms">
<list><ref bean="userRealm"/></list>
</property>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 相當(dāng)于調(diào)用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod"
value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
<!-- Shiro生命周期處理器-->
<bean id="lifecycleBeanPostProcessor"
class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
可以看出,只要把之前的 ini 配置翻譯為此處的 spring xml 配置方式即可,無須多解釋。 LifecycleBeanPostProcessor 用于在實(shí)現(xiàn)了 Initializable 接口的 Shiro bean 初始化時(shí)調(diào)用 Initializable 接口回調(diào),在實(shí)現(xiàn)了 Destroyable 接口的 Shiro bean 銷毀時(shí)調(diào)用 Destroyable 接口回調(diào)。如 UserRealm 就實(shí)現(xiàn)了 Initializable,而 DefaultSecurityManager 實(shí)現(xiàn)了 Destroyable。具體可以查看它們的繼承關(guān)系。
測試用例請參考 com.github.zhangkaitao.shiro.chapter12.ShiroTest。
Web 應(yīng)用和普通 JavaSE 應(yīng)用的某些配置是類似的,此處只提供一些不一樣的配置,詳細(xì)配置可以參考 spring-shiro-web.xml。
<!-- 會(huì)話Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="180000"/>
</bean>
<!-- 會(huì)話管理器 -->
<bean id="sessionManager"
class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
<!-- 基于Form表單的身份驗(yàn)證過濾器 -->
<bean id="formAuthenticationFilter"
class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<property name="usernameParam" value="username"/>
<property name="passwordParam" value="password"/>
<property name="loginUrl" value="/login.jsp"/>
</bean>
<!-- Shiro的Web過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login.jsp"/>
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/index.jsp = anon
/unauthorized.jsp = anon
/login.jsp = authc
/logout = logout
/** = user
</value>
</property>
</bean>
接著需要在 web.xml 中進(jìn)行如下配置:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-beans.xml,
classpath:spring-shiro-web.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
通過 ContextLoaderListener 加載 contextConfigLocation 指定的 Spring 配置文件。
<filter>
<filter-name>shiroFilter</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>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
DelegatingFilterProxy 會(huì)自動(dòng)到 Spring 容器中查找名字為 shiroFilter 的 bean 并把 filter 請求交給它處理。
其他配置請參考源代碼。
Shiro 提供了相應(yīng)的注解用于權(quán)限控制,如果使用這些注解就需要使用 AOP 的功能來進(jìn)行判斷,如 Spring AOP;Shiro 提供了 Spring AOP 集成用于權(quán)限注解的解析和驗(yàn)證。
為了測試,此處使用了 Spring MVC 來測試 Shiro 注解,當(dāng)然 Shiro 注解不僅僅可以在 web 環(huán)境使用,在獨(dú)立的 JavaSE 中也是可以用的,此處只是以 web 為例了。
在 spring-mvc.xml 配置文件添加 Shiro Spring AOP 權(quán)限注解的支持:
<aop:config proxy-target-class="true"></aop:config>
<bean class="
org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
如上配置用于開啟 Shiro Spring AOP 權(quán)限注解的支持;<aop:config proxy-target-class="true">
表示代理類。
接著就可以在相應(yīng)的控制器(AnnotationController)中使用如下方式進(jìn)行注解:
@RequiresRoles("admin")
@RequestMapping("/hello2")
public String hello2() {
return "success";
}
訪問 hello2 方法的前提是當(dāng)前用戶有 admin 角色。
當(dāng)驗(yàn)證失敗,其會(huì)拋出 UnauthorizedException 異常,此時(shí)可以使用 Spring 的 ExceptionHandler(DefaultExceptionHandler)來進(jìn)行攔截處理:
@ExceptionHandler({UnauthorizedException.class})
@ResponseStatus(HttpStatus.UNAUTHORIZED)
public ModelAndView processUnauthenticatedException(NativeWebRequest request, UnauthorizedException e) {
ModelAndView mv = new ModelAndView();
mv.addObject("exception", e);
mv.setViewName("unauthorized");
return mv;
}
如果集成 Struts2,需要注意《Shiro+Struts2+Spring3 加上 @RequiresPermissions 后 @Autowired 失效》問題:
http://jinnianshilongnian.iteye.com/blog/1850425
權(quán)限注解
@RequiresAuthentication
表示當(dāng)前 Subject 已經(jīng)通過 login 進(jìn)行了身份驗(yàn)證;即 Subject.isAuthenticated() 返回 true。
@RequiresUser
表示當(dāng)前 Subject 已經(jīng)身份驗(yàn)證或者通過記住我登錄的。
@RequiresGuest
表示當(dāng)前 Subject 沒有身份驗(yàn)證或通過記住我登錄過,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
表示當(dāng)前 Subject 需要角色 admin 和 user。
@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)
表示當(dāng)前 Subject 需要權(quán)限 user:a 或 user:b。
更多建議: