Shiro(3)实现验证码认证

验证码是有效防止暴力破解的一种手段,常用做法是在服务端产生一串随机字符串与当前用户会话关联(我们通常说的放入 Session),然后向终端用户展现一张经过“扰乱”的图片,只有当用户输入的内容与服务端产生的内容相同时才允许进行下一步操作

产生验证码

作为演示,我们选择开源的验证码组件 kaptcha。这样,我们只需要简单配置一个 Servlet,页面通过 IMG 标签就可以展现图形验证码。

	<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>/images/kaptcha.jpg</url-pattern>
	</servlet-mapping>


扩展 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);
	}
}



前面验证码校验不通过,我们抛出一个异常 IncorrectCaptchaException,此类继承 AuthenticationException,之所以需要扩展一个新的异常类,为的是在页面能更精准显示错误提示信息。


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);
	}
}

Filter的配置及使用

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">

	<!-- Shiro Filter 拦截器相关配置 -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- securityManager -->
		<property name="securityManager" ref="securityManager" />
		<!-- 登录路径 -->
		<property name="loginUrl" value="/login.jsp" />
		<!-- 登录成功后跳转路径 -->
		<property name="successUrl" value="/pages/index.jsp" />
		<!-- 授权失败跳转路径 -->
		<property name="unauthorizedUrl" value="/login.jsp" />
		<property name="filters">
			<util:map>
				<entry key="authc" value-ref="myAuthenFilter" />
			</util:map>
		</property>
		<!-- 过滤链定义 -->
		<property name="filterChainDefinitions">
			<value>
				/login.jsp = authc
				/pages/* = authc
				/index.jsp* = authc
				/logout.do = logout
				<!-- 访问这些路径必须拥有某种权限 /role/edit/* = perms[role:edit] /role/save = perms[role:edit] 
					/role/list = perms[role:view] -->
			</value>
		</property>
	</bean>

	<!-- 自定义验证拦截器 -->
	<bean id="myAuthenFilter" class="javacommon.shiro.CaptchaFormAuthenticationFilter" />

	<!-- securityManager -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="myRealm" />
	</bean>

	<!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
		<property name="cacheManager" ref="cacheManager" /> </bean> -->

	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />

	<!-- 自定义Realm实现 -->
	<bean id="myRealm" class="javacommon.shiro.CustomRealm">
		<!-- <property name="cacheManager" ref="shiroCacheManager" /> -->
	</bean>
</beans>

登录页面:


<%@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 + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath%>">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style type="text/css">
.error {
	color: red;
}
</style>
<script type="text/javascript">
function refreshCaptcha(){
	document.getElementById("img_captcha").src="<%=basePath%>images/kaptcha.jpg?t=" + Math.random();
}
</script>
</head>
<body>

	<%
		Object obj = request
				.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
		String msg = "";
		if (obj != null) {
			if (obj instanceof IncorrectCaptchaException)
				msg = "验证码错误!";
			else 
				msg = "账号或密码错误!";
		}
		
		out.println("<div class='error'>" + msg + "</div>");
	%>

	<form action="login.jsp" method="post">
		<input type="hidden" name="rememberMe" value="true" /> <br />
		<table>

			<tr>
				<td>用户帐号:</td>
				<td><input type="text" name="username" id="username" value="" /></td>
			</tr>
			<tr>
				<td>登录密码:</td>
				<td><input type="password" name="password" id="password"
					value="" /></td>
			</tr>
			<tr>
				<td>验证码:</td>
				<td><input type="text" name="captcha" /></td>
			</tr>
			<tr>
				<td> </td>
				<td><img alt="验证码" src="images/kaptcha.jpg" title="点击更换"
					id="img_captcha" onclick="javascript:refreshCaptcha();">(看不清<a href="javascript:void(0)" onclick="javascript:refreshCaptcha()">换一张</a>)</td>
			</tr>
			<tr>
				<td colspan="2"><input value="登录" type="submit"></td>
			</tr>
		</table>

	</form>
</body>
</html>


你可能感兴趣的:(spring,Web,shiro,验证码)