溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

SpringMVC中使用Kaptcha驗證碼組件生成比較安全的驗證碼

發(fā)布時間:2020-07-04 22:48:18 來源:網(wǎng)絡(luò) 閱讀:3066 作者:pangfc 欄目:開發(fā)技術(shù)

一 簡介

Kaptcha是一個基于SimpleCaptcha的驗證碼開源項目,在我們的項目中使用Kaptcha組件可以快速生成比較安全的驗證碼。同時Kaptcha還提供了許多的參數(shù)可以讓我們自定義生成的驗證碼樣式

jar包的官網(wǎng)下載地址:https://code.google.com/archive/p/kaptcha/downloads

當(dāng)然,為了照顧一些翻不了墻的同學(xué),我也在51cto上上傳了一份Kaptcha最新的的jar包(kaptcha-2.3.2),傳送門:http://down.51cto.com/data/2257293

二 代碼實現(xiàn)

(1)在項目中添加Kaptcha相關(guān)jar包:

具體就是:

  • kaptcha-2.3.2.jar

(2)在spring的配置文件中添加Kaptcha相關(guān)的配置:

	<!-- Kaptcha驗證碼生成器 -->
	<bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
		<property name="config">
			<bean class="com.google.code.kaptcha.util.Config">
				<constructor-arg>
					<props>
						<prop key="kaptcha.border">no</prop><!-- 是否有邊框 -->
						<prop key="kaptcha.noise.color">25,25,25</prop><!-- 干擾線顏色 -->
						<prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.ShadowGimpy</prop>
						<prop key="kaptcha.p_w_picpath.width">140</prop>
						<prop key="kaptcha.p_w_picpath.height">40</prop>
						<prop key="kaptcha.textproducer.char.string">AZWSXEDCRFVTGBYHNUJMIKLP23456789</prop>
						<prop key="kaptcha.textproducer.font.color">4,14,156</prop><!-- 驗證碼字體顏色 -->
						<prop key="kaptcha.textproducer.font.size">36</prop><!-- 驗證碼字體大小 -->
						<prop key="kaptcha.session.key">code</prop>
						<prop key="kaptcha.textproducer.char.length">5</prop><!-- 驗證碼個數(shù) -->
						<prop key="kaptcha.textproducer.font.names">宋體,楷體,微軟雅黑</prop>
					</props>
				</constructor-arg>
			</bean>
		</property>
	</bean>

注:其他的一些可選配置:

kaptcha.border  是否有邊框  默認(rèn)為true  我們可以自己設(shè)置yes,no
kaptcha.border.color   邊框顏色   默認(rèn)為Color.BLACK
kaptcha.border.thickness  邊框粗細(xì)度  默認(rèn)為1
kaptcha.producer.impl   驗證碼生成器  默認(rèn)為DefaultKaptcha
kaptcha.textproducer.impl   驗證碼文本生成器  默認(rèn)為DefaultTextCreator
kaptcha.textproducer.char.string   驗證碼文本字符內(nèi)容范圍  默認(rèn)為abcde2345678gfynmnpwx
kaptcha.textproducer.char.length   驗證碼文本字符長度  默認(rèn)為5
kaptcha.textproducer.font.names    驗證碼文本字體樣式  默認(rèn)為new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
kaptcha.textproducer.font.size   驗證碼文本字符大小  默認(rèn)為40
kaptcha.textproducer.font.color  驗證碼文本字符顏色  默認(rèn)為Color.BLACK
kaptcha.textproducer.char.space  驗證碼文本字符間距  默認(rèn)為2
kaptcha.noise.impl    驗證碼噪點生成對象  默認(rèn)為DefaultNoise
kaptcha.noise.color   驗證碼噪點顏色   默認(rèn)為Color.BLACK
kaptcha.obscurificator.impl   驗證碼樣式引擎  默認(rèn)為WaterRipple
kaptcha.word.impl   驗證碼文本字符渲染   默認(rèn)為DefaultWordRenderer
kaptcha.background.impl   驗證碼背景生成器   默認(rèn)為DefaultBackground
kaptcha.background.clear.from   驗證碼背景顏色漸進   默認(rèn)為Color.LIGHT_GRAY
kaptcha.background.clear.to   驗證碼背景顏色漸進   默認(rèn)為Color.WHITE
kaptcha.p_w_picpath.width   驗證碼圖片寬度  默認(rèn)為200
kaptcha.p_w_picpath.height  驗證碼圖片高度  默認(rèn)為50

(3)在Controller中分別添加用于生成驗證碼以及校驗驗證碼的controller:

package cn.zifangsky.controller;

import java.awt.p_w_picpath.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.p_w_picpathio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;

import cn.zifangsky.model.VerifyCode;

@Controller
public class CaptchaController {
	
