一 shiro 是什么
shiro 是一個(gè)功能強(qiáng)大和易于使用的Java安全框架,為開發(fā)人員提供一個(gè)直觀而全面的解決方案的認(rèn)證,授權(quán),加密,會(huì)話管理。
二 shiro 能干什么
先上圖:

shiro 四個(gè)主要的功能
- Authentication:身份認(rèn)證/登錄,驗(yàn)證用戶是不是擁有相應(yīng)的身份;
- Authorization:授權(quán),即權(quán)限驗(yàn)證,判斷某個(gè)已經(jīng)認(rèn)證過的用戶是否擁有某些權(quán)限訪問某些資源,一般授權(quán)會(huì)有角色授權(quán)和權(quán)限授權(quán);
- SessionManager:會(huì)話管理,即用戶登錄后就是一次會(huì)話,在沒有退出之前,它的所有信息都在會(huì)話中;會(huì)話可以是普通JavaSE環(huán)境的,也可以是如Web環(huán)境的,web 環(huán)境中作用是和 HttpSession 是一樣的;
- Cryptography:加密,保護(hù)數(shù)據(jù)的安全性,如密碼加密存儲(chǔ)到數(shù)據(jù)庫,而不是明文存儲(chǔ);
shiro 的其它幾個(gè)特點(diǎn)
- Web Support:Web支持,可以非常容易的集成到Web環(huán)境;
- Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權(quán)限不必每次去查,這樣可以提高效率;
- Concurrency:shiro支持多線程應(yīng)用的并發(fā)驗(yàn)證,即如在一個(gè)線程中開啟另一個(gè)線程,能把權(quán)限自動(dòng)傳播過去;
- Testing:提供測試支持;
- Run As:允許一個(gè)用戶假裝為另一個(gè)用戶(如果他們?cè)试S)的身份進(jìn)行訪問;
- Remember Me:記住我,這個(gè)是非常常見的功能,即一次登錄后,下次再來的話不用登錄了。
三 shiro 架構(gòu)
先上圖

從圖中我們可以看到不管是任何請(qǐng)求都會(huì)經(jīng)過 SecurityManager 攔截并進(jìn)行相應(yīng)的處理,shiro 幾乎所有的功能都是由 SecurityManager 來管理。
其中:
- Subject:主體,相當(dāng)與是請(qǐng)求過來的"用戶"
- SecurityManager: 是 Shiro 的心臟;所有具體的交互都通過 SecurityManager 進(jìn)行攔截并控制;它管理著所有 Subject、且負(fù)責(zé)進(jìn)行認(rèn)證和授權(quán)、及會(huì)話、緩存的管理
- Authenticator:認(rèn)證器,負(fù)責(zé)主體認(rèn)證的,即確定用戶是否登錄成功,我們可以使用 Shiro 提供的方法來認(rèn)證,有可以自定義去實(shí)現(xiàn),自己判斷什么時(shí)候算是用戶登錄成功
- Authrizer:授權(quán)器,即權(quán)限授權(quán),給 Subject 分配權(quán)限,以此很好的控制用戶可訪問的資源
- Realm:一般我們都需要去實(shí)現(xiàn)自己的 Realm ,可以有1個(gè)或多個(gè) Realm,即當(dāng)我們進(jìn)行登錄認(rèn)證時(shí)所獲取的安全數(shù)據(jù)來源(帳號(hào)/密碼)
- SessionManager:為了可以在不同的環(huán)境下使用 session 功能,shiro 實(shí)現(xiàn)了自己的 sessionManager ,可以用在非 web 環(huán)境下和分布式環(huán)境下使用
- SessionDAO:對(duì) session 的 CURD 操作
- CacheManager:緩存控制器,來管理如用戶、角色、權(quán)限等的緩存的;
- Cryptography:密碼模塊,Shiro提高了一些常見的加密組件用于如密碼加密/解密的。
四 shiro 的主要功能 - 身份認(rèn)證
1 Subject 認(rèn)證
身份認(rèn)證就是在應(yīng)用中誰能證明他就是他本人,一般會(huì)使用用戶名和密碼作為認(rèn)證信息。
2 Subject 認(rèn)證主體
Subject 認(rèn)證主體包含兩個(gè)信息:
- Principals:身份,即用戶名
- Credentials:憑證,即密碼
** 3 認(rèn)證流程**

