验证码是为了区分人与电脑,防止电脑代替人冲击系统。在伟大的中国人民人工验证的海洋面前,验证码完全不是个事。
不过一般不是特别热门的系统是不会有人雇佣人民海洋来人工识别验证码的,所有我们的系统还是需要加入验证码机制。
在Java Web系统中验证码世界已经很多种的实现,有些很大很复杂,有些没有维护了,有些验证码人也认证不出来。。。比较合适用的就是Jcaptcha和Kcaptcha。
Captcha使用都是很简单的,通过专门的servlet生成图片,把图片的数据保存在session中,最后在处理请求的servlet中验证图片输入是否正常。当然一般都会有很多配置,因为Captcha生成图片需要时间,所有需要谨慎配置。
Kcaptcha与Shiro登录结合使用
1. Web.xml
<servlet>
<servlet-name>Kaptcha</servlet-name>
<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Kaptcha</servlet-name>
<url-pattern>/kaptcha.jpg</url-pattern>
</servlet-mapping>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
2. 登录Jsp
<%
String error = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
if(error != null){
if(error.contains("IncorrectCaptchaException")){
out.print("用户输入验证码错误.");
}
else{
out.print("登录失败,请重试.");
}
}
%>
<form action="">
Username: <input type="text" name="username"/> <br/>
Password: <input type="password" name="password"/>
<img src="kaptcha.jpg" /> <input type="text" name="kaptcha" value="" />
</form>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
3. Shiro登录Filter
a. 扩展登录UsernamePasswordToken,加入kaptcha字段
public class CaptchaAuthenticationToken extends UsernamePasswordToken{
private String kaptcha;
public CaptchaAuthenticationToken (){}
public CaptchaAuthenticationToken (String username, String password,
boolean rememberMe, String host, String captcha) {
super(username, password, rememberMe, host);
this.captcha = captcha;
}
public void setKaptcha(String kaptcha){
this.kaptcha= kaptcha;
}
public String getKaptcha(){
return this.kaptcha;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
b. 扩展formAuthenticationFilter,检验用户输入验证码
public class CaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
private String captchaParam = "kaptcha";
public String getCaptchaParam() {
return captchaParam;
}
protected String getCaptcha(ServletRequest request) {
return WebUtils.getCleanParam(request, getCaptchaParam());
}
@Override
protected AuthenticationToken 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 CaptchaAuthenticationToken(username, password, rememberMe,
host, captcha);
}
// 验证码校验
protected void doCaptchaValidate(HttpServletRequest request, CaptchaAuthenticationToken token) {
String captcha = (String) request.getSession().getAttribute(ShiroConstant.CAPTCHA_SESSION_KEY);
if (StringUtil.isEmpty(token.getCaptcha()) || !token.getCaptcha().equalsIgnoreCase(captcha)) {
/* 定义IncorrectCaptchaException,shiro显示Exception class name作为error信息 */
throw new IncorrectCaptchaException("验证码错误!");
}
}
/*
protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) {
String className = ae.getClass().getName();
request.setAttribute(getFailureKeyAttribute(), className);
}
*/
// 认证
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
CaptchaAuthenticationToken token = createToken(request, response);
try {
doCaptchaValidate((HttpServletRequest) request, token);
Subject subject = getSubject(request, response);
subject.login(token);
return onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
return onLoginFailure(token, e, request, response);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
c. 增加 IncorrectCaptchaException
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
d.修改Shiro配置文件,让/kaptcha.jpg的访问变成匿名
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.jsp" />
<property name="successUrl" value="/" />
<property name="filters">
<map>
<!-- 启用验证码检验 -->
<entry key="authc" value-ref="captchaFormAuthenticationFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/kaptcha.jpg = anon
/styles/** = anon
/login = authc
/logout = logout
/** = user
</value>
</property>
</bean>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20