一 shiro 是什么
shiro 是一個功能強大和易于使用的Java安全框架脯爪,為開發(fā)人員提供一個直觀而全面的解決方案的認(rèn)證则北,授權(quán),加密痕慢,會話管理。
二 shiro 能干什么
先上圖:
所有功能
shiro 四個主要的功能
- Authentication:身份認(rèn)證/登錄掖举,驗證用戶是不是擁有相應(yīng)的身份快骗;
- Authorization:授權(quán),即權(quán)限驗證塔次,判斷某個已經(jīng)認(rèn)證過的用戶是否擁有某些權(quán)限訪問某些資源方篮,一般授權(quán)會有角色授權(quán)和權(quán)限授權(quán);
- SessionManager:會話管理励负,即用戶登錄后就是一次會話藕溅,在沒有退出之前,它的所有信息都在會話中继榆;會話可以是普通JavaSE環(huán)境的巾表,也可以是如Web環(huán)境的,web 環(huán)境中作用是和 HttpSession 是一樣的略吨;
- Cryptography:加密集币,保護數(shù)據(jù)的安全性,如密碼加密存儲到數(shù)據(jù)庫晋南,而不是明文存儲惠猿;
shiro 的其它幾個特點
- Web Support:Web支持,可以非常容易的集成到Web環(huán)境负间;
- Caching:緩存偶妖,比如用戶登錄后,其用戶信息政溃、擁有的角色/權(quán)限不必每次去查趾访,這樣可以提高效率;
- Concurrency:shiro支持多線程應(yīng)用的并發(fā)驗證董虱,即如在一個線程中開啟另一個線程扼鞋,能把權(quán)限自動傳播過去;
- Testing:提供測試支持愤诱;
- Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問云头;
- Remember Me:記住我,這個是非常常見的功能淫半,即一次登錄后溃槐,下次再來的話不用登錄了。
三 shiro 架構(gòu)
先上圖
架構(gòu)
從圖中我們可以看到不管是任何請求都會經(jīng)過 SecurityManager 攔截并進行相應(yīng)的處理科吭,shiro 幾乎所有的功能都是由 SecurityManager 來管理昏滴。
其中:
- Subject:主體猴鲫,相當(dāng)與是請求過來的"用戶"
- SecurityManager: 是 Shiro 的心臟;所有具體的交互都通過 SecurityManager 進行攔截并控制谣殊;它管理著所有 Subject拂共、且負責(zé)進行認(rèn)證和授權(quán)、及會話姻几、緩存的管理
- Authenticator:認(rèn)證器宜狐,負責(zé)主體認(rèn)證的,即確定用戶是否登錄成功鲜棠,我們可以使用 Shiro 提供的方法來認(rèn)證肌厨,有可以自定義去實現(xiàn),自己判斷什么時候算是用戶登錄成功
- Authrizer:授權(quán)器豁陆,即權(quán)限授權(quán),給 Subject 分配權(quán)限吵护,以此很好的控制用戶可訪問的資源
- Realm:一般我們都需要去實現(xiàn)自己的 Realm 盒音,可以有1個或多個 Realm,即當(dāng)我們進行登錄認(rèn)證時所獲取的安全數(shù)據(jù)來源(帳號/密碼)
- SessionManager:為了可以在不同的環(huán)境下使用 session 功能馅而,shiro 實現(xiàn)了自己的 sessionManager 祥诽,可以用在非 web 環(huán)境下和分布式環(huán)境下使用
- SessionDAO:對 session 的 CURD 操作
- CacheManager:緩存控制器,來管理如用戶瓮恭、角色雄坪、權(quán)限等的緩存的;
- Cryptography:密碼模塊屯蹦,Shiro提高了一些常見的加密組件用于如密碼加密/解密的维哈。
四 shiro 的主要功能 - 身份認(rèn)證
1 Subject 認(rèn)證
身份認(rèn)證就是在應(yīng)用中誰能證明他就是他本人,一般會使用用戶名和密碼作為認(rèn)證信息。
2 Subject 認(rèn)證主體
Subject 認(rèn)證主體包含兩個信息:
- Principals:身份,即用戶名
- Credentials:憑證,即密碼
** 3 認(rèn)證流程**
認(rèn)證流程
- 用戶發(fā)送請求進行 Subject 認(rèn)證(調(diào)用 subject.login(token))
- SecurityManager 會去 Authenticator(認(rèn)證器)中查找相應(yīng)的 Realms(可能不止一個)源
- Realms 可以根據(jù)不同類型的 Realm 中去查找用戶信息,并進行判斷是否認(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
- 進行身份驗證
public class HelloWorld {
public static void main(String[] args) {
// 加載配置文件,初始化 SecurityManager 工廠
Factory<SecurityManager> factory = new IniSecurityManagerFactory
("classpath:shiro.shiro.ini");
// 獲取 SecurityManager 實例
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 {
// 身份驗證
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)證中的幾個元素
- 權(quán)限:即操作某個資源的權(quán)限登澜,這些資源可以是某個鏈接阔挠,也可以是某個圖片,也可以是對某個模塊的數(shù)據(jù)的 CURL
- 角色:即權(quán)限的集合脑蠕,一個角色可以有多個權(quán)限
- 用戶:代表訪問的用戶购撼,即 Subject
2 授權(quán)的流程
授權(quán)流程
- 當(dāng)用戶訪問應(yīng)用中的某個資源時,會被 SecurityManager 攔截.
- SecurityManager 會去調(diào)用 Authorizer(授權(quán)器)
- Authorizer 會根據(jù) Subject 的身份去相應(yīng)的 Realm 中去查找該 Subject 是否有權(quán)限去訪問該資源
3 授權(quán)實現(xiàn)
- 導(dǎo)包
- 配置 permission(權(quán)限) resources/shiro_permission.ini
[main]
authc.loginUrl=/login //表示用戶登錄失敗跳轉(zhuǎn)到 /login
roles.unauthorrizedUrl=/unauthorrized.jsp //表示用戶沒有對應(yīng)的訪問角色跳轉(zhuǎn)到/unauthorrized.jsp
perms.unauthorrizedUrl=/unauthorrized.jsp //表示用戶沒有對應(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 //`?`通配符,表示一個字符,如/index1 /indexa /index- (不能匹配/index) ,
將符合這種規(guī)則的請求進行`authc`攔截
/index*=authc `*`通配符,表示零個或一個或多個字符,如/index1213asd /index /index2 ,
將符合這種規(guī)則的請求進行`authc`攔截
/index/**=authc `**`表示匹配零個或一個或多個路徑,如/index/create /index/create/update/... ,
將符合這種規(guī)則的請求進行`authc`攔截
/index*/**authc 可以匹配 /index12/create/update/...
3)配置 roles (角色) resources/shiro_role.ini
[users]
acey=123456,role1,role2 //表示有一個用戶,用戶名是acey,密碼為123456,有role1和role2角色
jack=123,role1
- 驗證用戶角色是否足夠
public class RoleTest {
// 使用 checkRole 來檢驗角色時,若權(quán)限不足會返回 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 來檢驗角色時,若權(quán)限不足會拋出異常
@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();
}
}
- 驗證用戶權(quán)限是否足夠
public class PermissionTest {
// 使用 checkPermission 來檢驗權(quán)限時,若權(quán)限不足會返回 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 來檢驗權(quán)限時,若權(quán)限不足會拋出異常
@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();
//進行授權(quán)角色
authorizationInfo.setRoles(userService.getRoles(userName));
//進行授權(quán)權(quán)限
authorizationInfo.setStringPermissions(userService.getPermissions(userName));
return authorizationInfo;
}
/**
*驗證當(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的核心安全接口,這個屬性是必須的 -->
<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>
<!-- 保證實現(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ù)庫中,所以我們還可以自定義一個 filter 去自己驗證每一個請求的 Subject 是否有權(quán)限去訪問,這樣我們就可以減少對過濾鏈的維護.比如
<!-- Shiro過濾器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,這個屬性是必須的 -->
<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的核心安全接口,這個屬性是必須的 -->
<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