1. 下載
在Maven項目中的依賴配置如下:
<!-- shiro配置 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${version.shiro}</version> </dependency> <!-- Enables support for web-based applications. --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${version.shiro}</version> </dependency> <!-- Enables AspectJ support for Shiro AOP and Annotations. --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-aspectj</artifactId> <version>${version.shiro}</version> </dependency> <!-- Enables Ehcache-based famework caching. --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${version.shiro}</version> </dependency> <!-- Enables Spring Framework integration. --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${version.shiro}</version> </dependency>
特別地!Shiro使用了日志框架slf4j,因此需要對應配置指定的日志實現組件,如:log4j,logback等。
在此,以使用log4j為日志實現為例:
<!-- 日志工具 --> <!-- shiro使用slf4j作為日志框架,所以必需配置slf4j。 同時,使用log4j作為底層的日志實現框架。 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.25</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
2.集成Shiro
在Spring框架中集成Shiro,本質上是與Spring IoC容器和Spring MVC框架集成,所以應該分為2部分來說。
(1)與Spring IoC容器集成
Spring IoC容器提供了一個非常重要的功能,就是依賴注入,將Bean的定義以及Bean之間關系的耦合通過容器來處理。
也就是說,在Spring中集成Shiro時,Shiro中的相應Bean的定義以及他們的關系也需要通過Spring IoC容器實現,配置如下:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/index"/> <property name="successUrl" value="/home"/> <property name="unauthorizedUrl" value="/unauthorized.jsp"/> <!-- The 'filters' property is not necessary since any declared javax.servlet.Filter bean --> <!-- defined will be automatically acquired and available via its beanName in chain --> <!-- definitions, but you can perform instance overrides or name aliases here if you like: --> <!-- <property name="filters"> <util:map> <entry key="logout" value-ref="logoutFilter" /> </util:map> </property> --> <property name="filterChainDefinitions"> <value> # some example chain definitions: # /admin/** = authc, roles[admin] # /docs/** = authc, perms[document:read] /login = anon /logout = anon /error = anon /** = user # more URL-to-FilterChain definitions here </value> </property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- Single realm app. If you have multiple realms, use the 'realms' property instead. --> <property name="realm" ref="myRealm" /> <!-- By default the servlet container sessions will be used. Uncomment this line to use shiro's native sessions (see the JavaDoc for more): --> <!-- <property name="sessionMode" value="native"/> --> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- Define the Shiro Realm implementation you want to use to connect to your back-end --> <!-- security datasource: --> <bean id="myRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm"> <property name="dataSource" ref="dataSource"/> <property name="permissionsLookupEnabled" value="true"/> </bean> <!-- Enable Shiro Annotations for Spring-configured beans. Only run after --> <!-- the lifecycleBeanProcessor has run: --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>
(2)與Spring MVC集成
跟在普通Java Web應用中使用Shiro一樣,集成Shiro到Spring MVC時,實際上就是通過在web.xml中添加指定Filter實現。配置如下:
<!-- The filter-name matches name of a 'shiroFilter' bean inside applicationContext.xml --> <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> <!-- Make sure any request you want accessible to Shiro is filtered. /* catches all --> <!-- requests. Usually this filter mapping is defined first (before all others) to --> <!-- ensure that Shiro works in subsequent filters in the filter chain: --> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
也就是說,其實在Spring中集成Shiro的原理就是:通過在web.xml中配置的Shiro Filter與Spring IoC中定義的相應的Shiro Bean定義建立關系,從而實現在Spring框架集成Shiro。
實際上,通常就是在web.xml添加的Filter與某個Shiro Spring Bean的定義name是相同的,參見示例。
Shiro Filter類圖:
3. 數據源配置
在Shiro中,Realm定義了訪問數據的方式,用來連接不同的數據源,如:LDAP,關系數據庫,配置文件等等。
Realm類圖:
也就是說,可以根據實際需求及應用的權限管理復雜度靈活選擇指定數據源。
在此,以org.apache.shiro.realm.jdbc.JdbcRealm為例,將用戶信息存放在關系型數據庫中。
在使用org.apache.shiro.realm.jdbc.JdbcRealm時,必須要在關系型數據庫中存在3張表,分別是:
(1)users表,存放認證用戶基本信息,在該表中必須存在2個字段:username,password。
(2)roles_permissions表,存放角色和權限定義,在該表中必須存在2個字段:role_name,permission。
(3)user_roles表,存放用戶角色對應關系,在該表中必須存在2個字段:username,role_name。
實際上,在更加復雜的應用場景下,通常需要擴展org.apache.shiro.realm.jdbc.JdbcRealm。
4. 認證
在Shiro中,認證即執行用戶登錄,讀取指定Realm連接的數據源,以驗證用戶身份的有效性與合法性。
關於Shiro在Web應用中的認證流程,與Shiro在非Web環境的獨立應用中的認證流程一樣,都需要執行用戶登錄,即:
Subject currentUser = SecurityUtils.getSubject(); if(!currentUser.isAuthenticated()) { UsernamePasswordToken token = new UsernamePasswordToken(name, password); try { currentUser.login(token); } catch (UnknownAccountException e) { logger.error(String.format("user not found: %s", name), e); } catch(IncorrectCredentialsException e) { logger.error(String.format("user: %s pwd: %s error", name, password), e); } catch (ConcurrentAccessException e) { logger.error(String.format("user has been authenticated: %s", name), e); } catch (AuthenticationException e) { logger.error(String.format("account except: %s", name), e); }
}
唯一的區別就是,在Java Web環境中,用戶名和密碼參數是通過前端頁面進行傳遞。
5. 授權
需要再三強調!!!Shiro作為權限框架,僅僅只能控制對資源的操作權限,並不能完成對數據權限的業務需求。
而對於Java Web環境下Shiro授權,包含個方面的含義。
其一,對於前端來說,用戶只能看到他對應訪問權限的元素。
其二,當用戶執行指定操作(即:訪問某個uri資源)時,需要驗證用戶是否具備對應權限。
對於第一點,在Java Web環境下,通過Shiro提供的JSP標簽實現。
<shiro:hasRole name="admin"> <a>用戶管理</a> </shiro:hasRole> <shiro:hasPermission name="winnebago:drive:eagle5"> <a>操作審計</a> </shiro:hasPermission>
必須在jsp頁面中引入shiro標簽庫:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
對於第二點,與在非Java Web環境下一樣,需要在后端調用API進行權限(或者角色)檢驗。
api調用:
String roleAdmin = "admin"; Subject currentUser = SecurityUtils.getSubject(); if(!currentUser.hasRole(roleAdmin)) { //todo something }
在Spring框架中集成Shiro,還可以直接通過Java注解方式實現:
@Controller public class HomeController { @RequestMapping("/home") @RequiresPermissions(value={"log:manage:*"}) public ModelAndView home(HttpServletRequest req) { ModelAndView mv = new ModelAndView("home"); return mv; } }
6.Spring集成Shiro注意事項
假設存在如下幾個配置文件,分別是:
springDAO.xml:數據源定義
springMVC.xml:Spring MVC配置
springService.xml:其他Spring組件配置
springShiro.xml:Shiro相關Bean配置
第一,在不同版本的Spring中集成Shiro,實現方式不同。
(1)在Spring 4.2.0 RELEASE+版本中集成Shiro:
web.xml:
<servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring*.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
(2)在Spring 4.1.9 RELEASE-版本中集成Shiro:
web.xml:
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/springDAO.xml,classpath:/springService.xml,classpath:/springShiro.xml</param-value> </context-param> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/springMVC.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
同時,還需要將在springShiro.xml中配置的org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator放到springMVC.xml中,即:
<!-- 解決在spring 4.1.9 RELEASE及以下版本,集成shiro時注解不生效的問題 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
第二,雖然shiro的注解定義是在Class級別的,但是實際驗證只能支持方法級別:
@RequiresAuthentication
@RequiresPermissions
@RequiresRoles
7. 完整示例
詳見:https://git.oschina.net/cchanghui/test-shirospring.git