	@Autowired
	private Producer producer;
	
	/**
	 * 生成驗證碼
	 * @param request
	 * @param response
	 */
	@RequestMapping("/user/user/verify.html")
	public void generate(HttpServletRequest request,HttpServletResponse response){
		response.setDateHeader("Expires", 0);
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        response.addHeader("Cache-Control", "post-check=0, pre-check=0");
        response.setHeader("Pragma", "no-cache");
        response.setContentType("p_w_picpath/jpeg");
        
        String validateText = producer.createText();  //生成驗證碼文字
        //存儲到session中
        request.getSession().setAttribute(Constants.KAPTCHA_SESSION_KEY, validateText);
        
        BufferedImage bImage = producer.createImage(validateText);
        try {
			ServletOutputStream outputStream = response.getOutputStream();
			ImageIO.write(bImage, "jpg", outputStream);
			outputStream.flush();
			
			outputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 校驗驗證碼
	 * @return 返回是否校驗成功
	 */
	@RequestMapping(value="/user/user/checkVerifyCode.json",method={RequestMethod.POST})
	@ResponseBody
	public Map<String, String> check(@RequestBody VerifyCode verifyCode,HttpServletRequest request){
		HttpSession session = request.getSession();
		Map<String, String> result = new HashMap<>();
		
		//session中的驗證碼
		String codeFromSession = (String) session.getAttribute(Constants.KAPTCHA_SESSION_KEY);
		session.removeAttribute(Constants.KAPTCHA_SESSION_KEY);  //使用之后刪除
		
		if(StringUtils.isNotBlank(verifyCode.getVerifyCodeValue()) && StringUtils.isNotBlank(codeFromSession)){
			if(verifyCode.getVerifyCodeValue().equalsIgnoreCase(codeFromSession)){
				session.setAttribute("codeCheck", true);  //設(shè)置校驗成功標(biāo)志
				result.put("result", "ok");
			}else{
				result.put("result", "error");
			}
		}else{
			result.put("result", "error");
		}
		return result;
	}
}

注:這里使用json請求異步校驗驗證時使用了Jackson來將前臺傳遞過來的json字符串自動轉(zhuǎn)換成Java對象,因此如果像我這里采用異步校驗的話還需要配置Jackson相關(guān)的配置,具體配置如下:

i)VerifyCode.java

package cn.zifangsky.model;

public class VerifyCode {
	private String verifyCodeValue;

	public String getVerifyCodeValue() {
		return verifyCodeValue;
	}

	public void setVerifyCodeValue(String verifyCodeValue) {
		this.verifyCodeValue = verifyCodeValue;
	}
	
}

ii)向項目中引入Jackson相關(guān)的jar包:

下載地址如下:http://down.51cto.com/data/2257291

