验证码是有效防止暴力破解的一种手段,常用做法是在服务端产生一串随机字符串与当前用户会话关联(我们通常说的放入 Session),然后向终端用户展现一张经过“扰乱”的图片,只有当用户输入的内容与服务端产生的内容相同时才允许进行下一步操作
作为演示,我们选择开源的验证码组件 kaptcha。这样,我们只需要简单配置一个 Servlet,页面通过 IMG 标签就可以展现图形验证码。
kaptcha
com.google.code.kaptcha.servlet.KaptchaServlet
kaptcha
/images/kaptcha.jpg
扩展 UsernamePasswordTokenShiro 表单认证,页面提交的用户名密码等信息,用 UsernamePasswordToken 类来接收,很容易想到,要接收页面验证码的输入,我们需要扩展此类:
package javacommon.shiro;
import org.apache.shiro.authc.UsernamePasswordToken;
public class CaptchaUsernamePasswordToken extends UsernamePasswordToken {
//验证码字符串
private String captcha;
public CaptchaUsernamePasswordToken(String username, char[] password,
boolean rememberMe, String host, String captcha) {
super(username, password, rememberMe, host);
this.captcha = captcha;
}
public String getCaptcha() {
return captcha;
}
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
}
扩展 FormAuthenticationFilter
接下来我们扩展 FormAuthenticationFilter 类
package javacommon.shiro;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
private static final Logger LOG = LoggerFactory.getLogger(CaptchaFormAuthenticationFilter.class);
public CaptchaFormAuthenticationFilter() {
}
@Override
/**
* 登录验证
*/
protected boolean executeLogin(ServletRequest request,
ServletResponse response) throws Exception {
CaptchaUsernamePasswordToken token = createToken(request, response);
try {
/*图形验证码验证*/
doCaptchaValidate((HttpServletRequest) request, token);
Subject subject = getSubject(request, response);
subject.login(token);//正常验证
LOG.info(token.getUsername()+"登录成功");
return onLoginSuccess(token, subject, request, response);
}catch (AuthenticationException e) {
LOG.info(token.getUsername()+"登录失败--"+e);
return onLoginFailure(token, e, request, response);
}
}
// 验证码校验
protected void doCaptchaValidate(HttpServletRequest request,
CaptchaUsernamePasswordToken token) {
//session中的图形码字符串
String captcha = (String) request.getSession().getAttribute(
com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);
//比对
if (captcha != null && !captcha.equalsIgnoreCase(token.getCaptcha())) {
throw new IncorrectCaptchaException("验证码错误!");
}
}
@Override
protected CaptchaUsernamePasswordToken createToken(ServletRequest request,
ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
String captcha = getCaptcha(request);
boolean rememberMe = isRememberMe(request);
String host = getHost(request);
return new CaptchaUsernamePasswordToken(username,
password.toCharArray(), rememberMe, host, captcha);
}
public static final String DEFAULT_CAPTCHA_PARAM = "captcha";
private String captchaParam = DEFAULT_CAPTCHA_PARAM;
public String getCaptchaParam() {
return captchaParam;
}
public void setCaptchaParam(String captchaParam) {
this.captchaParam = captchaParam;
}
protected String getCaptcha(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
//保存异常对象到request
@Override
protected void setFailureAttribute(ServletRequest request,
AuthenticationException ae) {
request.setAttribute(getFailureKeyAttribute(), ae);
}
}
package javacommon.shiro;
import org.apache.shiro.authc.AuthenticationException;
public class IncorrectCaptchaException extends AuthenticationException {
public IncorrectCaptchaException() {
super();
}
public IncorrectCaptchaException(String message, Throwable cause) {
super(message, cause);
}
public IncorrectCaptchaException(String message) {
super(message);
}
public IncorrectCaptchaException(Throwable cause) {
super(cause);
}
}
/login.jsp = authc
/pages/* = authc
/index.jsp* = authc
/logout.do = logout
登录页面:
<%@page import="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"%>
<%@page import="javacommon.shiro.IncorrectCaptchaException"%>
<%@page import="org.apache.shiro.authc.AuthenticationException"%>
<%@ 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 + "/";
%>
Insert title here
<%
Object obj = request
.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
String msg = "";
if (obj != null) {
if (obj instanceof IncorrectCaptchaException)
msg = "验证码错误!";
else
msg = "账号或密码错误!";
}
out.println("" + msg + "");
%>