SpringBoot+Shiro學習之“記住我”和“GIF驗證碼”功能的實現 頂 原

學習目標

如標題有如下兩個功能實現:

  • 記住我的功能:通過設置key爲“rememberMe”的cookie保存在客戶端來完成記住我的功能,下次用戶訪問指定頁面時就不會重新登錄,一直到cookie過期後纔會重新登錄。

  • GIF格式驗證碼: ,這個要感謝sojson的博主對這個GIF驗證碼插件的實現。

個人博客:http://z77z.oschina.io/

此項目下載地址:https://git.oschina.net/z77z/springboot_mybatisplus

記住我

  • ShiroConfig的配置:

在ShiroConfig.java中添加如下方法:

/**
 * cookie對象;
 * @return
 */
public SimpleCookie rememberMeCookie(){
   //這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe
   SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
   //<!-- 記住我cookie生效時間30天 ,單位秒;-->
   simpleCookie.setMaxAge(2592000);
   return simpleCookie;
}

/**
 * cookie管理對象;記住我功能
 * @return
 */
public CookieRememberMeManager rememberMeManager(){
   CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
   cookieRememberMeManager.setCookie(rememberMeCookie());
   //rememberMe cookie加密的密鑰 建議每個項目都不一樣 默認AES算法 密鑰長度(128 256 512 位)
   cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
   return cookieRememberMeManager;
}

rememberMeCookie()方法是設置Cookie的生成模版,比如cookie的name,cookie的有效時間等等。 rememberMeManager()方法是生成rememberMe管理器,而且要將這個rememberMe管理器設置到securityManager中,如下:

@Bean
public SecurityManager securityManager() {
	DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
	// 設置realm.
	securityManager.setRealm(myShiroRealm());
	// 自定義緩存實現 使用redis
	securityManager.setCacheManager(cacheManager());
	// 自定義session管理 使用redis
	securityManager.setSessionManager(SessionManager());
	//注入記住我管理器;
    securityManager.setRememberMeManager(rememberMeManager());
	return securityManager;
}

其實上面的步驟,也就是rememberMe管理器可以不用配置,shiro會使用默認的配置,之所以要配置的目的是爲了能夠在實際業務環境中自定義其中的參數。

  • 登錄controller的改造
