jcaptcha+使用redis集群保存jcaptcha

1、简述:jcaptcha是java生成图形验证码非常好的api,入手简单,配置简单,使用很少的代码完成了生成图形验证码功能

读过源码,我们知道jcaptcha通过sessionId来验证验证码的正确性


2、问题:tomcat服务器启动web应用后,会为每个用户生成一个sessionId,以此区别不同用户。由于现在的企业级web应用都比较大,通常会部署在多个tomcat服务器上,这样就产生了问题,A服务器生成的sessionId,在B服务器是没有的,假如用户在访问的是A服务器,接着可能是访问的B服务器,由于每个sessionId是根据web服务器来生成的,而服务器间不能共享互通。所以如果根据tomcat的sessionId来识别验证码正确性就出现了问题。

3、解决办法:思路,把sessionId放在redis等支持集群模式的缓存中,实现了共享互通。

pom.xml引入jar


    com.octo.captcha
    jcaptcha
    1.0
    
	
		org.springframework
		spring-beans
	
	
		 com.jhlabs
		 imaging
	
	
		org.apache.tomcat
			tomcat-juli
	
	
		javax.servlet
	servlet-api
	
    

第一种实现方式:通过UUID生成sessionId,把sessionId存到redis中

java config方式配置bean

@Configuration
//@Import(BaseWebMvcConfig.class)
public class AppConfig extends BaseAppConfig  {
	
}

public class BaseAppConfig {
	@Bean
	public CaptchaStore getCaptchaStore(){
		return new FastHashMapCaptchaStore();
	}
	
	@Bean
	public AbstractManageableImageCaptchaService captchaService(CaptchaStore captchaStore){
//		GenericManageableCaptchaService service = new GenericManageableCaptchaService(cSRCaptchaEngine(),300,20000);
//		System.out.println("------------GenericManageableCaptchaService 验证码 init--------------------");
		return new AbstractManageableImageCaptchaService(captchaStore, cSRCaptchaEngine(), 60, 100000, 75000){};
	}

	public ListImageCaptchaEngine cSRCaptchaEngine() {
		return new ListImageCaptchaEngine() {
			@Override
			protected void buildInitialFactories() {
				System.out.println("---------CSRCaptchaEngine buildInitialFactories2------ ");
				int minWordLength = 4;
				int maxWordLength = 4;
				int fontSize = 50;
				int imageWidth = 152;
				int imageHeight = 70;
				WordGenerator wordGenerator = new RandomWordGenerator("0123456789abcdefghijklmnopqrstuvwxyz");
				TextPaster randomPaster = new DecoratedRandomTextPaster(minWordLength, maxWordLength,
						new RandomRangeColorGenerator(new int[] { 0, 150 }, new int[] { 0, 150 }, new int[] { 0, 150 }),
						new TextDecorator[] { new LineTextDecorator(new Integer(1), Color.BLACK) });
				/*
				 * 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[] {new LineTextDecorator(new Integer(2),
				 * Color.BLACK)});
				 */

				BackgroundGenerator background = new UniColorBackgroundGenerator(imageWidth, imageHeight, Color.gray);

				/*
				 * 设置彩色背景
				 * BackgroundGenerator background = new
				 * FunkyBackgroundGenerator(new Integer( 260), new Integer(70));
				 */
				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(wordGenerator, word2image));
			}

		};

	}
}

controller层应用

package com.jing.controller;

import java.io.IOException;
import java.util.UUID;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.jing.util.Constants;
import com.jing.util.JedisUtils;
import com.jing.util.SenderUtils;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.image.AbstractManageableImageCaptchaService;

import redis.clients.jedis.JedisCluster;

@Controller
@RequestMapping("/userController")
public class UserController {
	
	@Resource
	private JedisCluster jedisCluster;
	
	@Resource
	private AbstractManageableImageCaptchaService captchaService;
	@Resource
	private CaptchaStore captchaStore;

	/**
	 * @description: 发送图形验证码
	 * @author: skyler
	 * @time: 2016年7月16日 下午2:40:17
	 */
	@RequestMapping(value = "/send_captcha", method = RequestMethod.GET)
	public void sendCaptcha(String times, HttpServletRequest request, HttpServletResponse response)
			throws IOException {
		String sessionId = UUID.randomUUID().toString();
		SenderUtils.sendPictureCaptcha(captchaService, sessionId, request, response);
		
		String key = "redis_picture_key:"+11;
		JedisUtils.process(jedisCluster, jedis -> {
			jedis.set(key, sessionId);
			jedis.expire(key, Constants.PICTURE_CAPTCHA_EXPIRE_TIME);
			return null;
		});
	}
	
