這裡的
JCaptcha指的是整合在
Jasig CAS,相關設定可參考另一篇
JCaptcha integration with CAS 這裡的測試也會延續這一篇的設定。因有追查在某些裝置上驗證碼會無效的需求,需要 log 產生在登入頁的驗證碼及 User 輸入的驗證碼,以查出問題之所在,查詢 JCaptcha API 後發現沒有 interface 可取得已產出的驗證碼,同樣求神問卜後找到這一篇
使用 JCaptcha 開發圖形和聲音驗證碼,因同時使用圖形及聲音驗證所以需要驗證碼一致,透過 override API 的方式來取得產出的驗證碼,主要是參考這一篇文章將取得驗證碼整合至 Jasig CAS 及 JCaptcha 以達到目的。
使用 JCaptcha 開發圖形和聲音驗證碼有提供
SourceCode ,會直接下載來整合進 Jasig CAS 和 JCaptcha,如果只是修改 package 則不再列出,build
cas-server-core
也請參考另一篇的說明。
準備執行環境
Maven是為 build JCaptcha integration with CAS 做準備
- JDK (Java Development Kit) version 1.6+
- Apache Maven 2.2.1+
- Apache Tomcat 6+
- JA-SIG CAS 3.4.2.1+
- JCaptcha 1.0
下載 Sample
Build & Running JCaptcha Sample integration with CAS
- 將 Sample 整合至 CAS 可參考以下二張圖的對照,因不想 CAS 的相關設定改變太多,所以選擇修改 Sample 的 package 架構,不然依測試的經驗此 Sample 不用修改即可運作;保留另一篇
JCaptcha integration with CAS
文章中的CaptchaServiceSingleton.java
及 ImageCaptchaServlet.java
,因此 Sample 複製到 CAS 會有以下的對應修改,另外只處理圖形驗證碼所以聲音驗證的相關 Java file 不用複製。
com\sample\jcaptcha\service\SampleImageCaptchaService.java --> org\jasig\cas\captcha\CaptchaServiceSingleton.java
com\sample\jcaptcha\servlet\ImageCaptchaServlet.java --> org\jasig\cas\captcha\ImageCaptchaServlet.java
cas-server-3.4.3\cas-server-core\src\main\java\org\jasig\cas\captcha\factory\SampleGimpyFactory.java
line-37 在產生驗證碼的圖檔之前先取得驗證碼的文字。
package org.jasig.cas.captcha.factory;
import java.awt.image.BufferedImage;
import java.util.Locale;
import com.octo.captcha.CaptchaException;
import com.octo.captcha.CaptchaQuestionHelper;
import com.octo.captcha.component.image.wordtoimage.WordToImage;
import com.octo.captcha.component.word.wordgenerator.WordGenerator;
import com.octo.captcha.image.ImageCaptcha;
import com.octo.captcha.image.gimpy.GimpyFactory;
import org.jasig.cas.captcha.service.WordBridge;
/**
* Sample Gimpy Factory
* @author guangqingzhong
*
*/
public class SampleGimpyFactory extends GimpyFactory {
private WordBridge wordBridge = new WordBridge();
public SampleGimpyFactory(WordGenerator generator, WordToImage word2image) {
super(generator, word2image);
}
public SampleGimpyFactory(WordGenerator generator, WordToImage word2image, WordBridge wordBridge) {
super(generator, word2image);
}
public ImageCaptcha getImageCaptcha(Locale locale) {
//length
Integer wordLength = getRandomLength();
String word = getWordGenerator().getWord(wordLength, locale);
if (this.wordBridge != null) {
this.wordBridge.setGeneratedWord(word);
}
BufferedImage image = null;
try {
image = getWordToImage().getImage(word);
} catch (Throwable e) {
throw new CaptchaException(e);
}
ImageCaptcha captcha = new
SampleGimpy(CaptchaQuestionHelper.getQuestion(locale,
BUNDLE_QUESTION_KEY),
image, word);
return captcha;
}
public WordBridge getWordBridge() {
return this.wordBridge ;
}
}
cas-server-3.4.3\cas-server-core\src\main\java\org\jasig\cas\captcha\engin\SampleListImageCaptchaEngine.java
有修改成亂數取背景圖的方式,不是完全照 Sample 處理,line-57 可看到 Create Image 是使用 SampleGimpyFactory object,再透過 WordBridge object 取得驗證碼的文字,line-65 則是之前產生驗證碼圖片的方式。
package org.jasig.cas.captcha.engine;
import java.awt.Color;
import java.awt.Font;
import com.octo.captcha.component.image.backgroundgenerator.FunkyBackgroundGenerator;
import com.octo.captcha.component.image.backgroundgenerator.FileReaderRandomBackgroundGenerator;
import com.octo.captcha.component.image.color.SingleColorGenerator;
import com.octo.captcha.component.image.fontgenerator.TwistedRandomFontGenerator;
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.RandomTextPaster;
import com.octo.captcha.component.image.textpaster.textdecorator.BaffleTextDecorator;
import com.octo.captcha.component.image.textpaster.textdecorator.TextDecorator;
import com.octo.captcha.component.image.wordtoimage.ComposedWordToImage;
import com.octo.captcha.component.word.wordgenerator.RandomWordGenerator;
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.image.ImageCaptchaFactory;
import org.jasig.cas.captcha.factory.SampleGimpyFactory;
import org.jasig.cas.captcha.service.WordBridge;
/**
* Sample Image Engine
*
* @author guangqingzhong
*
*/
public class SampleListImageCaptchaEngine extends ListImageCaptchaEngine {
private WordBridge wordBridge;
public SampleListImageCaptchaEngine() {
super();
}
protected void buildInitialFactories() {
FileReaderRandomBackgroundGenerator background = new FileReaderRandomBackgroundGenerator(new Integer(180), new Integer(50),"../webapps/cas/images/backgrounds/");
Font[] fontsList = new Font[] {
new Font("Arial", 0, 10),
new Font("Tahoma", 0, 10),
new Font("Verdana", 0, 10),
};
FontGenerator fontGenerator = new RandomFontGenerator(new Integer(30), new Integer(30), fontsList);
//create text parser
TextPaster randomPaster = new RandomTextPaster(new Integer(6),
new Integer(6), new SingleColorGenerator(Color.BLACK), true);
//create image captcha factory
ImageCaptchaFactory factory = new SampleGimpyFactory(
new RandomWordGenerator("abcdefghijklmnopqrstuvwxyz0123456789"),
new ComposedWordToImage(fontGenerator, background, randomPaster));
wordBridge = ((SampleGimpyFactory)factory).getWordBridge();
ImageCaptchaFactory characterFactory[] = { factory};
this.addFactories(characterFactory);
//this.addFactory(new GimpyFactory(wgen, wordToImage));
}
public WordBridge getWordBridge() {
return wordBridge;
}
}
cas-server-3.4.3\cas-server-core\src\main\java\org\jasig\cas\captcha\CaptchaServiceSingleton.java
前一篇文章是將這一支程式同時實作 ImageCaptchaService
和 ImageCaptchaEngine
的功能,現在則採用 Sample 的方式各自實作,這裡取得 Image Buffer 後會以 sessionID 為 Key 將驗證碼文字放到 WordMap object (line - 47),另外為了 CAS 判斷驗證碼的方便性,將 CaptchaServiceSingleton 改為 ImageCaptchaService line - 20、25。
package org.jasig.cas.captcha;
import java.awt.image.BufferedImage;
import com.octo.captcha.engine.CaptchaEngine;
import com.octo.captcha.engine.image.ListImageCaptchaEngine;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.captchastore.FastHashMapCaptchaStore;
import com.octo.captcha.service.image.AbstractManageableImageCaptchaService;
import com.octo.captcha.service.image.ImageCaptchaService;
import org.jasig.cas.captcha.engine.SampleListImageCaptchaEngine;
import org.jasig.cas.captcha.service.WordMap;
import org.jasig.cas.captcha.service.WordBridge;
public class CaptchaServiceSingleton extends
AbstractManageableImageCaptchaService implements ImageCaptchaService {
//private static CaptchaServiceSingleton instance;
private static ImageCaptchaService instance;
private static ListImageCaptchaEngine engine;
//public static CaptchaServiceSingleton getInstance() {
public static ImageCaptchaService getInstance() {
if (instance == null) {
engine = new SampleListImageCaptchaEngine();
instance = new CaptchaServiceSingleton(
new FastHashMapCaptchaStore(), engine, 180, 1000, 750);
}
return instance;
}
public CaptchaServiceSingleton(CaptchaStore captchaStore,
CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds,
int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) {
super(captchaStore, captchaEngine, minGuarantedStorageDelayInSeconds,
maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection);
}
@Override
public BufferedImage getImageChallengeForID(String ID)
throws CaptchaServiceException {
BufferedImage image= super.getImageChallengeForID(ID);
String generatedWord = ((SampleListImageCaptchaEngine) engine).
getWordBridge().getGeneratedWord();
WordMap.getWordsMap().put(ID, generatedWord);
return image;
}
}
- 修改
cas-server-3.4.3\cas-server-core\src\main\java\org\jasig\cas\web\flow\AuthenticationViaFormAction.java
,於驗證錯誤時將登入前後的驗證碼文字 print 出來,參考 line-30 ~ 32
。
import com.octo.captcha.service.CaptchaServiceException;
import org.jasig.cas.captcha.CaptchaServiceSingleton;
import org.jasig.cas.captcha.service.WordMap;
....
public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception {
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
final Service service = WebUtils.getService(context);
// for captcha check input and expect value begin
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
Boolean isResponseCorrect = Boolean.FALSE;
String enterCaptchaId = request.getParameter( "j_captcha_response" );
String captchaId = request.getSession().getId();
....
try {
//WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials));
//putWarnCookieIfRequestParameterPresent(context);
//return "success";
// for captcha
if( Boolean.TRUE.equals(isResponseCorrect) ) {
WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials));
putWarnCookieIfRequestParameterPresent(context);
return "success";
} else {
// for captcha, captcha validate failed
System.out.println("Session ID=" + captchaId);
System.out.println("generated word=" + WordMap.getWordsMap().get(captchaId));
System.out.println("j_captcha_response=" + enterCaptchaId);
messageContext.addMessage( new MessageBuilder().error().code( "captcha.input.error" ).defaultText( "captcha.input.error" ).build() );
return "error";
}// for captcha
} catch (final TicketException e) {
populateErrorsInstance(e, messageContext);
return "error";
}
}
- build
cas-server-core-3.4.3.jar
,切換到 cas-server-3.4.3\cas-server-core
路徑執行下列指令,將產出的cas-server-core-3.4.3.jar
複製到cas\WEB-INF\lib
此路徑下。
mvn clean package -Dmaven.test.skip=true
- 啟動 Tomcat ,連結至CAS Login Page,輸入錯誤的驗證碼,可看到以下的 log,驗證碼為
e4ct0a
第一次出現時是進入登入頁時,第二次出現時是按登入驗證後,123456
是故意輸錯的驗證碼。
- 測試驗證碼一直出現錯誤的裝置,可看到以下的 log,於登入頁顯示的驗證碼為
691152
,但 Server 已經產生第二組驗證碼053171
了,造成驗證碼不符無法登入成功,經高人指點是因為 Browser 的行為造成的,可看相關參考的連結,說明的相當清楚就不再重覆了。
相關設定可參考:
使用 JCaptcha 開發圖形和聲音驗證碼
Response Download file for Android Browser
沒有留言:
張貼留言