細說shiro之五:在spring框架中集成shiro

官網:https://shiro.apache.org/前端

 

1. 下載
在Maven項目中的依賴配置以下:java

<!-- 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爲日誌實現爲例:git

<!-- 日誌工具 -->
<!--
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容器實現,配置以下:web

<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集成spring

跟在普通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類圖:apache

 

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。api

 

4. 認證
在Shiro中,認證即執行用戶登陸,讀取指定Realm鏈接的數據源,以驗證用戶身份的有效性與合法性。
關於Shiro在Web應用中的認證流程,與Shiro在非Web環境的獨立應用中的認證流程同樣,都須要執行用戶登陸,即:session

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

相關文章
相關標籤/搜索