kaptcha 是一个很有用的验证码生成工具。有了它,你能够生成各种样式的验证码,由于它是可配置的。使用kaptcha能够方便的配置:
* 验证码的字体
* 验证码字体的大小
* 验证码字体的字体颜色
* 验证码内容的范围(数字,字母,中文汉字!)
* 验证码图片的大小。边框,边框粗细,边框颜色
* 验证码的干扰线(能够自己继承com.google.code.kaptcha.NoiseProducer写一个自己定义的干扰线)
* 验证码的样式(鱼眼样式、3D、普通模糊……当然也能够继承com.google.code.kaptcha.GimpyEngine自己定义样式)
<dependency>
<groupId>com.github.pengglegroupId>
<artifactId>kaptchaartifactId>
<version>2.3.2version>
dependency>
KaptchaConfig.java
@Component
public class KaptchaConfig {
@Bean
public ServletRegistrationBean kaptchaServlet() {
ServletRegistrationBean 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表示该拦截器实例已经处理了,将直接返回即可
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");
//:这是一个坑呢,一不小心代码就不好使了;
//
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.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");
//
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;
}
......
<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>