一、Shiro功能
-
Authentication:身份認證/登錄
-
Authorization:授權(權限驗證)
-
Session Manager:會話管理,會話可以是Java SE環境也可以是Java EE環境。使用Shiro,在service層和dao層也可以直接活動session對象,實現解耦
-
Cryptography:加密
-
Caching:緩存,比如用戶登錄後,其用戶用於的角色/權限不必每次去查
-
Run As:允許一個用戶假如另一個用戶的身份進行訪問
-
Remember Me:記住我
二、Shiro架構
1、緩存管理器
- 內置的緩存管理器:org.apache.shiro.cache.MemoryConstrainedCacheManager
- Ehcache管理器:org.apache.shiro.cache.ehcache.EhCacheManager
- redis緩存管理器:org.crazycake.shiro.RedisCacheManager
三、SSM整合shiro
1、pom.xml
<shiro.version>1.4.2</shiro.version>
<commons-collections.version>3.2.1</commons-collections.version>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>
</dependency>
2、web.xml
<!-- shiro的攔截器 -->
<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>
3、spring-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- 緩存管理器 使用shiro內置的緩存 還可以使用ehcache和redis緩存 -->
<bean id="shiroCacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean>
<!-- 憑證匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 加密算法名稱 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!-- 加密次數 -->
<property name="hashIterations" value="10"></property>
</bean>
<!-- Realm實現 -->
<bean id="userRealm" class="com.bs.shiro.realm.ShiroRealm">
<!-- 使用credentialsMatcher實現密碼驗證服務 -->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<!-- 是否啓用緩存 -->
<property name="cachingEnabled" value="true"/>
<!-- 啓用認證緩存 -->
<property name="authenticationCachingEnabled" value="true"/>
<!-- 緩存AuthenticationInfo信息的緩存名稱 -->
<property name="authenticationCacheName" value="authenticationCache"/>
<!-- 啓用授權緩存 -->
<property name="authorizationCachingEnabled" value="true"/>
<!-- 緩存AuthorizationInfo信息的緩存名稱 -->
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
<!-- 會話Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<!-- 如果設置爲true,則客戶端不會暴露給服務端腳本代碼,有助於減少某些類型的跨站腳本攻擊 -->
<property name="httpOnly" value="true"/>
<property name="maxAge" value="-1"/><!-- maxAge=-1表示瀏覽器關閉時失效此Cookie -->
</bean>
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean>
<!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- cipherKey是加密rememberMe Cookie的密匙,默認AES算法 -->
<property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean>
<!-- 會話驗證調度器 -->
<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
<property name="sessionValidationInterval" value="1800000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!-- 會話管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- 設置全局會話過期時間:默認30分鐘 -->
<property name="globalSessionTimeout" value="1800000"/>
<!-- 是否自動刪除無效會話 -->
<property name="deleteInvalidSessions" value="true"/>
<!-- 會話驗證是否啓用 -->
<property name="sessionValidationSchedulerEnabled" value="true"/>
<!-- 會話驗證調度器 -->
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<!-- 是否啓用sessionIdCookie,默認是啓用的 -->
<property name="sessionIdCookieEnabled" value="true"/>
<!-- 會話Cookie -->
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>
<!--配置安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="shiroCacheManager" />
<property name="sessionManager" ref="sessionManager"/>
<property name="realm" ref="userRealm" />
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- 配置ShiroFilter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- 未認證時返回的頁面 被authc user logout 等認證攔截器攔截後訪問的url -->
<property name="loginUrl" value="/user/unAuthenticated.do"/>
<!-- 未授權時返回的頁面 被roles 等授權攔截器攔截後訪問的url -->
<property name="unauthorizedUrl" value="/user/unAuthorized.do"/>
<property name="filterChainDefinitions">
<value>
<!-- 可以匿名訪問 -->
/user/login.do = anon
<!-- 訪問該url登出 -->
/user/logout.do = logout
<!-- 認證或記住我後可以訪問 -->
/user/index.do = user
<!-- 需要授權纔可以訪問 -->
/user/admin.do = roles[admin]
<!-- 需要認證纔可以訪問 -->
/** = authc
</value>
</property>
</bean>
<!-- shiro聲明週期處理器 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans>
四、Shiro使用
1、Session Manager
Subject currentUser = SecurityUtils.getSubject();
Session session = currentUser.getSession();
2、自定義realm
public class ShiroRealm extends AuthorizingRealm{
/**
* 認證時會被shiro回調
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1、把AuthenticationToken轉換爲UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2、從UsernamePasswordToken獲取用戶名
String userName = upToken.getUsername();
//3、從數據庫獲取用戶信息
String realUserName = "user";
String realPassWord = "e4216961e03f91a7632f9fee1444d3bb";
//4、拋出異常
// 1)用戶不存在
if("unknow".equals(realUserName)) {
throw new UnknownAccountException("用戶不存在");
}
// 2)用戶被鎖定
if("monster".equals(userName)) {
throw new LockedAccountException("用戶被鎖定");
}
// 3)其他異常
//throw new AuthenticationException();
//5、構建AuthenticationInfo 並返回。從數據庫獲取以下信息
//1)principal:認證的實體信息,可以是username
Object principal = realUserName;
//2)credentials:密碼
Object credentials = realPassWord;
//3)credentialsSalt:鹽值,一般用隨機字符串或用戶名(唯一值)
ByteSource credentialsSalt = ByteSource.Util.bytes(userName);
//4)realmName
String realmName = getName();
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
return info;
}
/**
* 檢測是否有授權時會被shiro回調
* 不用主動調用
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//1.從PrincipalCollection中獲取登錄用戶信息
Object principal = principals.getPrimaryPrincipal();
//2.利用登錄用戶的信息來獲取用戶權限信息
Set<String> roles = new HashSet<>();
roles.add("user");
if("admin".equals(principal)){
roles.add("admin");
}
//3.創建SimpleAuthorization 並設置其reles屬性
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//4.返回SimpleAutorizationInfo對象
return info;
}
}
3、Authentication(用戶登錄相關邏輯)
//獲得當前用戶
Subject currentUser = SecurityUtils.getSubject();
//如果用戶還未被認證(登錄)
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(user, password);
try {
token.setRememberMe(true);//記住我
currentUser.login(token);//會使用自定義的ShiroRealm來驗證登錄(在Shiro的配置文件中配置)
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
} catch (AuthenticationException ae) {
ae.printStackTrace();
}
}
4、Authorization
//該用戶是否爲某個角色
currentUser.hasRole("schwartz");
//該用戶是否有某個授權
currentUser.isPermitted("lightsaber:wield"); //對lightsaber做wield
currentUser.isPermitted("winnebago:drive:eagle5"); //對winnebago類型的eagle5對象做drive,如"user:delete:張三"