我的shiro之旅: 十六 验证码

博客已移至 http://blog.gogl.top

验证码几乎是所有站点都会用到的,验证码在一定程序上能有效处理一些恶意攻击。这里用到生成验证码的工具是SimpleCaptcha,目前在官网看到最新版的是1.2.1,下载地址:http://sourceforge.net/projects/simplecaptcha/files/simplecaptcha-1.2.1.jar/download。下面给出生成验证码的工具类:

 

import java.awt.Color;
import java.awt.Font;
import java.util.List;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import nl.captcha.Captcha;
import nl.captcha.servlet.CaptchaServletUtil;
import nl.captcha.text.renderer.DefaultWordRenderer;
import nl.captcha.text.renderer.WordRenderer;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.concom.lang.exception.InvalidCaptchaException;
import com.google.common.collect.ImmutableList;

/**
 * @author Dylan
 * @mail [email protected]
 * @time 2014年4月9日
 */
public final class HttpCaptchaHelper {

	private final static Logger log = LoggerFactory.getLogger(HttpCaptchaHelper.class);

	private final static String CAPTCHA_PARAM = "captcha";
	
	private final static List FOUNTS = ImmutableList.of(
			  												  new Font("Courier New", Font.ITALIC, 40), 
			  												  new Font("Arial", Font.ITALIC, 40),
			  												  new Font("Times New Roman", Font.ITALIC, 40),
			  												  new Font("Verdana", Font.ITALIC, 40)
			 												 );

	private final static List COLORS = ImmutableList.of(Color.black);

	private final static WordRenderer RENDERER = new DefaultWordRenderer(COLORS, FOUNTS);
	
	private HttpCaptchaHelper() {}

	/**store captcha to session
	 * @param captcha
	 * @param session
	 */
	public static void storeCaptcha(Captcha captcha, HttpSession session) {

		DemonPredict.notNull(session, new InvalidCaptchaException("can not get session"));
		DemonPredict.notNull(captcha, new InvalidCaptchaException(String.format("no captcha in session 【%s】", session.getId())));
		
		synchronized (session) {
			if(log.isTraceEnabled()){
				log.trace(String.format("store captcha to session 【%s】", session.getId()));
			}
			session.setAttribute(CAPTCHA_PARAM, captcha);
		}
	}
	
	/**check captcha
	 * @param captchaCode
	 * @param session
	 */
	public static void checkCaptcha(String captchaCode,HttpSession session){
		
		if(StringUtils.isBlank(captchaCode)){
			throw new InvalidCaptchaException("captcha is blank");
		}
		
		DemonPredict.notNull(session, new InvalidCaptchaException("can not get session"));
		
		Captcha captcha = (Captcha)session.getAttribute(CAPTCHA_PARAM);
		
		DemonPredict.notNull(captcha, new InvalidCaptchaException(String.format("no captcha in session 【%s】", session.getId())));
		
		DemonPredict.isTrue(captcha.isCorrect(captchaCode), new InvalidCaptchaException(String.format("the captcha is %s ,but user input %s",captcha.getAnswer(),captchaCode)));
	
		synchronized (captcha) {
			if(log.isTraceEnabled()){
				log.trace("remove captcha from session");
			}
			session.removeAttribute(CAPTCHA_PARAM);
		}
		
	}
	
	/**
	 * @return
	 */
	public static Captcha createCaptcha(){
		Captcha captcha = new Captcha.Builder(120, 40).addText(RENDERER).addNoise().build();
		return captcha;
	}
	
	/**
	 * @param response
	 * @param session
	 */
	public static void writeAndStoreCaptcha(HttpServletResponse response, HttpSession session){
		Captcha captcha = createCaptcha();
		CaptchaServletUtil.writeImage(response, captcha.getImage());
		storeCaptcha(captcha,session);
	}
	
}

 

 

 

其中DemonPredict类是自己写的一个断言类,在这里不给出代码,读者可以写一个或者用第三方包提供的断言类。这个工具类主要是生成一个Captcha,把Captcha的image写回客户端,并缓存到session中,当check后再把Captcha从session中删除。下面再给出另一个工具类,主要是针对shiro项目,在HttpCaptchaHelper基础上进一步封装。

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.web.subject.WebSubject;

import nl.captcha.Captcha;

import com.concom.lang.helper.HttpCaptchaHelper;

/**
 * @author Dylan
 * @mail [email protected]
 * @time 2014年4月9日
 */
public final class ShiroCaptchaHelper {

	
	private ShiroCaptchaHelper(){}
	
	
	/**
	 * @param captcha
	 */
	public static void storeCaptcha(Captcha captcha){
		HttpCaptchaHelper.storeCaptcha(captcha, getHttpSession());
	}
	
	
	/**
	 * @param captchaCode
	 */
	public static void checkCaptcha(String captchaCode){
		HttpCaptchaHelper.checkCaptcha(captchaCode, getHttpSession());
	}
	
	/**
	 * @param response
	 */
	public static void writeAndStoreCaptcha(HttpServletResponse response){
		HttpCaptchaHelper.writeAndStoreCaptcha(response, getHttpSession());
	}
	
	/**
	 * @return
	 */
	private static HttpSession getHttpSession(){
		ServletRequest request = ((WebSubject)SecurityUtils.getSubject()).getServletRequest(); 
		HttpSession session = ((HttpServletRequest)request).getSession(false);
		return session;
	}
}

 

 

 

下面是一个测试用的controller代码

 

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.web.util.WebUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.concom.security.infrastructure.helper.ShiroCaptchaHelper;

@Controller
public class TestController {
	

	@RequestMapping(value="/captcha.html",method=RequestMethod.GET)
	public void captcha(HttpServletRequest request,HttpServletResponse response){
		
		ShiroCaptchaHelper.writeAndStoreCaptcha(response);
	}
	
	@RequestMapping(value="/check.html",method=RequestMethod.POST)
	public void checkCaptcha(HttpServletRequest request,HttpServletResponse response){
		
		String captchaCode = WebUtils.getCleanParam(request, "captcha");
		
		ShiroCaptchaHelper.checkCaptcha(captchaCode);
	}


}

 

 

页面获取验证码的html为

 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>







	
验证码 : 看不清,请点击图片换一张(看不清换一张)


这里有一个地方要注意的是,页面的js点击事件里加了Math.random().toString(),这样火狐和ie浏览器才可以正常改变验证码。这个,这个问题在谷歌浏览器不存在。

 

 

 

 

 

你可能感兴趣的:(shiro)