天天看點

CAS增加驗證碼驗證功能

簡述:在部署CAS登入過程中,可能會用到驗證碼功能,這裡介紹一下加入驗證碼的詳細過程。

1.本文使用的CAS伺服器版本是3.5.2 release版本,驗證碼采用的是JCAPTCHA,因為我使用的Maven部署,是以隻需要在原來CAS項目中的pom.xml中加入以下代碼引入jacptcha.jar,Maven會幫我們下載下傳關聯的jar包。

<dependency>
    	<groupId>com.octo.captcha</groupId>
   	 	<artifactId>jcaptcha</artifactId>
   		<version>1.0</version>
    </dependency>
           

2.修改CAS登入頁面,修改位于WEB-INF/view/jsp/default/ui下的casLoginView.jsp,在密碼輸入框下面添加如下代碼:

<div class="row fl-controls-left">
                   	    <label for="password" class="fl-label"><spring:message code="screen.welcome.label.vcode" /></label>
                        <img alt="vcode" src="captcha.jpg" height="50px;"  width="190px;" style="padding-top:5px;padding-left:5px;"> 
						<spring:message code="screen.welcome.label.vcode.accesskey" var="vCodeAccessKey" /> 
						<form:input cssClass="required" cssErrorClass="error" id="vcode" size="25" tabindex="1" accesskey="${vCodeAccessKey}" path="vcode" autocomplete="false" htmlEscape="true" /> 
                    </div>
           

當然,screen.welcome.label.vcode以及screen.welcome.label.vcode.accesskey需要根據自己需要去WEB-INF/classes/下面的messages_xxxxx.properties修改,一般隻需要改中文和英文即可。

3.重寫credentials,修改位于WEB-INF下的login-webflow.xml,原來的代碼:

<var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
           

修改代碼後:

<var name="credentials" class="com.lyyhk.verify.bean.UsernamePasswordVCodeCredentials" />
           

并且添加對應的自定義實體類,增加一個表單綁定屬性vcode,即驗證碼,以下是自定義類的代碼:

package com.lyyhk.verify.bean;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;

public class UsernamePasswordVCodeCredentials extends UsernamePasswordCredentials {
	private static final long serialVersionUID = 1L;

	private String firstname;

	@NotNull
	@Size(min = 1, message = "required.vcode")
	/* 這裡需要到相應的屬性檔案裡面增加描述 */
	private String vcode;

	public String getVcode() {
		return vcode;
	}

	public void setVcode(String vcode) {
		this.vcode = vcode;
	}

	public String getFirstname() {
		return firstname;
	}

	public void setFirstname(String firstname) {
		this.firstname = firstname;
	}

}
           

4.原CAS送出在用戶端驗證之後就會送出到realSubmit,在這裡我們修改位于WEB-INF下的login-webflow.xml, 

原來的代碼:

<view-state id="viewLoginForm" view="casLoginView" model="credentials"> 
        <var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" /> 
        <binder> 
            <binding property="username" /> 
            <binding property="password" /> 
        </binder> 
        <on-entry> 
            <set name="viewScope.commandName" value="'credentials'" /> 
        </on-entry> 
<transition on="submit" bind="true" validate="true" to="realSubmit"> 
            <set name="flowScope.credentials" value="credentials" /> 
            <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" /> 
        </transition> 
</view-state> 
           

修改代碼後:(增加一個表單綁定屬性vcode,即驗證碼,并且把送出指向到我們自己定義的vcodeSubmit,在原來的工作流之間加入一個步驟)

<view-state id="viewLoginForm" view="casLoginView" model="credentials">
        <binder>
            <binding property="username" />
            <binding property="password" />
            <binding property="vcode"/>
        </binder>
        <on-entry>
            <set name="viewScope.commandName" value="'credentials'" />
        </on-entry>
		<transition on="submit" bind="true" validate="true" to="vcodeSubmit">
            <evaluate expression="authenticationViaFormAction.doBind(flowRequestContext, flowScope.credentials)" />
        </transition>
	</view-state>
           

并且在上述代碼下添加如下代碼:(vcodeSubmit作為一個驗證碼驗證中間站,驗證成功就跳轉到原代碼中的realSubmit中去做各種邏輯操作,如果失敗跳回generateLoginTicket後會跳回登入頁面,并且把驗證碼錯誤的資訊顯示在頁面)

