1.什么是Shiro
Shiro是Java的一個安全框架, 完成權限控制的任務.
權限控制的基本功能: 認證(讓系統知道你是誰); 授權(讓系統知道你能做什么)
權限控制常用的技術: 過濾器/攔截器, 或者使用代理模式
權限控制的數據模型: 權限表-(多對多)-角色表-(多對多)-用戶表
shiri的組成
shiro的調用流程
Application code:應用程序代碼
Subject:當前用戶
SecurityManager:安全管理器,shiro框架的核心對象,管理各個組件
Realm:獲取權限信息, 決定subject的訪問權限, 可以有多個
2.Shiro的使用
2.1登錄
Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try{ // 注意:登錄不成功以拋異常的方式通知 subject.login(token); }catch (UnknownAccountException e) { // 沒找到用戶名 }catch (IncorrectCredentialsException e) { // 用戶名密碼不匹配 }catch (AuthenticationException e) { // 其他登錄錯誤 } if (subject.isAuthenticated()) { // 登錄成功后, 取回認證時通過簽名存儲的對象 User user = (User) subject.getPrincipal(); }
2.2 在realm里管理用戶~權限
public class MyRealm extends AuthorizingRealm {
// 登錄及認證 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 獲得登錄用的token UsernamePasswordToken token = (UsernamePasswordToken) token; // 獲得封裝的username String username = token.getUsername(); User user = userDao.findByUsername(username); if (null == user) { return null; } else { // 獲得真正的password String password = user.getPassword(); // 創建簡單認證對象, 返回給安全管理器 // 參數一:可放入任何對象的簽名, 執行subject.login()方法后可從subject.getPrincipal()取回 // 參數二:封裝真正的密碼, 交由安全管理器比對 // 參數三:當前realm的名稱 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getClass().getSimpleName()); return info; } } // 授權 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 獲得簽名, 可做進一步查詢 User user = (User) principals.getPrimaryPrincipal(); // 創建簡單授權對象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //授予perms["keyword"] info.addStringPermission("staff"); //授予role["keyword"] info.addRole("staff"); return info; } }
2.3 配置及管理訪問目標~權限
2.3.1 過濾器方式:
web.xml
<!-- shiro過濾器, 從spring工廠拿, beanname為"shiroFilter" --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
applicationContext.xml
<!-- shiro: 自定義的realm --> <bean id = "userDefineRealm" class="com.zx.bos.shiro.UserDefineRealm"></bean> <!-- 二級緩存--> <bean id="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- realm需注冊給安全管理器 --> <property name="realm" ref = "userDefineRealm"></property> <!-- 使用ehcache二級緩存, 沒必要頻繁查詢權限 --> <property name="cacheManager" ref="ehCacheManager"></property> </bean> <!-- shiro使用方式1, 對URL進行控制 --> <bean id = "shiroFilter" class = "org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 注入安全管理器 --> <property name="securityManager" ref="securityManager"></property> <!-- 未登錄時跳轉到登錄頁面 --> <property name="loginUrl" value="/login.jsp"/> <!-- 登錄成功頁面 --> <property name="successUrl" value="/index.jsp"/> <!-- 登錄后跳轉到登陸前被攔截的頁面, 不跳轉至此 --> <!-- 權限不足提示頁面 --> <property name="unauthorizedUrl" value="/unauthorizedUrl.jsp"/> <!-- URL攔截規則 --> <!-- 有先后順序 從上往下匹配 --> <property name="filterChainDefinitions"> <value> /css/** = anon /images/** = anon /js/** = anon /login.jsp* = anon <!-- 后面帶參數 --> /validatecode.jsp* = anon
/page_base_staff.action = roles["staff"] /userAction_login.action = anon /* = authc </value> </property> </bean>
anon:可以匿名使用。
authc:需要認證(登錄)才能使用
roles:可以帶參數, 參數可以多個,例如roles["admin,guest"], 表示每個參數都通過才算通過
perms:可以帶參數, 參數可以多個,perms["user:add:*,user:modify:*"]
port:port[8081]表示當請求的url的端口不是8081時跳轉到schema://serverName:8081?queryParams
user:必須存在用戶,登錄操作時不做檢查
2.3.2 注解方式:
<!-- shiro使用方式2, 使用注解對方法進行控制. 不同於url方式權限不足時跳轉界面,它直接拋出異常 --> <!-- 代理 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <property name="proxyTargetClass" value="true"></property> <!-- 使用cglib創建代理對象, 默認false用jdk動態代理 --> </bean> <!-- 切面類 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>
注意捕獲權限不足時拋出的異常. 如果在struts里需要配置
struts.xml
<global-results> <result name="unauthorizedUrl">/unauthorizedUrl.jsp</result> </global-results> <!-- 接收異常 --> <global-exception-mappings> <exception-mapping result="unauthorizedUrl" exception="org.apache.shiro.authz.AuthorizationException"></exception-mapping> </global-exception-mappings>
例如:
@RequiresRoles("rolename");
void someMethod();
@RequiresPermissions({"file:read", "write:aFile.txt"});
void someMethod();
void someMethod();
最后說一遍, 需要處理可能的AuthorizationException異常
注解方式是在方法級別的控制,無法做類級別的控制訪問。
過濾器方式是根據訪問的URL進行控制。允許使用*通配URL,所以可以進行粗粒度,也可以進行細粒度控制。
過濾器方式是根據訪問的URL進行控制。允許使用*通配URL,所以可以進行粗粒度,也可以進行細粒度控制。
2.4 在JSP頁面使用shiro標簽
使用shiro標簽, 根據權限過濾部分內容
<shiro:authenticated> 登錄之后
<shiro:notAuthenticated> 沒有登錄
<shiro:hasRole name="abc"> 擁有角色abc
<shiro:lacksRole name="abc"> 沒有角色abc
<shiro:hasPermission name="abc"> 擁有權限資源abc
<shiro:lacksPermission name="abc"> 沒有abc權限資源
<shiro:principal> 取回簽名對象
<shiro:notAuthenticated> 沒有登錄
<shiro:hasRole name="abc"> 擁有角色abc
<shiro:lacksRole name="abc"> 沒有角色abc
<shiro:hasPermission name="abc"> 擁有權限資源abc
<shiro:lacksPermission name="abc"> 沒有abc權限資源
<shiro:principal> 取回簽名對象