簡介
Apache Shiro 是 Java 的一個安全(權限)框架。主要提供了認證、授權、加密和會話管理等功能。
Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;
Authorization:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用
戶是否能進行什么操作,如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶
對某個資源是否具有某個權限;
Session Manager:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有
信息都在會話中;會話可以是普通 JavaSE 環境,也可以是 Web 環境的;
Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
Web Support:Web 支持,可以非常容易的集成到Web 環境;
Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權限不必每次去查,這樣可
以提高效率;
Concurrency:Shiro 支持多線程應用的並發驗證,即如在一個線程中開啟另一個線程,能
把權限自動傳播過去;
Testing:提供測試支持;
Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登
錄了。
集成Spring
工程目錄
1、引入依賴
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- spring webmvc相關jar --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.0.0.RELEASE</version> </dependency> <!-- spring相關jar --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <!-- log --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.21</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.28</version> </dependency> <!-- json --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.0</version> <exclusions> <exclusion> <artifactId>jackson-annotations</artifactId> <groupId>com.fasterxml.jackson.core</groupId> </exclusion> </exclusions> </dependency> <!-- web jstl的jar --> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- ehcache的jar --> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.3</version> </dependency> <!--shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-aspectj</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency> </dependencies>
2、Spring配置文件
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="org.tarena"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"/> <import resource="classpath*:/spring-*.xml" /> </beans>
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 開啟controller注解支持 --> <context:component-scan base-package="org.tarena.shiro" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <mvc:annotation-driven></mvc:annotation-driven> <mvc:default-servlet-handler/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean>
</beans>
spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- ========================================================= Shiro Core Components - Not Spring Specific ========================================================= --> <!-- Shiro's main business-tier object for web-enabled applications (use DefaultSecurityManager instead when there is no web environment)--> <!-- 1. 配置 SecurityManager! --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="cacheManager" ref="cacheManager"/> <!-- Single realm app. If you have multiple realms, use the 'realms' property instead. --> <property name="sessionMode" value="native"/> <property name="realms"> <list> <ref bean="myRealm"/> <ref bean="secondRealm"/> </list> </property> </bean> <bean id="myRealm" class="org.tarena.shiro.realm.MyRealm"> <property name="credentialsMatcher" > <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="MD5"></property> <property name="hashIterations" value="1024"></property> </bean> </property> </bean> <bean id="secondRealm" class="org.tarena.shiro.realm.SecondRealm"> <property name="credentialsMatcher" > <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <property name="hashAlgorithmName" value="SHA1"></property> <property name="hashIterations" value="1024"></property> </bean> </property> </bean> <!-- Let's use some enterprise caching support for better performance. You can replace this with any enterprise caching framework implementation that you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc --> <!-- 2. 配置 CacheManager. 2.1 需要加入 ehcache 的 jar 包及配置文件. --> <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <!-- Set a net.sf.ehcache.CacheManager instance here if you already have one. If not, a new one will be creaed with a default config: <property name="cacheManager" ref="ehCacheManager"/> --> <!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance to inject, but you want a specific Ehcache configuration to be used, specify that here. If you don't, a default will be used.: --> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> </bean> <!-- ========================================================= Shiro Spring-specific integration ========================================================= --> <!-- Post processor that automatically invokes init() and destroy() methods for Spring-configured Shiro objects so you don't have to 1) specify an init-method and destroy-method attributes for every bean definition and 2) even know which Shiro objects require these methods to be called. --> <!-- 4. 配置 LifecycleBeanPostProcessor. 可以自定的來調用配置在 Spring IOC 容器中 shiro bean 的生命周期方法. --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <!-- Enable Shiro Annotations for Spring-configured beans. Only run after the lifecycleBeanProcessor has run: --> <!-- 5. 啟用 IOC 容器中使用 shiro 的注解. 但必須在配置了 LifecycleBeanPostProcessor 之后才可以使用. --> <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> <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml - web.xml uses the DelegatingFilterProxy to access this bean. This allows us to wire things with more control as well utilize nice Spring things such as PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: --> <!-- 6. 配置 ShiroFilter. 6.1 id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致. 若不一致, 則會拋出: NoSuchBeanDefinitionException. 因為 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應的 filter bean. --> <!-- Define the Shiro Filter here (as a FactoryBean) instead of directly in web.xml - web.xml uses the DelegatingFilterProxy to access this bean. This allows us to wire things with more control as well utilize nice Spring things such as PropertiesPlaceholderConfigurer and abstract beans or anything else we might need: --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="successUrl" value="/list"/> <property name="unauthorizedUrl" value="/unauthorized"/> <!-- <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>--> <!-- 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 overrides or parent/child consolidated configuration here if you like: --> <!-- <property name="filters"> <util:map> <entry key="aName" value-ref="someFilterPojo"/> </util:map> </property> --> <property name="filterChainDefinitions"> <value> /**/login = anon /toLogin = anon # everything else requires authentication: /** = authc </value> </property> </bean> <!-- <bean id="filterChainDefinitionMapFactory" class="org.tarena.shiro.factory.FilterChainDefinitionMapFactory"> </bean> <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory" factory-method="bulidFilterChainDefinitionMap"></bean> --> </beans>
ehcache.xml
<!-- ~ Licensed to the Apache Software Foundation (ASF) under one ~ or more contributor license agreements. See the NOTICE file ~ distributed with this work for additional information ~ regarding copyright ownership. The ASF licenses this file ~ to you under the Apache License, Version 2.0 (the ~ "License"); you may not use this file except in compliance ~ with the License. You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, ~ software distributed under the License is distributed on an ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ~ KIND, either express or implied. See the License for the ~ specific language governing permissions and limitations ~ under the License. --> <!-- EhCache XML configuration file used for Shiro spring sample application --> <ehcache> <!-- Sets the path to the directory where cache .data files are created. If the path is a Java System Property it is replaced by its value in the running VM. The following properties are translated: user.home - User's home directory user.dir - User's current working directory java.io.tmpdir - Default temp file path --> <diskStore path="java.io.tmpdir/shiro-spring-sample"/> <!--Default Cache configuration. These will applied to caches programmatically created through the CacheManager. The following attributes are required: maxElementsInMemory - Sets the maximum number of objects that will be created in memory eternal - Sets whether elements are eternal. If eternal, timeouts are ignored and the element is never expired. overflowToDisk - Sets whether elements can overflow to disk when the in-memory cache has reached the maxInMemory limit. The following attributes are optional: timeToIdleSeconds - Sets the time to idle for an element before it expires. i.e. The maximum amount of time between accesses before an element expires Is only used if the element is not eternal. Optional attribute. A value of 0 means that an Element can idle for infinity. The default value is 0. timeToLiveSeconds - Sets the time to live for an element before it expires. i.e. The maximum time between creation time and when an element expires. Is only used if the element is not eternal. Optional attribute. A value of 0 means that and Element can live for infinity. The default value is 0. diskPersistent - Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskExpiryThreadIntervalSeconds- The number of seconds between runs of the disk expiry thread. The default value is 120 seconds. memoryStoreEvictionPolicy - Policy would be enforced upon reaching the maxElementsInMemory limit. Default policy is Least Recently Used (specified as LRU). Other policies available - First In First Out (specified as FIFO) and Less Frequently Used (specified as LFU) --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <!-- We want eternal="true" (with no timeToIdle or timeToLive settings) because Shiro manages session expirations explicitly. If we set it to false and then set corresponding timeToIdle and timeToLive properties, ehcache would evict sessions without Shiro's knowledge, which would cause many problems (e.g. "My Shiro session timeout is 30 minutes - why isn't a session available after 2 minutes?" Answer - ehcache expired it due to the timeToIdle property set to 120 seconds.) diskPersistent=true since we want an enterprise session management feature - ability to use sessions after even after a JVM restart. --> <cache name="shiro-activeSessionCache" maxElementsInMemory="10000" eternal="true" overflowToDisk="true" diskPersistent="true" diskExpiryThreadIntervalSeconds="600"/> <cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization" maxElementsInMemory="100" eternal="false" timeToLiveSeconds="600" overflowToDisk="false"/> </ehcache>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <!-- needed for ContextLoaderListener --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!-- Bootstraps the root web application context before servlet initialization --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 設置servlet編碼開始 --> <filter> <filter-name>Set Character Encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>Set Character Encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- The front controller of this Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- Shiro Filter is defined in the spring application context: --> <!-- 1. 配置 Shiro 的 shiroFilter. 2. DelegatingFilterProxy 實際上是 Filter 的一個代理對象. 默認情況下, Spring 會到 IOC 容器中查找和 <filter-name> 對應的 filter bean. 也可以通過 targetBeanName 的初始化參數來配置 filter bean 的 id. --> <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> </web-app>