@RequestMapping(value="ajaxLogin",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> submitLogin(String username, String password,Boolean rememberMe,Model model) {
	Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
	try {
		UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
		SecurityUtils.getSubject().login(token);
		resultMap.put("status", 200);
		resultMap.put("message", "登錄成功");

	} catch (Exception e) {
		resultMap.put("status", 500);
		resultMap.put("message", e.getMessage());
	}
	return resultMap;
}

之前我是將shiro已經實現的UsernamePasswordToken類再封裝了一層,最後發現沒有必要,直接使用shiro提供的UsernamePasswordToken的類,其中有一個構造函數需要傳rememberMe這個參數,也就是shiro爲我們已經實現好了,推薦大家去看下UsernamePasswordToken這個類的源碼。

  • jsp頁面的改造
<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path;
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript"
	src="<%=basePath%>/static/js/jquery-1.11.3.js"></script>
<title>登錄</title>
</head>
<body>
	錯誤信息:
	<h4 id="erro"></h4>
	<form>
		<p>
			賬號:<input type="text" name="username" id="username" value="admin" />
		</p>
		<p>
			密碼:<input type="text" name="password" id="password" value="123" />
		</p>		
		<P><input type="checkbox" name="rememberMe"  id="rememberMe" />記住我</P>
		<p>
			<input type="button" id="ajaxLogin" value="登錄" />
		</p>
		
	</form>
</body>
<script>
$(function(){
	$("#ajaxLogin").click(function() {
		var username = $("#username").val();
		var password = $("#password").val();
		var rememberMe =$('#rememberMe').is(':checked');
		$.post("/ajaxLogin", {
			"username" : username,
			"password" : password,
			"rememberMe" : rememberMe
		}, function(result) {
			if (result.status == 200) {
				location.href = "/index";
			} else {
				$("#erro").html(result.message);
			}
		});
	});
});
</script>
</html>

添加一個記住我的單選框,來控制是否需要記住我。

  • 修改權限配置,修改sys_permission_init表

鏈接權限控制表

因爲getGifCode是獲取驗證碼的鏈接,所以要配置爲anon,不需要權限驗證。user權限是配置記住我或認證通過可以訪問,所以將/**鏈接設置爲user權限,就可以實現記住我的功能。

注意權限添加的排序。 GIF驗證碼

  • 編寫一個獲取GIF驗證碼圖片的controller:
/**
 * 獲取驗證碼(Gif版本)
 * @param response
 */
@RequestMapping(value="getGifCode",method=RequestMethod.GET)
public void getGifCode(HttpServletResponse response,HttpServletRequest request){
	try {
		response.setHeader("Pragma", "No-cache");  
        response.setHeader("Cache-Control", "no-cache");  
        response.setDateHeader("Expires", 0);  
        response.setContentType("image/gif");  
        /**
         * gif格式動畫驗證碼
         * 寬,高,位數。
         */
        Captcha captcha = new GifCaptcha(146,33,4);
        //輸出
        captcha.out(response.getOutputStream());
        HttpSession session = request.getSession(true);  
        //存入Session
        session.setAttribute("_code",captcha.text().toLowerCase());  
	} catch (Exception e) {
		System.err.println("獲取驗證碼異常:"+e.getMessage());
	}
}

生成驗證碼後,將圖片返回到頁面,將字符串保存在當前會話的session域中。

這個GIF驗證碼的生成插件源碼在我的項目io.z77z.vcode這個包下面,大家需要的話可以在我的碼雲上去下載。

  • 改造登錄controller:
/**
 * ajax登錄請求
 * @param username
 * @param password
 * @return
 */
@RequestMapping(value="ajaxLogin",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> submitLogin(String username, String password,String vcode,Boolean rememberMe,Model model) {
	Map<String, Object> resultMap = new LinkedHashMap<String, Object>();
	
	if(vcode==null||vcode==""){
		resultMap.put("status", 500);
		resultMap.put("message", "驗證碼不能爲空!");
		return resultMap;
	}
	
	Session session = SecurityUtils.getSubject().getSession();
	//轉化成小寫字母
	vcode = vcode.toLowerCase();
	String v = (String) session.getAttribute("_code");
	//還可以讀取一次後把驗證碼清空,這樣每次登錄都必須獲取驗證碼
	//session.removeAttribute("_come");
   	if(!vcode.equals(v)){
   		resultMap.put("status", 500);
		resultMap.put("message", "驗證碼錯誤!");
		return resultMap;
   	}
	
	try {
		UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
		SecurityUtils.getSubject().login(token);
		resultMap.put("status", 200);
		resultMap.put("message", "登錄成功");

	} catch (Exception e) {
		resultMap.put("status", 500);
		resultMap.put("message", e.getMessage());
	}
	return resultMap;
}

登錄的時候判斷前臺傳入的驗證碼和session中的是否一致。

  • 前端jsp頁面改造
<!DOCTYPE html>
<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path;
%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript"
	src="<%=basePath%>/static/js/jquery-1.11.3.js"></script>
<title>登錄</title>
</head>
<body>
	錯誤信息:
	<h4 id="erro"></h4>
	<form>
		<p>
			賬號:<input type="text" name="username" id="username" value="admin" />
		</p>
		<p>
			密碼:<input type="text" name="password" id="password" value="123" />
		</p>
		<p>
			驗證碼:<input type="text" name="vcode" id="vcode"/>
		</p>
		<p>
			<img alt="驗證碼" src="/getGifCode">
		</p>
		
		<P><input type="checkbox" name="rememberMe"  id="rememberMe" />記住我</P>
		<p>
			<input type="button" id="ajaxLogin" value="登錄" />
		</p>
		
	</form>
</body>
<script>
$(function(){
	$("#ajaxLogin").click(function() {
		var username = $("#username").val();
		var password = $("#password").val();
		var vcode = $("#vcode").val();
		var rememberMe =$('#rememberMe').is(':checked');
		$.post("/ajaxLogin", {
			"username" : username,
			"password" : password,
			"vcode" : vcode,
			"rememberMe" : rememberMe
		}, function(result) {
			if (result.status == 200) {
				location.href = "/index";
			} else {
				$("#erro").html(result.message);
			}
		});
	});
});
</script>
</html>

總結:

到此,我們集成shiro和redis,學習了一下功能的實現:

  1. 用戶必須要登陸之後才能訪問定義鏈接,否則跳轉到登錄頁面,被禁用戶不能登錄。並且對一些敏感操作鏈接設置權限,只有滿足權限的纔可以訪問。
  2. 每個鏈接的權限信息保存在數據庫,可以動態進行設置,並且熱加載權限。
  3. 使用redis對shiro的用戶信息進行緩存,不用每次都去執行MyShiroRealm.doGetAuthorizationInfo()權限認證方法。
  4. 之前有很多同學下載我的項目時,運行會報錯,那是因爲最近都在不斷修改提交,有可能會出現版本問題,現在我在我的碼雲上面創建了stable_version分支,都是可以跑起來的。sqltable放在resource目錄下面。
  5. “記住我”的功能,利用cookie。
  6. GIF驗證碼的生成,在登陸時進行驗證碼的校驗。利用session。
  7. 下一博,我應該會寫對在線用戶的管理,踢出登錄的功能學習記錄。

香蕉硬幣點贊走一波啦。。。。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章