iii)在SpringMVC的配置文件中添加以下配置:

	<bean id="mappingJacksonHttpMessageConverter"
        class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
                <value>application/json;charset=UTF-8</value>
            </list>
        </property>
        <property name="objectMapper">
            <bean class="org.codehaus.jackson.map.ObjectMapper">
                <property name="dateFormat">
                    <bean class="java.text.SimpleDateFormat">
                        <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss"></constructor-arg>
                    </bean>
                </property>
            </bean>
        </property>
    </bean>
    <!-- 啟動Spring MVC的注解功能,完成請求和注解POJO的映射 -->
    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="mappingJacksonHttpMessageConverter" /><!-- json轉(zhuǎn)換器 -->
            </list>
        </property>
    </bean>
    <mvc:annotation-driven
        content-negotiation-manager="contentNegotiationManager" />
    <bean id="contentNegotiationManager"
        class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <!-- true,開啟擴展名支持,false關(guān)閉支持 -->
        <property name="favorPathExtension" value="false" />
        <!-- 用于開啟 /userinfo/123?format=json的支持 -->
        <property name="favorParameter" value="true" />
        <!-- 設(shè)置為true以忽略對Accept Header的支持 -->
        <property name="ignoreAcceptHeader" value="false" />
        <property name="mediaTypes">
            <value>
                atom=application/atom+xml
                html=text/html
                json=application/json
                xml=application/xml
                *=*/*
            </value>
        </property>
    </bean>

(4)登錄頁面:

<%@page import="java.security.SecureRandom"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<c:set var="path" value="${pageContext.request.contextPath}" />
<title>ShiroDemo2</title>
<script src="${path}/scripts/jquery/jquery-1.9.0.min.js"
	type="text/javascript"></script>
<script type="text/javascript">
	$(function() {
		$("#verifyImg").click(
				function() {
					$("#message").text("");
					$(this).attr(
							"src",
							"<c:url value='/user/user/verify.html'/>?"
									+ Math.floor(Math.random() * 100));
				});
		$("#verifyCode").keyup(function() {
			var verifyCodeValue = $("#verifyCode").val().replace("/\s/g", "");
			var data = {"verifyCodeValue":verifyCodeValue};
			if (verifyCodeValue.length == 5) {
				$.ajax({
					url : "<c:url value='/user/user/checkVerifyCode.json'/>",
					type : "POST",
					contentType : 'application/json; charset=utf-8',
					data : JSON.stringify(data),
					success : function(response) {
						if (response.result == "ok") {
							$("#message").text("ok");
							$("#password").focus();
						} else if(response.result == "error"){
							$("#message").text("error");
							$("#verifyImg").attr("src","<c:url value='/user/user/verify.html'/>?"
											+ Math.floor(Math.random() * 100));
						}
					},
					error : function(response){
						alert("提交失敗,請重新提交");
					}
				});
			}
		});
		$("#submit").click(function(){
			var action = "<c:url value='/user/user/check.json'/>";
			var data = {"username":$("#username").val(),"password":$("#password").val()};
			$.ajax({
				url : action,
				type:"POST",
				contentType:'application/json',
				data:JSON.stringify(data),
				success:function(response){
					if(response.result == "success"){
						window.location.href ="<c:url value='/user/index.html'/>";
					}else if(response.result == "error"){
						$("#error").text("登錄失敗,請重新提交");
						$("#verifyImg").attr("src","<c:url value='/user/user/verify.html'/>?"
								+ Math.floor(Math.random() * 100));
					}
				},
				error:function(response){
					alert("提交失敗,請重新提交");
				}
			});
		});
		
	});
</script>
<STYLE type="text/css">
#login {
	width: 400px;
	height: 280px;
	position: absolute;
	left: 50%;
	top: 50%;
	margin-left: -200px;
	margin-top: -140px;
	border: 1px;
	align: center;
}

#form {
	width: 300px;
	height: 160px;
	position: relative;
	left: 50%;
	top: 50%;
	margin-left: -150px;
	margin-top: -80px;
	text-align: center";
}
</STYLE>
</head>
<body>
	<div id="login">
		<div id="form">
			<form id="myform">
				<div>
					<input type="text" id="username" name="username" placeholder="用戶名" />
					<input type="password" id="password" name="password"
						placeholder="密碼" />
				</div>
				<div>
					<input type="text" id="verifyCode" name="verifyCode" maxlength="5"
						placeholder="驗證碼" /> <img id="verifyImg" name="verifyImg"
						title="點擊刷新" src="<c:url value='/user/user/verify.html'/>"
						><span id="message"
						></span>
				</div>
				<div id="submit">
					<input type="submit" value="登錄" />
					<div id="error" ></div>
				</div>
			</form>
		</div>
	</div>
</body>
</html>

前臺登錄頁面這里就不做過多解釋了,都是jQuery的一些寫法。代碼不難,如果了解jQuery的一些基本用法的話,理解上面的代碼應(yīng)該挺容易的。當(dāng)然,最后進行登錄校驗時需要判斷session中是否有“codeCheck”這個參數(shù),用于判斷驗證碼是否已經(jīng)校驗成功過。具體的代碼如下:

	/**
	 * 登錄校驗
	 * 
	 * @param UsrUser
	 *            登錄時的User對象,包括form表單中的用戶名和密碼
	 * @param request
	 * @return 是否登錄成功標(biāo)志
	 */
	@RequestMapping(value = "/user/user/check.json",method={RequestMethod.POST})
	@ResponseBody
	public Map<String, String> loginCheck(@RequestBody UsrUser user,HttpServletRequest request) {
		HttpSession session = request.getSession();
		Map<String, String> result = new HashMap<>();

		Boolean codeCheck = (Boolean) session.getAttribute("codeCheck");  //驗證碼是否校驗成功標(biāo)志
		session.removeAttribute("codeCheck");
		
		if (codeCheck != null && codeCheck) {
			UsrUserBO userBO = userManager.login(user.getUsername(), user.getPassword());
			if (userBO != null) {
				session.setAttribute("userBO", userBO); // 登錄成功之后加入session中
				result.put("result", "success");
			} else {
				result.put("result", "error");
			}
		}else{
			result.put("result", "error");
		}
		return result;
	}

(5)最后生成的驗證碼效果如下:

SpringMVC中使用Kaptcha驗證碼組件生成比較安全的驗證碼

SpringMVC中使用Kaptcha驗證碼組件生成比較安全的驗證碼

參考文章:

  • https://my.oschina.net/u/1450300/blog/486377

PS:上面圖片中的水印是我個人博客的域名,因此還請管理員手下留情不要給我標(biāo)為“轉(zhuǎn)載文章”,謝謝?。?!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI