springsecurity3和JCaptcha的整合

JCaptcha是一个验证码的框架.
照着白衣的springside3的例子鼓捣了半天,弄出来感觉不复杂但是很有用.
不多说,放货.

创建一个jcaptcha包
在jcaptcha包里创建2个类:GMailEngine 和 JCaptchaFilter
GMailEngine :是JCaptcha验证码图片生成引擎,仿照JCaptcha2.0编写类似GMail验证码的样式.
JCaptchaFilter:是针对 JCaptcha 专门的过滤器(Filter).

另外 remember-me 这个功能我测试用 把系统时间更改下,确实是可以实现在两周之类免登录.


包结构
springsecurity3和JCaptcha的整合_第1张图片


GMailEngine.java
package com.sjax.myapp.jcaptcha;

import java.awt.Color;
import java.awt.Font;
import java.awt.image.ImageFilter;

import com.octo.captcha.component.image.backgroundgenerator.BackgroundGenerator;
import com.octo.captcha.component.image.backgroundgenerator.UniColorBackgroundGenerator;
import com.octo.captcha.component.image.color.RandomListColorGenerator;
import com.octo.captcha.component.image.deformation.ImageDeformation;
import com.octo.captcha.component.image.deformation.ImageDeformationByFilters;
import com.octo.captcha.component.image.fontgenerator.FontGenerator;
import com.octo.captcha.component.image.fontgenerator.RandomFontGenerator;
import com.octo.captcha.component.image.textpaster.DecoratedRandomTextPaster;
import com.octo.captcha.component.image.textpaster.TextPaster;
import com.octo.captcha.component.image.textpaster.textdecorator.TextDecorator;
import com.octo.captcha.component.image.wordtoimage.DeformedComposedWordToImage;
import com.octo.captcha.component.image.wordtoimage.WordToImage;
import com.octo.captcha.component.word.FileDictionary;
import com.octo.captcha.component.word.wordgenerator.ComposeDictionaryWordGenerator;
import com.octo.captcha.component.word.wordgenerator.WordGenerator;
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.image.gimpy.GimpyFactory;

/**
 * JCaptcha验证码图片生成引擎,仿照JCaptcha2.0编写类似GMail验证码的样式.
 * 
 * @author liukai
 * 
 */
public class GMailEngine extends ListImageCaptchaEngine {

	@Override
	protected void buildInitialFactories() {
		int minWordLength = 4;
		int maxWordLength = 5;
		int fontSize = 50;
		int imageWidth = 250;
		int imageHeight = 100;
		WordGenerator dictionnaryWords = new ComposeDictionaryWordGenerator(
				new FileDictionary("toddlist"));

		// word2image components
		TextPaster randomPaster = new DecoratedRandomTextPaster(minWordLength,
				maxWordLength, new RandomListColorGenerator(new Color[] {
						new Color(23, 170, 27), new Color(220, 34, 11),
						new Color(23, 67, 172) }), new TextDecorator[] {});
		BackgroundGenerator background = new UniColorBackgroundGenerator(
				imageWidth, imageHeight, Color.white);
		FontGenerator font = new RandomFontGenerator(fontSize, fontSize,
				new Font[] { new Font("nyala", Font.BOLD, fontSize),
						new Font("Bell MT", Font.PLAIN, fontSize),
						new Font("Credit valley", Font.BOLD, fontSize) });
		ImageDeformation postDef = new ImageDeformationByFilters(
				new ImageFilter[] {});
		ImageDeformation backDef = new ImageDeformationByFilters(
				new ImageFilter[] {});
		ImageDeformation textDef = new ImageDeformationByFilters(
				new ImageFilter[] {});

		WordToImage word2image = new DeformedComposedWordToImage(font,
				background, randomPaster, backDef, textDef, postDef);
		addFactory(new GimpyFactory(dictionnaryWords, word2image));
	}

}


JCaptchaFilter.java
package com.sjax.myapp.jcaptcha;

import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.octo.captcha.service.CaptchaService;
import com.octo.captcha.service.CaptchaServiceException;

/**
 * 针对 JCaptcha 专门的过滤器(Filter)
 * @author liukai
 *
 */
public class JCaptchaFilter implements Filter {

	//web.xml中的参数名定义
	public static final String PARAM_CAPTCHA_PARAMTER_NAME = "captchaParamterName";
	public static final String PARAM_CAPTCHA_SERVICE_ID = "captchaServiceId";
	public static final String PARAM_FILTER_PROCESSES_URL = "filterProcessesUrl";
	public static final String PARAM_FAILURE_URL = "failureUrl";
	public static final String PARAM_AUTO_PASS_VALUE = "autoPassValue";

	//默认值定义
	public static final String DEFAULT_FILTER_PROCESSES_URL = "/j_spring_security_check";
	public static final String DEFAULT_CAPTCHA_SERVICE_ID = "captchaService";
	public static final String DEFAULT_CAPTCHA_PARAMTER_NAME = "j_captcha";
	
	private static Logger logger = LoggerFactory.getLogger(JCaptchaFilter.class);
	
	private String failureUrl;
	private String filterProcessesUrl = DEFAULT_FILTER_PROCESSES_URL;
	private String captchaServiceId = DEFAULT_CAPTCHA_SERVICE_ID;
	private String captchaParamterName = DEFAULT_CAPTCHA_PARAMTER_NAME;
	private String autoPassValue;

	private CaptchaService captchaService;
	
	/**
	 * Filter回调初始化函数.
	 */
	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		initParameters(filterConfig);
		initCaptchaService(filterConfig);

	}

	public void doFilter(ServletRequest theRequest, ServletResponse theResponse,
			FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) theRequest;
		HttpServletResponse response = (HttpServletResponse) theResponse;
		String servletPath = request.getServletPath();
		logger.info("servletPath:"+servletPath);
		//符合filterProcessesUrl为验证处理请求,其余为生成验证图片请求.
		if (StringUtils.startsWith(servletPath, filterProcessesUrl)) {
			boolean validated = validateCaptchaChallenge(request);
			if (validated) {
				chain.doFilter(request, response);
			} else {
				redirectFailureUrl(request, response);
			}
		} else {
			genernateCaptchaImage(request, response);
		}
	}

	/**
	 * Filter回调退出函数.
	 */
	public void destroy() {
		// TODO Auto-generated method stub

	}
	
	/**
	 * 初始化web.xml中定义的filter init-param.
	 */
	protected void initParameters(final FilterConfig fConfig) {
		if (StringUtils.isBlank(fConfig.getInitParameter(PARAM_FAILURE_URL))) {
			throw new IllegalArgumentException("CaptchaFilter缺少failureUrl参数");
		}

		failureUrl = fConfig.getInitParameter(PARAM_FAILURE_URL);
		logger.info("failureUrl:"+failureUrl);

		if (StringUtils.isNotBlank(fConfig.getInitParameter(PARAM_FILTER_PROCESSES_URL))) {
			filterProcessesUrl = fConfig.getInitParameter(PARAM_FILTER_PROCESSES_URL);
		}

		if (StringUtils.isNotBlank(fConfig.getInitParameter(PARAM_CAPTCHA_SERVICE_ID))) {
			captchaServiceId = fConfig.getInitParameter(PARAM_CAPTCHA_SERVICE_ID);
		}

		if (StringUtils.isNotBlank(fConfig.getInitParameter(PARAM_CAPTCHA_PARAMTER_NAME))) {
			captchaParamterName = fConfig.getInitParameter(PARAM_CAPTCHA_PARAMTER_NAME);
		}

		if (StringUtils.isNotBlank(fConfig.getInitParameter(PARAM_AUTO_PASS_VALUE))) {
			autoPassValue = fConfig.getInitParameter(PARAM_AUTO_PASS_VALUE);
		}
	}
	
	/**
	 * 从ApplicatonContext获取CaptchaService实例.
	 */
	protected void initCaptchaService(final FilterConfig fConfig) {
		ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(fConfig.getServletContext());
		captchaService = (CaptchaService) context.getBean(captchaServiceId);
	}
	
	/**
	 * 生成验证码图片.
	 */
	protected void genernateCaptchaImage(final HttpServletRequest request, final HttpServletResponse response)
			throws IOException {

		setDisableCacheHeader(response);
		response.setContentType("image/jpeg");

		ServletOutputStream out = response.getOutputStream();
		try {
			String captchaId = request.getSession(true).getId();
			BufferedImage challenge = (BufferedImage) captchaService.getChallengeForID(captchaId, request.getLocale());
			ImageIO.write(challenge, "jpg", out);
			out.flush();
		} catch (CaptchaServiceException e) {
			logger.error(e.getMessage(), e);
		} finally {
			out.close();
		}
	}
	
	/**
	 * 验证验证码.
	 */
	protected boolean validateCaptchaChallenge(final HttpServletRequest request) {
		try {
			String captchaID = request.getSession().getId();
			logger.info("captchaID:"+captchaID);
			String challengeResponse = request.getParameter(captchaParamterName);
			logger.info("challengeResponse:"+challengeResponse);
			//自动通过值存在时,检验输入值是否等于自动通过值
			if (StringUtils.isNotBlank(autoPassValue) && autoPassValue.equals(challengeResponse)) {
				return true;
			}
			return captchaService.validateResponseForID(captchaID, challengeResponse);
		} catch (CaptchaServiceException e) {
			logger.error(e.getMessage(), e);
			return false;
		}
	}
	/**
	 * 跳转到失败页面.
	 * 
	 * 可在子类进行扩展, 比如在session中放入SpringSecurity的Exception.
	 */
	protected void redirectFailureUrl(final HttpServletRequest request, final HttpServletResponse response)
			throws IOException {
		logger.info("跳转到失败页面:"+request.getContextPath()+failureUrl);
		response.sendRedirect(request.getContextPath() + failureUrl);
	}
	
	/**
	 * 设置禁止客户端缓存的Header.
	 */
	public static void setDisableCacheHeader(HttpServletResponse response) {
		//Http 1.0 header
		response.setDateHeader("Expires", 1L);
		response.addHeader("Pragma", "no-cache");
		//Http 1.1 header
		response.setHeader("Cache-Control", "no-cache, no-store, max-age=0");
	}
	

}


