簡述:在部署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管理項目的可以直接忽略這一句。