一、 完成shiro在項目中基本的配置
思路:分別在web.xml配置過濾器以及在applicationContext.xml去配置
實現步驟:
1、在pom.xml裏引入shiro的座標
<shiro.version>1.2.2</shiro.version>
<!-- 權限控制 框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>${shiro.version}</version>
</dependency>
2、在web.xml裏配置shiro過濾器
<!-- 注意:一定配置在struts2過濾器之前;配置Spring提供代理過濾器 -->
<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>
3、在applicationContext.xml裏配置安全管理器以及校驗過濾器
<!--配置過濾器工廠 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 注入安全管理器 --> <property name="securityManager" ref="securityManager"></property> <!-- 注入相關頁面 loginUrl :登陸頁面=用戶沒有登錄,訪問某個url(要求當前用戶登陸後可見)shiro框架會自動跳轉到登錄頁 successUrl;登陸成功頁面=可以不配(通過struts2框架跳轉) unauthorizedUrl;權限不足頁面=用戶登陸後訪問url(要求必須有某個權限),如果用戶沒有權限,跳轉此頁面 --> <property name="loginUrl" value="/login.jsp"></property> <property name="unauthorizedUrl" value="/unauthorized.jsp"></property> <!-- 配置過濾器鏈:配置項目中url對應攔截規則(怎麼驗權) --> <!-- 等號左側代表項目url /** 項目中所有url 等號右側代表url經過哪個過濾器(shiro框架提供,使用簡稱即可) authc:表單認證過濾器(訪問url,要求當前用戶必須認證通過後纔有權限訪問) anon:匿名過濾器(訪問url,不需要登陸,不需要有權限==直接放行) perms:權限授權過濾器(訪問url,要求當前用戶必須有某個權限) roles:角色授權過濾器(訪問url,要求當前用戶必須有某個角色)(通過角色分配權限) --> <property name="filterChainDefinitions"> <value> /js/** = anon /images/** = anon /css/** = anon /login.jsp = anon /validatecode.jsp* = anon /userAction_login.action = anon /pages/base/standard.jsp = perms["standard_page"] /courierAction_delete.action = perms["courier_delete"] /pages/base/courier.jsp = roles["admin"] /** = authc </value> </property> </bean> <!-- 配置安全管理器對象 --><!-- 安全管理器是這個框架提供的接口,我們不能直接使用接口,需要寫出他的實現類來 DefaultWebSecurityManager --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 注入自定義realm對象 --> <property name="realm" ref="bosRealm"></property> </bean> <!-- 開始shrio的註解的支持 --> <!-- 設置自動代理,強制使用cglib代理技術 產生代理對象 --> <!-- 開啓shiro註解支持 --> <!-- 自動代理:自動根據情況不同選擇代理技術 有接口:使用jdk動態代理==產生實現類代理對象 沒有接口:使用cglib動態代理==產生子類代理對象 設置自動代理:強制使用cglib動態代理產生代理對象==如果使用自動代理因爲我們早業務層寫的有接口,會默認使用jdk動態代理。產生對象爲null --> <!-- proxyTargetClass通過該屬性來強制指定代理方式爲cglib動態代理方式 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"> <property name="proxyTargetClass" value="true"></property> </bean> <!-- 配置驗權切面:通知/增強(擴展功能代碼:驗證權限)+切點(shiro註解所在方法) --> <!-- 切面就是 通知 + 切點 --> <!-- AuthorizationAttributeSourceAdvisor驗權切面 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
4、自定義realm對象(認證/授權)
package cn.itcast.bos.web.realms;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import cn.itcast.bos.dao.system.UserDao;
import cn.itcast.bos.domain.system.User;
/**
* @Description:查詢安全數據 查詢安全數據(用戶認證過程查詢用戶信息;用戶授權過程查詢用戶權限信息)
* 1、認證 2、授權
* @Author: 張洋
* @Company: http://www.myfreecloud.cn
* @CreateDate: 2018年6月26日
*/
//繼承了子類,實現類 兩個抽象方法
//然後注入到配置文件只中
@Component("bosRealm")
public class BosRelam extends AuthorizingRealm{
@Autowired
private UserDao userDao;
//返回當前用戶的認證信息
//認證信息
/**
* @Description: 返回當前用戶認證信息(判斷用戶是否合法)
* @param token :Subject調用login方法參數
* 認證邏輯:1、根據用戶名查詢數據庫中真實密碼 2、比對密碼是否正確框架實現
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//用戶在登陸表單輸入值
String username = usernamePasswordToken.getUsername();
User user = userDao.findByUsername(username);
//輸入了不存在的用戶名
if(user==null){
//用戶名錯誤
return null; //shiro框架拋出異常:未知賬戶異常
}
//參數一:主角信息 參數二:用戶真實密碼
//如果密碼比對失敗shiro框架拋異常 new 簡單的認證信息(主角信息 數據庫中的真實密碼 當前class的名稱)
AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
return info;
}
//注意方法上有註解,類上也有註解,以類上的註解爲準
//授權信息
/**
* @Description: 授權(查詢當前用戶是否有權限)
* 1、當使用shiro框架提供給url驗證方式,使用權限,角色過濾器調用此方法查詢用戶權限。
* 2、當使用shiro框架提供給註解驗證方式,代理對象調用此方法查詢用戶權限。
* 3、當使用shiro框架提供頁面標籤方式驗證權限,使用驗證權限,驗證角色的標識,調用此方法
* 4、當使用代碼級別驗證權限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
System.out.println("開始授權");
// TODO Auto-generated method stub
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("standard_page");
//info.addStringPermission("courier_delete");
info.addRole("admin");
return info;
}
}
5、將realm注入到manager中註冊
參見[]配置3的安全管理器的配置
二、 使用shiro的方法註解方式進行權限控制
思路:在applicationContext.xml裏使用aop去實現
實現步驟:
1、在spring文件中配置開啓shiro註解支持
<!-- 開始shrio的註解的支持 -->
<!-- 設置自動代理,強制使用cglib代理技術 產生代理對象 -->
<!-- 開啓shiro註解支持 -->
<!--
自動代理:自動根據情況不同選擇代理技術
有接口:使用jdk動態代理==產生實現類代理對象
沒有接口:使用cglib動態代理==產生子類代理對象
設置自動代理:強制使用cglib動態代理產生代理對象==如果使用自動代理因爲我們早業務層寫的有接口,會默認使用jdk動態代理。產生對象爲null
-->
<!-- proxyTargetClass通過該屬性來強制指定代理方式爲cglib動態代理方式 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="proxyTargetClass" value="true"></property>
</bean>
<!-- 配置驗權切面:通知/增強(擴展功能代碼:驗證權限)+切點(shiro註解所在方法) -->
<!-- 切面就是 通知 + 切點 -->
<!-- AuthorizationAttributeSourceAdvisor驗權切面 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
2、修改事務註解
這裏詳細的原因是因爲之前的transaction事物註解是採用的自動選擇的方式在自動選擇的時候有接口的回默認採用jdk的動態代理的方式,沒有接口的會採用cglib的方式產生子類對象進而採用動態代理的方式進行方法的增強和事物的開啓和提交,現在使用了shrio權限管理框架,我們就需要強制採用cglib代理的方式,不能使用jdk的動態代理,所以在Spring配置文件中引入一下配置,強制開啓cglib方式的動態代理技術
<!-- 業務層事物 註解默認採用也是自動代理技術,在我們使用shrio後強制使用cglib代理技術 加上了這個屬性 proxy-target-class="true" -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
3、在Service方法上使用shiro註解
@Service
@Transactional
public class MenuServiceImpl implements MenuService{
@Autowired
private MenuDao menuDao;
@Override
public List<Menu> findAll() {
return menuDao.findByParentMenuIsNull();
}
}
三、 使用shiro的標籤進行權限控制
思路:這些標籤用於JSP頁面,控制一些元素是否可見
實現步驟:
1、 在頁面引入shiro標籤庫
不管使用什麼標籤,首要的事情就是引入標籤庫
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro"%>
2、 通過shiro自帶標籤控制按鈕是否顯示
<shiro:hasPermission name="courier_delete">
{
id : 'button-delete',
text : '作廢',
iconCls : 'icon-cancel',
handler : doDelete
},
</shiro:hasPermission>
四、 總結shiro提供的權限控制方式
思路:
1、URL級別粗粒度權限控制
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<!--配置過濾器工廠 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 注入安全管理器 -->
<property name="securityManager" ref="securityManager"></property>
<!-- 注入相關頁面
loginUrl :登陸頁面=用戶沒有登錄,訪問某個url(要求當前用戶登陸後可見)shiro框架會自動跳轉到登錄頁
successUrl;登陸成功頁面=可以不配(通過struts2框架跳轉)
unauthorizedUrl;權限不足頁面=用戶登陸後訪問url(要求必須有某個權限),如果用戶沒有權限,跳轉此頁面
-->
<property name="loginUrl" value="/login.jsp"></property>
<property name="unauthorizedUrl" value="/unauthorized.jsp"></property>
<!-- 配置過濾器鏈:配置項目中url對應攔截規則(怎麼驗權) -->
<!--
等號左側代表項目url /** 項目中所有url
等號右側代表url經過哪個過濾器(shiro框架提供,使用簡稱即可)
authc:表單認證過濾器(訪問url,要求當前用戶必須認證通過後纔有權限訪問)
anon:匿名過濾器(訪問url,不需要登陸,不需要有權限==直接放行)
perms:權限授權過濾器(訪問url,要求當前用戶必須有某個權限)
roles:角色授權過濾器(訪問url,要求當前用戶必須有某個角色)(通過角色分配權限)
-->
<property name="filterChainDefinitions">
<value>
/js/** = anon
/images/** = anon
/css/** = anon
/login.jsp = anon
/validatecode.jsp* = anon
/userAction_login.action = anon
/pages/base/standard.jsp = perms["standard_page"]
/courierAction_delete.action = perms["courier_delete"]
/pages/base/courier.jsp = roles["admin"]
/** = authc
</value>
</property>
</bean>
<!-- 配置安全管理器對象 --><!-- 安全管理器是這個框架提供的接口,我們不能直接使用接口,需要寫出他的實現類來 DefaultWebSecurityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 注入自定義realm對象 -->
<property name="realm" ref="bosRealm"></property>
</bean>
<!-- 開始shrio的註解的支持 -->
<!-- 設置自動代理,強制使用cglib代理技術 產生代理對象 -->
<!-- 開啓shiro註解支持 -->
<!--
自動代理:自動根據情況不同選擇代理技術
有接口:使用jdk動態代理==產生實現類代理對象
沒有接口:使用cglib動態代理==產生子類代理對象
設置自動代理:強制使用cglib動態代理產生代理對象==如果使用自動代理因爲我們早業務層寫的有接口,會默認使用jdk動態代理。產生對象爲null
-->
<!-- proxyTargetClass通過該屬性來強制指定代理方式爲cglib動態代理方式 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="proxyTargetClass" value="true"></property>
</bean>
<!-- 配置驗權切面:通知/增強(擴展功能代碼:驗證權限)+切點(shiro註解所在方法) -->
<!-- 切面就是 通知 + 切點 -->
<!-- AuthorizationAttributeSourceAdvisor驗權切面 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
</beans>
2、方法級別細粒度權限控制(基於代理技術實現)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection paramPrincipalCollection) {
System.out.println("開始授權");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("standard_page");
//info.addStringPermission("courier_delete");
info.addRole("admin");
return info;
}
3、通過shiro自定義標籤,實現頁面元素顯示控制(基於標籤技術實現)
<shiro:hasPermission name="courier_delete">
{
id : 'button-delete',
text : '作廢',
iconCls : 'icon-cancel',
handler : doDelete
},
</shiro:hasPermission>
五、 用戶認證、授權功能的實現
思路:在UserAction的login方法裏使用Subject完成
實現步驟:
1、 在UserAction的login方法裏得到Subject對象
//獲取當前登陸用戶
Subject subject = SecurityUtils.getSubject();
2、 封裝表單提交的用戶名和密碼
//開始認證 認證狀態:未認證
//創建認證令牌-用戶名密碼令牌
AuthenticationToken token = new UsernamePasswordToken(model.getUsername(), Md5Util.encode(model.getPassword()));
3、 調用subject的login方法
subject.login(token);
4、 自定義realm對象(認證|授權)
//用戶認證通過 subject對象變爲 認證通過
//將用戶登陸信息存在session
User user = (User) subject.getPrincipal();
六、 完成EasyUITree菜單列表的顯示功能
思路:菜單存在上下級關係(樹形結構),通過easyUI提供的treegrid控件完成
實現步驟:
1、 將需要的數據導入到數據庫
2、修改menu.jsp的treegird的url屬性
url:'${pageContext.request.contextPath}/menuAction_findAll.action',
3、編寫MenuAction 提供list 查詢所有菜單的方法
只是查詢沒有父節點的所有子節點的信息
@Action("menuAction_findAll")
public String findAll() throws Exception {
List<Menu> list = menuService.findAll();
//把獲取到的list集合轉爲json數據返回
this.java2Json(list, new String[]{"roles", "childrenMenus", "parentMenu"});
return NONE;
}
@Service
@Transactional
public class MenuServiceImpl implements MenuService{
@Autowired
private MenuDao menuDao;
@Override
public List<Menu> findAll() {
return menuDao.findByParentMenuIsNull();
}
}
4、編寫Service和DAO
List<Menu> findByParentMenuIsNull();
七、 完成菜單數據的添加功能
思路:在列表顯示的前提下完成添加
實現步驟:
1、 檢查頁面表單元素 name是否與實體類匹配
2、 父菜單項使用combotree展示
3、 爲保存按鈕綁定事件,提交表單
4、 在MenuAction 添加 save保存方法
5、 編寫MenuService