- 用戶發(fā)送請(qǐng)求進(jìn)行 Subject 認(rèn)證(調(diào)用 subject.login(token))
- SecurityManager 會(huì)去 Authenticator(認(rèn)證器)中查找相應(yīng)的 Realms(可能不止一個(gè))源
- Realms 可以根據(jù)不同類型的 Realm 中去查找用戶信息,并進(jìn)行判斷是否認(rèn)證成功
4 快速搭建 helloWorld
- 導(dǎo)包
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
</dependencies>
- 創(chuàng)建 Realm /resources/shiro.ini
[users]
acey=123456
jack=111
- 進(jìn)行身份驗(yàn)證
public class HelloWorld {
public static void main(String[] args) {
// 加載配置文件,初始化 SecurityManager 工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory
("classpath:shiro.shiro.ini");
// 獲取 SecurityManager 實(shí)例
SecurityManager securityManager = factory.getInstance();
// 把 SecurityManager 綁定到 SecurityUtils 中
SecurityUtils.setSecurityManager(securityManager);
// 得到當(dāng)前執(zhí)行的用戶
Subject currentUser = SecurityUtils.getSubject();
// 創(chuàng)建 token 令牌,用戶名/密碼
UsernamePasswordToken token = new UsernamePasswordToken("acey", "123456");
try {
// 身份驗(yàn)證
currentUser.login(token);
System.out.println("登錄成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("登錄失敗");
}
}
}
四 shiro 的主要功能 - 授權(quán)
權(quán)限授權(quán)就是訪問控制,在應(yīng)用中控制誰能訪問哪些資源
1 權(quán)限認(rèn)證中的幾個(gè)元素
- 權(quán)限:即操作某個(gè)資源的權(quán)限,這些資源可以是某個(gè)鏈接,也可以是某個(gè)圖片,也可以是對(duì)某個(gè)模塊的數(shù)據(jù)的 CURL
- 角色:即權(quán)限的集合,一個(gè)角色可以有多個(gè)權(quán)限
- 用戶:代表訪問的用戶,即 Subject
2 授權(quán)的流程

- 當(dāng)用戶訪問應(yīng)用中的某個(gè)資源時(shí),會(huì)被 SecurityManager 攔截.
- SecurityManager 會(huì)去調(diào)用 Authorizer(授權(quán)器)
- Authorizer 會(huì)根據(jù) Subject 的身份去相應(yīng)的 Realm 中去查找該 Subject 是否有權(quán)限去訪問該資源
3 授權(quán)實(shí)現(xiàn)
- 導(dǎo)包
- 配置 permission(權(quán)限) resources/shiro_permission.ini
[main]
authc.loginUrl=/login //表示用戶登錄失敗跳轉(zhuǎn)到 /login
roles.unauthorrizedUrl=/unauthorrized.jsp //表示用戶沒有對(duì)應(yīng)的訪問角色跳轉(zhuǎn)到/unauthorrized.jsp
perms.unauthorrizedUrl=/unauthorrized.jsp //表示用戶沒有對(duì)應(yīng)的訪問權(quán)限跳轉(zhuǎn)到/unauthorrized.jsp
[users]
acey=123456,role1,role2
jack=123,role1
[roles]
role1=user:select // role1 角色有訪問 user:select 的權(quán)限
role2=user:add,/delete //role2 角色有訪問 user:add 和 /delete 的權(quán)限
[urls]
/login=anon //表示任何用戶都可以訪問 /login
/index=authc //表示只有身份認(rèn)證通過的用戶才可以訪問 /index
/index=roles[role1,role2...] //表示只有用戶含有 role1 role2 ... 角色才可以訪問 /index
/index=perms["user:create","/update"] //表示只有用戶含有 "user:create"
和"/update"權(quán)限才可以訪問 /index
/index?=authc //`?`通配符,表示一個(gè)字符,如/index1 /indexa /index- (不能匹配/index) ,
將符合這種規(guī)則的請(qǐng)求進(jìn)行`authc`攔截
/index*=authc `*`通配符,表示零個(gè)或一個(gè)或多個(gè)字符,如/index1213asd /index /index2 ,
將符合這種規(guī)則的請(qǐng)求進(jìn)行`authc`攔截
/index/**=authc `**`表示匹配零個(gè)或一個(gè)或多個(gè)路徑,如/index/create /index/create/update/... ,
將符合這種規(guī)則的請(qǐng)求進(jìn)行`authc`攔截
/index*/**authc 可以匹配 /index12/create/update/...
3)配置 roles (角色) resources/shiro_role.ini
[users]
acey=123456,role1,role2 //表示有一個(gè)用戶,用戶名是acey,密碼為123456,有role1和role2角色
jack=123,role1
- 驗(yàn)證用戶角色是否足夠
public class RoleTest {
// 使用 checkRole 來檢驗(yàn)角色時(shí),若權(quán)限不足會(huì)返回 false
@Test
public void testHasRole() {
Subject currentUser= ShiroUtil.login("classpath:shiro_role.ini", "acey", "123456");
// Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
System.out.println(currentUser.hasRole("role1")?"has role1":"has not role1");
currentUser.logout();
}
// 使用 checkRole 來檢驗(yàn)角色時(shí),若權(quán)限不足會(huì)拋出異常
@Test
public void testCheckRole() {
Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "acey", "123456");
// Subject currentUser=ShiroUtil.login("classpath:shiro_role.ini", "jack", "123");
currentUser.checkRole("role1");
currentUser.logout();
}
}
- 驗(yàn)證用戶權(quán)限是否足夠
public class PermissionTest {
// 使用 checkPermission 來檢驗(yàn)權(quán)限時(shí),若權(quán)限不足會(huì)返回 false
@Test
public void testIsPermitted() {
Subject currentUser= ShiroUtil.login("classpath:shiro_permission.ini", "acey", "123456");
System.out.println(currentUser.isPermitted("user:select")?"has user:select":"hsa not user:select");
currentUser.logout();
}
// 使用 checkPermission 來檢驗(yàn)權(quán)限時(shí),若權(quán)限不足會(huì)拋出異常
@Test
public void testCheckPermitted() {
Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "acey", "123456");
// Subject currentUser=ShiroUtil.login("classpath:shiro_permission.ini", "jack", "123");
currentUser.checkPermission("user:select");
currentUser.logout();
}
}
五 ssm 和 shiro 整合
- 導(dǎo)入依賴
2)配置 web.xml(shiro過濾器)
<!-- shiro過濾器定義 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 該值缺省為false,表示生命周期由SpringApplicationContext管理,設(shè)置為true則表示由ServletContainer管理 -->
<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)編寫自己的 Realm(一般權(quán)限都是從數(shù)據(jù)庫中查找,所以需要自定義)
public class MyRealm extends AuthorizingRealm{
@Resource
private UserService userService;
/**
* 為當(dāng)前登錄的用戶授予角色和權(quán)限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//獲取用戶名
String userName=(String)principals.getPrimaryPrincipal();
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
//進(jìn)行授權(quán)角色
authorizationInfo.setRoles(userService.getRoles(userName));
//進(jìn)行授權(quán)權(quán)限
authorizationInfo.setStringPermissions(userService.getPermissions(userName));
return authorizationInfo;
}
/**
*驗(yàn)證當(dāng)前登錄的用戶
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName=(String)token.getPrincipal();
//根據(jù)用戶名查找用戶信息
User user=userService.getByUserName(userName);
if(user!=null){
AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),getName());
return authcInfo;
}else{
return null;
}
}
}
- spring 和 shiro 配置整合
...
<!-- 自定義Realm -->
<bean id="myRealm" class="com.acey.realm.MyRealm"/>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- Shiro過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,這個(gè)屬性是必須的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份認(rèn)證失敗,則跳轉(zhuǎn)到登錄頁面的配置 -->
<property name="loginUrl" value="/index.jsp"/>
<!-- 權(quán)限認(rèn)證失敗,則跳轉(zhuǎn)到指定頁面 -->
<property name="unauthorizedUrl" value="/unauthor.jsp"/>
<!-- Shiro連接約束配置,即過濾鏈的定義 -->
<property name="filterChainDefinitions">
<value>
/login=anon
/admin*=authc
/student=roles[teacher]
/teacher=perms["user:create"]
</value>
</property>
</bean>
<!-- 保證實(shí)現(xiàn)了Shiro內(nèi)部lifecycle函數(shù)的bean執(zhí)行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 開啟Shiro注解 -->
<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>
...
一般角色和權(quán)限都存在數(shù)據(jù)庫中,所以我們還可以自定義一個(gè) filter 去自己驗(yàn)證每一個(gè)請(qǐng)求的 Subject 是否有權(quán)限去訪問,這樣我們就可以減少對(duì)過濾鏈的維護(hù).比如
<!-- Shiro過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,這個(gè)屬性是必須的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份認(rèn)證失敗,則跳轉(zhuǎn)到登錄頁面的配置 -->
<property name="loginUrl" value="/index.jsp"/>
<!-- 權(quán)限認(rèn)證失敗,則跳轉(zhuǎn)到指定頁面 -->
<property name="unauthorizedUrl" value="/unauthor.jsp"/>
<!-- Shiro連接約束配置,即過濾鏈的定義 -->
<property name="filterChainDefinitions">
<value>
/login=anon
/admin*=authc
/student=roles[teacher]
/teacher=perms["user:create"]
</value>
</property>
</bean>
可以改成
<!-- Shiro過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,這個(gè)屬性是必須的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 身份認(rèn)證失敗,則跳轉(zhuǎn)到登錄頁面的配置 -->
<property name="loginUrl" value="/index.jsp"/>
<!-- 權(quán)限認(rèn)證失敗,則跳轉(zhuǎn)到指定頁面 -->
<property name="unauthorizedUrl" value="/unauthor.jsp"/>
<property name="ownFilter" class="ownFilter.class">
<!-- Shiro連接約束配置,即過濾鏈的定義 -->
<property name="filterChainDefinitions">
<value>
/login=anon
/**=ownFilter
</value>
</property>
</bean>
待續(xù)! 歡迎大家拍磚
源碼地址:ShirDemos