	public void validateCaptcha(String captcha) throws Exception {
		String key = "redis_picture_key:"+11;
		String sessionId = JedisUtils.process(jedisCluster, jedis -> {
			return jedis.get(key);
		});

		if (sessionId == null) {
			throw new Exception("work.user.pictureCaptcha.timeout");
		}

		// 校验用户输入端图形验证码是否正确
		boolean isResponseCorrect = captchaService.validateResponseForID(sessionId, captcha.toLowerCase());
		JedisUtils.process(jedisCluster, jedis -> {
			return jedis.del(key);
		});
		if (!isResponseCorrect) {
			throw new Exception("work.user.pictureCaptcha.wrong");
		}
	}
}
启动应用访问:localhost:port/appNmae/userController/send_captcha?time=1234567890,浏览器页面生成4位的验证码


第二种实现方式:重写CaptchaStore类,这个类是把sessionId存到HashMap中,而HashMap是不能在多个web服务器间共享的,所以要通过重写CaptchaStore把sessionId存到redis中,上代码


pom.xml和上面一下

java config方式配置bean,重写CaptchaStore在这里实现

package com.jing.web.config;

import redis.clients.jedis.JedisCluster;

public class BaseAppConfig {
	 

	@Resource
	private JedisCluster jedisCluster;
	
	//CaptchaService captchaService;
	/**
	 * 自定义ackson ObjectMapper
	 * 
	 * @return
	 */
	@Bean
	public ObjectMapper jacksonObjectMapper() {

		ObjectMapper objectMapper = new ObjectMapper();
		// objectMapper.setSerializationInclusion(Include.NON_NULL);
		objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() {
			@Override
			public void serialize(Object value, JsonGenerator jg, SerializerProvider sp)
					throws IOException, JsonProcessingException {
				// 所有null字段,重写为空字符串
				jg.writeString("");
				sp.getDefaultNullKeySerializer();
			}
		});
		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
		return objectMapper;
	}

	@Bean
	public CaptchaStore getCaptchaStore(){
		//return new FastHashMapCaptchaStore();
		return new CaptchaStore(){

			@Override
			public void cleanAndShutdown() {
				// TODO Auto-generated method stub
				
			}

			@Override
			public void empty() {
				// TODO Auto-generated method stub
				
			}

			@Override
			public Captcha getCaptcha(String key) throws CaptchaServiceException {
				String value = JedisUtils.process(jedisCluster, jedis -> {
					return jedis.get(key);
				});
				return null;
			}

			@Override
			public Collection getKeys() {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public Locale getLocale(String arg0) throws CaptchaServiceException {
				// TODO Auto-generated method stub
				return null;
			}

			@Override
			public int getSize() {
				// TODO Auto-generated method stub
				return 0;
			}

			@Override
			public boolean hasCaptcha(String arg0) {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public void initAndStart() {
				// TODO Auto-generated method stub
				
			}

			@Override
			public boolean removeCaptcha(String arg0) {
				// TODO Auto-generated method stub
				return false;
			}

			@Override
			public void storeCaptcha(String id, Captcha captcha) throws CaptchaServiceException {
				JedisUtils.process(jedisCluster, jedis -> {
					jedis.set(id,SerializationUtils.serialize(captcha).toString());
					return null;
				});
			}

			@Override
			public void storeCaptcha(String id, Captcha captcha, Locale locale) throws CaptchaServiceException {
				// TODO Auto-generated method stub
				
			}
			
		};
	}
	
	@Bean
	public AbstractManageableImageCaptchaService captchaService(CaptchaStore captchaStore){
//		GenericManageableCaptchaService service = new GenericManageableCaptchaService(cSRCaptchaEngine(),300,20000);
//		System.out.println("------------GenericManageableCaptchaService 验证码 init--------------------");
		return new AbstractManageableImageCaptchaService(captchaStore, cSRCaptchaEngine(), 60, 100000, 75000){};
	}

	public ListImageCaptchaEngine cSRCaptchaEngine() {
		return new ListImageCaptchaEngine() {
			@Override
			protected void buildInitialFactories() {
				System.out.println("---------CSRCaptchaEngine buildInitialFactories2------ ");
				int minWordLength = 4;
				int maxWordLength = 4;
				int fontSize = 50;
				int imageWidth = 152;
				int imageHeight = 70;
				WordGenerator wordGenerator = new RandomWordGenerator("0123456789abcdefghijklmnopqrstuvwxyz");
				TextPaster randomPaster = new DecoratedRandomTextPaster(minWordLength, maxWordLength,
						new RandomRangeColorGenerator(new int[] { 0, 150 }, new int[] { 0, 150 }, new int[] { 0, 150 }),
						new TextDecorator[] { new LineTextDecorator(new Integer(1), Color.BLACK) });
				/*
				 * 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[] {new LineTextDecorator(new Integer(2),
				 * Color.BLACK)});
				 */

				BackgroundGenerator background = new UniColorBackgroundGenerator(imageWidth, imageHeight, Color.gray);

				/*
				 * BackgroundGenerator background = new
				 * FunkyBackgroundGenerator(new Integer( 260), new Integer(70));
				 */
				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(wordGenerator, word2image));
			}

		};

	}
}


controller同上



你可能感兴趣的:(jcaptcha+使用redis集群保存jcaptcha)