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