<action-state id="vcodeSubmit"> 
        <evaluate expression="vcodeViaFormAction.validatorCode(flowRequestContext, flowScope.credentials, messageContext)" /> 
		<transition on="success" to="realSubmit" /> 
		<transition on="error" to="generateLoginTicket" /> 
	</action-state> 
           

5.添加完上述代碼後,打開位于WEB-INF下的cas-servlet.xml檔案,添加下面代碼:

<bean id="vcodeViaFormAction" class="com.lyyhk.verify.web.VcodeAuthenticationViaFormAction" />
           

這是一個我們自定義的驗證驗證碼的類,代碼如下面(這裡定義的CaptchaServiceSingleton代碼會在下面貼出)

package com.lyyhk.verify.web;

import org.jasig.cas.authentication.principal.Credentials;
import org.jasig.cas.web.flow.AuthenticationViaFormAction;
import org.jasig.cas.web.support.WebUtils;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.webflow.execution.RequestContext;

import com.lyyhk.verify.bean.CaptchaServiceSingleton;
import com.lyyhk.verify.bean.UsernamePasswordVCodeCredentials;

public class VcodeAuthenticationViaFormAction extends AuthenticationViaFormAction {
	private final String ERROR = "error";
	private final String SUCCESS = "success";

	public final String validatorCode(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception {
		MessageBuilder msgBuilder = new MessageBuilder();
		Boolean flag = Boolean.valueOf(false);
		String captchId = WebUtils.getHttpServletRequest(context).getSession().getId();
		UsernamePasswordVCodeCredentials upv = (UsernamePasswordVCodeCredentials) credentials;
		// 驗證驗證碼,驗證通過才真正送出表單
		flag = CaptchaServiceSingleton.getInstance().validateResponseForID(captchId, upv.getVcode());
		if (flag.booleanValue()) {
			return SUCCESS;
		}
		msgBuilder.defaultText("驗證碼有誤!");
		messageContext.addMessage(msgBuilder.error().build());
		return ERROR;
	}
}
           

CaptchaServiceSingleton代碼,GMailEngine代碼會在下面貼出

package com.lyyhk.verify.bean;

import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;

public class CaptchaServiceSingleton {
	
	private static ImageCaptchaService instance = new 
			DefaultManageableImageCaptchaService(new FastHashMapCaptchaStore(), new GMailEngine(), 180, 100000, 75000);

	public static ImageCaptchaService getInstance() {
		return instance;
	}
}
           

GMailEngine代碼

package com.lyyhk.verify.bean;

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;
import java.awt.Color;
import java.awt.Font;
import java.awt.image.ImageFilter;

public class GMailEngine extends ListImageCaptchaEngine {
	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"));

		TextPaster randomPaster = new DecoratedRandomTextPaster(Integer.valueOf(minWordLength), Integer.valueOf(maxWordLength), new RandomListColorGenerator(new Color[] { new Color(23, 170, 27), new Color(220, 34, 11), new Color(23, 67, 172) }), new TextDecorator[0]);
		BackgroundGenerator background = new UniColorBackgroundGenerator(Integer.valueOf(imageWidth), Integer.valueOf(imageHeight), Color.white);
		FontGenerator font = new RandomFontGenerator(Integer.valueOf(fontSize), Integer.valueOf(fontSize), new Font[] { new Font("nyala", 1, fontSize), new Font("Bell MT", 0, fontSize), new Font("Credit valley", 1, fontSize) });

		ImageDeformation postDef = new ImageDeformationByFilters(new ImageFilter[0]);
		ImageDeformation backDef = new ImageDeformationByFilters(new ImageFilter[0]);
		ImageDeformation textDef = new ImageDeformationByFilters(new ImageFilter[0]);

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

6.最後一步則是注冊驗證碼生成器,打開web.xml檔案,加入以下代碼:

<servlet>  
        <servlet-name>jcaptcha</servlet-name>  
        <servlet-class>com.ivan.zhang.servlet.ImageCaptchaServlet</servlet-class>  
        <load-on-startup>0</load-on-startup>  
    </servlet> 
<servlet-mapping>  
        <servlet-name>jcaptcha</servlet-name>  
        <url-pattern>/captcha.jpg</url-pattern>  
    </servlet-mapping>
           

到這裡就結束了,嘿嘿,還沒有,重新用Maven打包吧生成一個war包,所有步驟就結束了,如果不是用Maven管理項目的可以直接忽略這一句。

繼續閱讀