JFinal自动集成了验证码模块,使用起来也非常简单
后台Controller
public class UserController extends Controller{
private static final String FORM_ITEM_CODE = "code";
/**
* 返回验证码
*/
public void code(){
renderCaptcha();
}
public void login(){
String result = "";
//验证验证码
if(validateCaptcha(FORM_ITEM_CODE)){
// 验证码验证成功
result = "验证成功";
}else{
result = "验证失败";
}
renderText(result);
}
}
前端html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="name"/></br>
密码: <input type="password" name="password"/>
<!-- 获取验证码并设置点击事件,点击之后获取新的验证码 -->
<img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="/user/code" onclick="this.src='/user/code?x='+Math.random()"></br>
<!-- 设置name为code 用于后台接收验证码并进行校验-->
验证码:<input type="text" name="code"/>
<button type="submit">登陆</button>
</form>
</body>
</html>
效果如下
这样就实现了简单验证码的实现,接下来看下JFinal是如何实现的
首先看下JFInal是如何生成验证码的
/**
* 返回验证码
*/
public void code(){
renderCaptcha();
}
public void renderCaptcha() {
render = renderFactory.getCaptchaRender();
}
public Render getCaptchaRender() {
return new CaptchaRender();
}
这里返回了一个CaptchaRender对象,主要看下其中的render()方法生产验证码
/**
* 生成验证码
*/
public void render() {
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//draw一个Image的验证码,并返回验证码vCode,生成验证码的方法作者加了详细的注释,这里不多说
String vCode = drawGraphic(image);
vCode = vCode.toUpperCase(); // 转成大写重要
vCode = HashKit.md5(vCode);
// 把生产的验证码进行md5加密,保持到cookie中,键:_jfinal_captcha,值:md5之后的验证码
// 这里将会在校验的时候使用
Cookie cookie = new Cookie(captchaName, vCode);
cookie.setMaxAge(-1);
cookie.setPath("/");
try {
// try catch 用来兼容不支持 httpOnly 的 tomcat、jetty
cookie.setHttpOnly(true);
} catch (Exception e) {
LogKit.logNothing(e);
}
response.addCookie(cookie);
response.setHeader("Pragma","no-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
ServletOutputStream sos = null;
try {
sos = response.getOutputStream();
ImageIO.write(image, "jpeg", sos);
} catch (IOException e) {
if (getDevMode()) {
throw new RenderException(e);
}
} catch (Exception e) {
throw new RenderException(e);
} finally {
if (sos != null) {
try {sos.close();} catch (IOException e) {LogKit.logNothing(e);}
}
}
}
校验验证码
public boolean validateCaptcha(String paraName) {
return com.jfinal.render.CaptchaRender.validate(this, getPara(paraName));
}
/**
* 仅能验证一次,验证后立即销毁 cookie
* @param controller 控制器
* @param userInputCaptcha 用户输入的验证码
* @return 验证通过返回 true, 否则返回 false
*/
public static boolean validate(Controller controller, String userInputCaptcha) {
if (StrKit.isBlank(userInputCaptcha)) {
return false;
}
// 这里把用户输入的验证码md5之后和之前生成之后存储在cookie中的md5验证码进行比对校验
userInputCaptcha = userInputCaptcha.toUpperCase(); // 转成大写重要
userInputCaptcha = HashKit.md5(userInputCaptcha);
boolean result = userInputCaptcha.equals(controller.getCookie(captchaName));
if (result == true) {
controller.removeCookie(captchaName);
}
return result;
}
再来梳理一遍流程
- 后台生产验证码,进行md5加密
- 存储的cookie中
- 返回验证码图片到前台
- 用户输入验证码,后台获取到用户的验证码,进行md5加密,同时取出第二步中设置到cookie中的验证码,对二者进行比对
前端cookie
可以看到JFinal的验证码检验依赖于用户输入验证码和存储的cookie中的验证码md5值,而前端的cookie可以轻易进行更改,并且这里的采取普通的md5加密算法没有加入任何的私钥,这就使得非法用户可以轻易的构建出md5密文,从而绕开验证码的拦截。
解决方案一:放弃cookie存储,存储的session中
解决方案二:对验证码加密算法进行改进,加入私钥或者采取其他算法
点击查看更多内容
3人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