然后在springsecurity的配置文件里添加,直接把文件全部拷贝过来 JCaptcha相关的配置就一段,在最下面.

application-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:s="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd"
	default-lazy-init="true">

	<s:authentication-manager>
		<s:authentication-provider>
			<s:password-encoder hash="md5" />
			<s:jdbc-user-service data-source-ref="dataSource" />
		</s:authentication-provider>
	</s:authentication-manager>

	<!-- 导入自定义的springsecurity国际化文件 -->
	<bean id="messageSource"
		class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
		<property name="basename" value="classpath:messages_zh_CN" />
	</bean>
	<bean id="localeResolver"
		class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver" />

	<s:http auto-config="true">
		<!-- 指定登录页面 -->
		<s:form-login login-page="/login.jsp" />
		<s:logout logout-success-url="/login.jsp" />
		<!-- 对登录页面不进行拦截,这个页面也许带有参数 -->
		<s:intercept-url pattern="/login.jsp*" filters="none" />
		<s:intercept-url pattern="/resources/**" filters="none" />
		
		<s:remember-me  />
		
		<!-- 会话配置管理 -->
		<s:session-management invalid-session-url="/login.jsp">
		<!-- 只允许一个人登陆,并且第二个人登陆不了 -->
			<s:concurrency-control max-sessions="1"
				error-if-maximum-exceeded="true" />
		</s:session-management>
	</s:http>

	<!-- 启动annotation -->
	<s:global-method-security secured-annotations="enabled" />



	<!-- Jcaptcha相关的配置 -->
	<bean id="captchaService"
		class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService">
		<property name="captchaEngine">
			<bean class="com.sjax.myapp.jcaptcha.GMailEngine" />
		</property>
		<!-- 默认生成的图片180秒过期 , 可另行设置 
		<property name="minGuarantedStorageDelayInSeconds" value="180" />
		-->
	</bean>
	
