驗證碼實現
關於kaptcha
kaptcha 是一個很有用的驗證碼生成工具。有了它,你能夠生成各種樣式的驗證碼,由於它是可配置的。使用kaptcha能夠方便的配置:
- 驗證碼的字型
- 驗證碼字型的大小
- 驗證碼字型的字型顏色
- 驗證碼內容的範圍(數字,字母,中文漢字!)
- 驗證碼圖片的大小。邊框,邊框粗細,邊框顏色
- 驗證碼的干擾線(能夠自己繼承com.google.code.kaptcha.NoiseProducer寫一個自己定義的干擾線)
- 驗證碼的樣式(魚眼樣式、3D、普通模糊……當然也能夠繼承com.google.code.kaptcha.GimpyEngine自己定義樣式)
maven依賴
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
複製程式碼
注入驗證碼Servlet
** KaptchaConfig.java **
@Component
public class KaptchaConfig {
@Bean
public ServletRegistrationBean<KaptchaServlet> kaptchaServlet() {
ServletRegistrationBean<KaptchaServlet> registrationBean = new ServletRegistrationBean<>(new KaptchaServlet(), "/captcha/kaptcha.jpg");
registrationBean.addInitParameter(Constants.KAPTCHA_SESSION_CONFIG_KEY,
Constants.KAPTCHA_SESSION_KEY);
//寬度
registrationBean.addInitParameter(Constants.KAPTCHA_IMAGE_WIDTH,"140");
//高度
registrationBean.addInitParameter(Constants.KAPTCHA_IMAGE_HEIGHT,"60");
//字型大小
registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_SIZE,"50");
// registrationBean.addInitParameter(Constants.KAPTCHA_BORDER_THICKNESS,"1"); //邊框
//無邊框
registrationBean.addInitParameter(Constants.KAPTCHA_BORDER,"no");
//文字顏色
registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
//長度
registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
//字元間距
registrationBean.addInitParameter(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "6");
//可以設定很多屬性,具體看com.google.code.kaptcha.Constants
// kaptcha.border 是否有邊框 預設為true 我們可以自己設定yes,no
// kaptcha.border.color 邊框顏色 預設為Color.BLACK
// kaptcha.border.thickness 邊框粗細度 預設為1
// kaptcha.producer.impl 驗證碼生成器 預設為DefaultKaptcha
// kaptcha.textproducer.impl 驗證碼文字生成器 預設為DefaultTextCreator
// kaptcha.textproducer.char.string 驗證碼文字字元內容範圍 預設為abcde2345678gfynmnpwx
// kaptcha.textproducer.char.length 驗證碼文字字元長度 預設為5
// kaptcha.textproducer.font.names 驗證碼文字字型樣式 預設為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
// kaptcha.textproducer.font.size 驗證碼文字字元大小 預設為40
// kaptcha.textproducer.font.color 驗證碼文字字元顏色 預設為Color.BLACK
// kaptcha.textproducer.char.space 驗證碼文字字元間距 預設為2
// kaptcha.noise.impl 驗證碼噪點生成物件 預設為DefaultNoise
// kaptcha.noise.color 驗證碼噪點顏色 預設為Color.BLACK
// kaptcha.obscurificator.impl 驗證碼樣式引擎 預設為WaterRipple
// kaptcha.word.impl 驗證碼文字字元渲染 預設為DefaultWordRenderer
// kaptcha.background.impl 驗證碼背景生成器 預設為DefaultBackground
// kaptcha.background.clear.from 驗證碼背景顏色漸進 預設為Color.LIGHT_GRAY
// kaptcha.background.clear.to 驗證碼背景顏色漸進 預設為Color.WHITE
// kaptcha.image.width 驗證碼圖片寬度 預設為200
// kaptcha.image.height 驗證碼圖片高度 預設為50
return registrationBean;
}
}
複製程式碼
在這裡我們注入了一個連結為“/captcha/kaptcha.jpg”的servlet。點選執行專案開啟連結如果看到驗證碼圖片,則說明配置成功了。
驗證碼攔截器
** CaptchaValidateFilter.java **
ublic class CaptchaValidateFilter extends AccessControlFilter {
private String captchaParam = "captchaCode"; //前臺提交的驗證碼引數名
private String failureKeyAttribute = "shiroLoginFailure"; //驗證失敗後儲存到的屬性名
public String getCaptchaCode(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
// 從session獲取正確的驗證碼
Session session = SecurityUtils.getSubject().getSession();
//頁面輸入的驗證碼
String captchaCode = getCaptchaCode(request);
String validateCode = (String)session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
//判斷驗證碼是否表單提交(允許訪問)
if ( !"post".equalsIgnoreCase(httpServletRequest.getMethod())) {
return true;
}
// 若驗證碼為空或匹配失敗則返回false
if(captchaCode == null) {
return false;
} else if (validateCode != null) {
captchaCode = captchaCode.toLowerCase();
validateCode = validateCode.toLowerCase();
if(!captchaCode.equals(validateCode)) {
return false;
}
}
return true;
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//如果驗證碼失敗了,儲存失敗key屬性
request.setAttribute(failureKeyAttribute, "驗證碼錯誤");
return true;
}
public String getCaptchaParam() {
return captchaParam;
}
public void setCaptchaParam(String captchaParam) {
this.captchaParam = captchaParam;
}
}
複製程式碼
驗證碼攔截器繼承了AccessControlFilter,該類提供了訪問控制的基礎功能,比如是否允許訪問/當訪問拒絕時如何處理等。主要有兩個方法:
- isAccessAllowed:表示是否允許訪問;mappedValue就是[urls]配置中攔截器引數部分,如果允許訪問返回true,否則false;
- onAccessDenied:表示當訪問拒絕時是否已經處理了;如果返回true表示需要繼續處理;如果返回false表示該攔截器例項已經處理了,將直接返回即可
修改ShiroConfig.java
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//攔截器.
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
// 配置不會被攔截的連結 順序判斷
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/img/**", "anon");
filterChainDefinitionMap.put("/layui/**", "anon");
filterChainDefinitionMap.put("/captcha/**", "anon");
filterChainDefinitionMap.put("/favicon.ico", "anon");
//配置退出 過濾器,其中的具體的退出程式碼Shiro已經替我們實現了
filterChainDefinitionMap.put("/logout", "logout");
//<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心程式碼就不好使了;
//<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問; user”表示訪問該地址的使用者是身份驗證通過或RememberMe登入的都可以-->
filterChainDefinitionMap.put("/add", "perms[add]");
filterChainDefinitionMap.put("/login", "captchaVaildate,authc");
filterChainDefinitionMap.put("/**", "user");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
shiroFilterFactoryBean.setLoginUrl("/login");
// 登入成功後要跳轉的連結
shiroFilterFactoryBean.setSuccessUrl("/index");
//未授權介面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");
//自定義攔截器
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
filters.put("captchaVaildate", new CaptchaValidateFilter());
filters.put("authc", new MyFormAuthenticationFilter());
return shiroFilterFactoryBean;
}
複製程式碼
在表單驗證攔截器前加入驗證碼攔截器
記住登入實現
ShiroConfig的配置
在ShiroConfig.java中新增如下方法:
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
......
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setSecurityManager(securityManager);
.....
}
/**
* 安全管理器
* @return securityManager
*/
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
//注入記住我管理器
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
/**
* cookie物件;
* rememberMeCookie()方法是設定Cookie的生成模版,比如cookie的name,cookie的有效時間等等。
* @return rememberMeCookie
*/
@Bean
public SimpleCookie rememberMeCookie(){
//這個引數是cookie的名稱,對應前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//<!-- 記住我cookie生效時間30天 ,單位秒;-->
simpleCookie.setMaxAge(30*24*60*60);
simpleCookie.setHttpOnly(true);
return simpleCookie;
}
/**
* cookie管理物件;
* rememberMeManager()方法是生成rememberMe管理器,而且要將這個rememberMe管理器設定到securityManager中
* @return rememberMeManager
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的金鑰 建議每個專案都不一樣 預設AES演算法 金鑰長度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
......
複製程式碼
login頁面
<!DOCTYPE html>
<html lang="en" class="no-js" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>登入--layui後臺管理模板</title>
<link rel="stylesheet" href="../../layui/css/layui.css" media="all" />
<link rel="stylesheet" href="../css/login.css" media="all" />
</head>
<body>
<div class="login">
<h1>layuiCMS-管理登入</h1>
<form class="layui-form" method="post">
<div class="layui-form-item">
<input class="layui-input" name="username" placeholder="使用者名稱" type="text" autocomplete="off"/>
</div>
<div class="layui-form-item">
<input class="layui-input" name="password" placeholder="密碼" type="password" autocomplete="off"/>
</div>
<div class="layui-form-item form_code">
<input class="layui-input" name="captchaCode" placeholder="驗證碼" lay-verify="required" type="text" autocomplete="off"/>
<div>
<img type="image" src="../captcha/kaptcha.jpg" id="codeImage" onclick="chageCode()" title="圖片看不清?點選重新得到驗證碼" style="cursor:pointer;" width="116" height="36"/>
</div>
</div>
<div class="layui-form-item">
<input type="checkbox" name="rememberMe" title="記住我" lay-skin="primary"/>
</div>
<button class="layui-btn login_btn" lay-submit="" lay-filter="login">登入</button>
</form>
</div>
<script type="text/javascript" src="../layui/layui.js"></script>
<script th:inline="javascript">
layui.use(['layer'], function(){
var layer = layui.layer;
var message = [[${shiroLoginFailure}]]?[[${shiroLoginFailure}]]:getUrlPara("shiroLoginFailure");
if(message) {
layer.msg(message);
}
});
function getUrlPara(name)
{
var url = document.location.toString();
var arrUrl = url.split("?"+name +"=");
var para = arrUrl[1];
console.log(para);
if(para)
return decodeURI(para);
}
function chageCode(){
document.getElementById("codeImage").src="../captcha/kaptcha.jpg?"+Math.random();
}
</script>
</body>
</html>
複製程式碼