基於SSM + Redis的Shiro許可權管理專案
一、專案介紹(準備工作)
執行前申明
-
請看完本頁面的所有細節,對你掌握這個專案來說很重要,別一上來就搞,你不爽,我也不爽。
-
本專案需要一定的Java功底,需要對
SpringMvc
,Mybatis
,有基本的瞭解,其次對Redis
有了解和使用更佳。 -
本專案理論上,只需要一個
Redis
,然後一個Mysql
和一個有Maven
環境的開發工具即可執行起來。
執行步驟
-
下載原始碼,匯入到
Eclipse
、MyEclipes
、Idea
類似開發工具。 -
解決編譯錯誤,修改
JDK
為1.7
以上(請勿使用工具自帶JDK
)。 -
在
Mysql
資料庫中建立一個數據庫,庫名隨便,資料庫版本為5.6(必要條件,否則部分語法不支援)。 -
從專案
/init/sql/
下,先執行tables.sql
建立表,再執行init.data.sql
插入初始化資料。 -
再修改配置
jdbc.properties
把資料庫連結改成您的。 -
安裝完畢後,修改配置:
spring-cache.xml
,如果是本地,無序修改,啟動Redis
,如對Redis
不瞭解的同學,建議別設定密碼。
二、執行效果
-
專案帳號和線上Demo一致:管理員帳號:admin,密碼:sojson.com 如果密碼錯誤,請用 sojson
-
截圖如下:
三、程式實現過程
1、SSM(SpringMVC + Mybatis)框架的增刪改查(含分頁)
本教程是 + Shiro + Redis 做的整體Demo,其他框架需要自己自行解決,所以不做其他框架的講解,其實是大同小異。
Controller
==> Service
(事務控制層)
==> Dao
==> SqlMapper
==> Mysql
2、View層 Freemarker,JSP
通用View
層配置在spring-mvc.xml
中的以【通用試圖解析器】註釋標註的區間配置。
3、Shiro + Redis 的整合,也提供Ehcache的依賴Jar
Redis
快取配置主要在spring-cache.xml
中。對應的所有Cache
相關 Java 程式碼在package:com.sojson.core.shiro.cache
中
4、Shiro 初始許可權動態載入
我們一般是這麼載入的。在spring-shiro.xml
<property name="filterChainDefinitions" > <value> /** = anon /page/login.jsp = anon /page/register/* = anon /page/index.jsp = authc /page/addItem* = authc,roles[資料管理員] /page/file* = authc,roleOR[普通使用者,資料管理員] /page/listItems* = authc,roleOR[資料管理員,普通使用者] /page/showItem* = authc,roleOR[資料管理員,普通使用者] /page/updateItem*=authc,roles[資料管理員] </value> </property>
本教程採用動態載入,你可以從資料庫裡讀取然後拼接成shiro要的資料。
<property name="filterChainDefinitions" value="#\{shiroManager.loadFilterChainDefinitions()\}"/>
5、Shiro 自定義許可權校驗Filter定義,及功能實現
Shiro Filter在package:com.sojson.core.shiro.filter
,具體配置在spring-shiro.xml
中。定義了5個攔截器,具體功能看程式碼以及程式碼註釋。
<bean id="shiroManager" class="com.sojson.core.shiro.service.impl.ShiroManagerImpl"/> <bean id="login" class="com.sojson.core.shiro.filter.LoginFilter"/> <bean id="role" class="com.sojson.core.shiro.filter.RoleFilter"/> <bean id="permission" class="com.sojson.core.shiro.filter.PermissionFilter"/> <bean id="simple" class="com.sojson.core.shiro.filter.SimpleAuthFilter"/>
<property name="filters"> <util:map> <entry key="login" value-ref="login"></entry> <entry key="role" value-ref="role"></entry> <entry key="simple" value-ref="simple"></entry> <entry key="permission" value-ref="permission"></entry> </util:map> </property>
6、Shiro Ajax請求許可權不滿足,攔截後解決方案
這裡有一個前提,我們知道Ajax不能做頁面redirect
和forward
跳轉,所以Ajax請求假如沒登入,那麼這個請求給使用者的感覺就是沒有任何反應,而使用者又不知道使用者已經退出了。解決程式碼如下:
//Java程式碼,判斷如果是Ajax請求,然後並且沒登入,那麼就給予返回JSON,login_status = 300,message = 當前使用者沒有登入!
if (ShiroFilterUtils.isAjax(request)) {// ajax請求
Map<String,String> resultMap = new HashMap<String, String>();
LoggerUtils.debug(getClass(), "當前使用者沒有登入,並且是Ajax請求!");
resultMap.put("login_status", "300");
resultMap.put("message", "\u5F53\u524D\u7528\u6237\u6CA1\u6709\u767B\u5F55\uFF01");//當前使用者沒有登入!
ShiroFilterUtils.out(response, resultMap);
}
//前端程式碼 if(result.login_status == 300){ layer.msg(result.message);//當前使用者沒有登入! }
7、Shiro Freemarker標籤使用
8、Shiro JSP標籤使用
9、Shiro 登入後跳轉到最後一個訪問的頁面
在 Java 中就可以這樣獲取上一個地址:
//上一個瀏覽的非Ajax的地址,在登入後,取得地址,如果不為null,那麼就跳轉過去。
String url = (String) request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE);
//shiro也有他的方法。詳細看下面。
如果需要儲存登入之前的Request資訊,那麼需要在Login攔截的Filter中先儲存:
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws Exception {
//儲存Request和Response,登入後可以取到
saveRequestAndRedirectToLogin(request, response);
return Boolean.FALSE ;
}
//登入後,取到之前的Request中的一些資訊。
SavedRequest saveRequest = WebUtils.getSavedRequest(request);
saveRequest.getMethod();//之前的請求方法
saveRequest.getQueryString();//之前請求的條件
saveRequest.getRequestURI();//之前請求的路徑
saveRequest.getRequestUrl();//之前請求的全路徑
10、使用者禁止登入Demo
這個功能其實是一個改變使用者資料庫表裡的一個欄位,本Demo中:1:有效,0:禁止登入
然後踢出使用者登入狀態。程式碼詳細請檢視CustomSessionManager.java
類的forbidUserById(Long
id, Long status)
方法。
而再次登入的話,需要再登入,而登入的地方限制了使用者狀態為(0
:禁止登入)的使用者登入。
/**
* 查詢要禁用的使用者是否線上。
* @param id 使用者ID
* @param status 使用者狀態
*/
public void forbidUserById(Long id, Long status) {
//獲取所有線上使用者
for(UserOnlineBo bo : getAllUser()){
Long userId = bo.getId();
//匹配使用者ID
if(userId.equals(id)){
//獲取使用者Session
Session session = shiroSessionRepository.getSession(bo.getSessionId());
//標記使用者Session
SessionStatus sessionStatus = (SessionStatus) session.getAttribute(SESSION_STATUS);
//是否踢出 true:有效,false:踢出。
sessionStatus.setOnlineStatus(status.intValue() == 1);
//更新Session
customShiroSessionDAO.update(session);
}
}
}
11、線上顯示,線上使用者管理(踢出登入)
上面的功能依賴這個功能。從Redis中獲取所有有效的Session。
/**
* 獲取所有的有效Session使用者
* @return
*/
public List getAllUser() {
/*獲取所有session*/
Collection sessions = customShiroSessionDAO.getActiveSessions();
List list = new ArrayList();
for (Session session : sessions) {
UserOnlineBo bo = getSessionBo(session);
if(null != bo){
list.add(bo);
}
}
return list;
}
踢出後,不能直接退出,要不然使用者感覺莫名其妙。所有增加了一個Filter。SimpleAuthFilter.java
如果標記為踢出,會提示使用者。具體檢視原始碼以及配合專案的使用。
12、登入註冊密碼加密傳輸
這個地方好多人糾結的。比如密碼過於簡單,即使加密後也能破解。如我們把密碼:123456
,加密後就是:e10adc3949ba59abbe56e057f20f883e
這個很容易被識別,導致不安全,現在市面上的MD5破解其實就是把常用的數字,字母進行先加密,然後對比,美其名曰破解MD5。
那怎麼能讓使用者即使使用很簡單的密碼,也發現不了?
本Demo的實現方式是:MD5(登入帳號 + “固定值” + 密碼),把這個作為密碼,這樣,基本是不可能猜的出來。
//Java程式碼。UserManager.md5Pswd(UUser user);
/**
* 加工密碼,和登入一致。
* @param user
* @return
*/
public static UUser md5Pswd(UUser user){
//密碼為 email + '#' + pswd,然後MD5
user.setPswd(md5Pswd(user.getEmail(),user.getPswd()));
return user;
}
/**
* 字串返回值
* @param email
* @param pswd
* @return
*/
public static String md5Pswd(String email ,String pswd){
pswd = String.format("%s#%s", email,pswd);
pswd = MathUtil.getMD5(pswd);
return pswd;
}
//JS程式碼
var pswd = MD5(username +"#" + password);
13、密碼修改
不講了,和上面原理一致,然後具體看Demo。
14、使用者個人中心
包含的功能有[個人資料,資料修改,密碼修改,我的許可權],具體看Demo。
15、許可權的增刪改查。
具體實現看Demo。
16、角色的增刪改查
17、許可權->角色->使用者之間的關係維護
把許可權賦給角色。
把角色賦給使用者。
18、管理員許可權的自動新增
這裡每次新增一個許可權,都會新增到“管理員”角色下,保證“管理員”角色擁有最大許可權。
19、Spring定時任務資料初始化
這個Demo
因為是開放的,所以建立了一個定時任務。每20分鐘執行一次,用Mysql儲存過程重新建立表,重新插入初始化資料。
具體資料看專案中的init/sql
下的tables.sql(初始化表),init.data.sql(初始化資料)。
//定時任務配置檔案spring-timer.xml
<task:executor id="executor" pool-size="5" />
<task:scheduler id="scheduler" pool-size="10" />
<task:annotation-driven executor="executor" scheduler="scheduler" />
//Java 程式碼 RoleServiceImpl.java
/**
* 每20分鐘執行一次
*/
@Override
@Scheduled(cron = "0 0/20 * * * ? ")
public void initData() {
roleMapper.initData();
}
20、整合驗證碼
專案中package:com.sojson.common.utils.vcode
包是驗證碼的封裝包。
並且提供了一個VerifyCodeUtils.java 的驗證碼工具類。
使用方法參見:CommonController.java
類中的getVCode()
方法和getGifCode()
方法。
四、專案程式碼截圖