</beans>


然后是web.xml 这就不把所有的配置列出来 ,只列出和JCaptcha相关的东西

web.xml

<!-- JCaptcha`s filter -->
	<filter>
		<filter-name>jcaptchaFilter</filter-name>
		<filter-class>com.sjax.myapp.jcaptcha.JCaptchaFilter</filter-class>
		<init-param>
			<param-name>failureUrl</param-name>
			<param-value>/login.jsp</param-value>
		</init-param>
	</filter>
	
	<!-- jcaptcha图片生成URL. -->
	<filter-mapping>
		<filter-name>jcaptchaFilter</filter-name>
		<url-pattern>/jcaptcha.jpg</url-pattern>
	</filter-mapping>
	
	<!-- jcaptcha登录表单处理URL.
	             必须放在springSecurityFilter的filter-mapping定义之前 -->
	<filter-mapping>
		<filter-name>jcaptchaFilter</filter-name>
		<url-pattern>/j_spring_security_check</url-pattern>
	</filter-mapping>


最后就是JSP页面了
login.jsp
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ include file="/common/taglibs.jsp"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
	<head>
		<title>Login</title>
		<%@ include file="/common/meta.jsp"%>
		<link href="<c:url value="/resources/css/screen.css" />"
			rel="stylesheet" type="text/css" />
		<link href="<c:url value="/resources/css/ie.css" />" rel="stylesheet"
			type="text/css" />
		<script type="text/javascript"
			src="<c:url value="/resources/jquery/1.4/jquery.js" />">
</script>
	</head>
	<body>
		<div class="container">
			<%@ include file="/common/header.jsp"%>
			<div id="content">
				<div class="span-24 last">
					<h3>
						用户登录
					</h3>
					<div class="error">
						${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message }
					</div>
					<form id="loginForm" style="margin-top: 1em;"
						action="<c:url value="/j_spring_security_check" />" method="post"">
						<table class="noborder">
							<tr>
								<td>
									<label for="username">
										用户名:
									</label>
								</td>
								<td>
									<input type="text" name="j_username" class="required" />
								</td>
								<td rowspan="3">
									<img id="captchaImg" src="<c:url value="/jcaptcha.jpg"/>" />
								</td>
							</tr>
							<tr>
								<td>
									<label for="password">
										密码:
									</label>
								</td>
								<td>
									<input type="password" name="j_password" class="required">
								</td>
							</tr>
							<tr>
								<td>
									<label for="j_captcha">
										验证码:
									</label>
								</td>
								<td>
									<input type='text' name='j_captcha' class="required" size='5' />
								</td>
							</tr>
							<tr>
								<td colspan='3'>
									<input type="checkbox" name="_spring_security_remember_me" />
									两周内记住我
									<span style="margin-left: 25px"><a
										href="javascript:refreshCaptcha()">看不清楚换一张</a>
									</span>
								</td>
							</tr>
							<tr>
								<td colspan="2">
									<input type="submit" class="button" value="登录">
								</td>
							</tr>
						</table>
					</form>
				</div>
			</div>
			<%@ include file="/common/footer.jsp"%>
		</div>
	</body>
	<script type="text/javascript">
function refreshCaptcha() {
	$('#captchaImg').hide().attr(
			'src',
			'<c:url value="/jcaptcha.jpg"/>' + '?' + Math
					.floor(Math.random() * 100)).fadeIn();
}
</script>

</html>



我的开发环境是STS+tomcat6.29,工程是maven项目.框架用的spring3.0.5



效果图1.

springsecurity3和JCaptcha的整合_第2张图片


效果图2.

springsecurity3和JCaptcha的整合_第3张图片


BTW:因为是maven项目,所以自己要配好maven路径之类的.然后install下就行了.
如果实在不喜欢或者不会用maven,源码下下来, 照着拷贝也行的.
注意要加上JCaptcha的包还有commons-lang包
其它的spring3 springsecurity3之类的包就不多说了 打开pom.xml一个一个的看吧.

你可能感兴趣的:(spring,maven,jsp,servlet,Security)