添加驗證碼功能html
在Application.java中添加一個action:captcha()java
- /**
- * 添加驗證碼
- */
- public static void captcha(String id) {
- //Images.Captcha繼承了InputStream,具有流的功能
- Images.Captcha captcha = Images.captcha();
- //向客戶端輸出流
- renderBinary(captcha);
- }
修改routes文件,爲得到驗證碼添加新的路由緩存
- GET /captcha Application.captcha
訪問http://localhost:9000/captcha,便可顯示驗證碼,每次刷新都顯示不一樣的驗證碼安全
服務端與客戶端如何處理驗證碼cookie
驗證碼生成了,服務端如何對其進行校驗呢?session
首先,play是一個無狀態的框架,它不會去維護每一個客戶端的session狀態框架
session以cookie形式存儲的,可是cookie未加密,在客戶端可以獲取到captcha,不安全dom
說一千道一萬,要驗證都須要對客戶端顯示的驗證碼進行跟蹤才能實現,否則丟了關聯如何驗證?!post
解決辦法:ui
在服務端使用緩存來保存驗證碼,爲其生成一個ID,將此ID傳到客戶端
再由客戶端顯示驗證碼時傳回服務端,服務端拿到ID後保存驗證碼
同時,客戶端的form中增長一個hidden來保存ID
當提交表單時,經過這個ID就能找到當時生成的驗證碼,繼而驗證
集羣環境下,這個緩存中的驗證碼怎麼處理呢?
Server A 處理請求,生成驗證碼,同時保存ID,返回驗證碼
提交表單時,請求被分配給Server B 進行處理,Server B 的緩存裏沒有這個ID啊,怎麼辦?
共享緩存,好比,使用Redis做爲幾個Server共用的一個大緩存
改造Application的captcha(),將驗證碼captcha放到緩存中
- /**
- * 添加驗證碼
- * @param id 服務端緩存中存放的驗證碼的id(一個uuid)
- */
- public static void captcha(String id) {
- //Images.Captcha繼承了InputStream,具有流的功能
- Images.Captcha captcha = Images.captcha();
- //爲驗證碼指定顏色並返回驗證碼
- String code = captcha.getText("#E4EAFD");
- //放到緩存中,緩存有效期10mn mn分鐘?
- Cache.set(id, code, "10mn");
- //向客戶端輸出流
- renderBinary(captcha);
- }
在顯示評論的窗體時,生成一個惟一的ID,返回到客戶端的頁面中
- /**
- * 顯示詳細的博文評論
- */
- public static void show(Long id) {
- Post post = Post.findById(id);
- //生成一個惟一的ID,做爲服務端保存驗證碼時的key
- String randomID = Codec.UUID();
- render(post, randomID);
- }
在show.html模板中顯示驗證碼時,傳入此ID
- <!-- 顯示一個表單,用戶能夠添加評論 -->
- <h3>Post a comment</h3>
- #{form @Application.postComment(post.id)}
- <!-- 在這裏顯示提交評論時出現的錯誤 -->
- #{ifErrors}
- <p class="error">All fields are required!</p>
- #{/ifErrors}
- <p>
- <label for="author">Your name:</label>
- <input type="text" name="author" id="author"/>
- </p>
- <p>
- <label for="content">Your comment:</label>
- <textarea name="content" id="content"></textarea>
- </p>
- <!-- 驗證碼 -->
- <p>
- <label for="code">Please type the code below:</label>
- <img alt="captcha" src="@{Application.captcha(randomID)}">
- <br/>
- <input type="text" name="code" id="code" size="18" value=""/>
- <input type="hidden" name="id" value="${randomID}">
- </p>
- <p>
- <input type="submit" value="submit your comment"/>
- </p>
- #{/form}
刷新頁面,點擊博文添加評論
服務端對驗證碼進行校驗
客戶端已經經過隱藏域保存了驗證碼的ID,提交表單後,服務端的postComment()接收到ID後就能進行驗證操做了!
讓postComment()接收ID並從緩存中取出驗證碼,進行校驗
此外,具體指定了@Required標明的字段爲空時,對應的提示信息
- /**
- * 添加評論
- * 使用@Required註解,檢測author和content參數不能爲空
- *
- * @param code 客戶端輸入的驗證碼
- * @param randomID 服務端保存驗證碼時用的ID
- */
- public static void postComment(
- Long postId,
- @Required(message="Author is required") String author,
- @Required(message="A comment is required") String content,
- @Required(message="Please type the code below") String code,
- String randomID) {
- Post post = Post.findById(postId);
- //驗證碼校驗,若是equals()返回false,則message()中的信息將傳遞到客戶端
- validation.equals(code, Cache.get(randomID)
- ).message("Invalid code,Please type it again!");
- //錯誤檢測
- if(validation.hasErrors()) {
- //將post對象從新傳入模板中,不然新打開的show.html中的post爲null!!!
- render("Application/show.html",post);
- }
- //保存評論信息
- post.addComment(author, content);
- //設置提交成功後的提示信息到flash做用域
- flash.success("Thanks for posting %s", author);
- //從新顯示該篇博文即其評論
- show(postId);
- }
修改show.html模板,若是驗證失敗,則顯示驗證失敗的提示信息
- <!-- 顯示一個表單,用戶能夠添加評論 -->
- <h3>Post a comment</h3>
- #{form @Application.postComment(post.id)}
- <!--
- 這裏顯示提交評論時出現的錯誤
- 因爲postComment()中已經使用@Required(message="xxx")聲明瞭錯誤提示信息
- 因此,這裏只須要顯示第一個錯誤便可!
- -->
- #{ifErrors}
- <p class="error">${errors[0]}</p>
- #{/ifErrors}
- <p>
- <label for="author">Your name:</label>
- <input type="text" name="author" id="author"/>
- </p>
- <p>
- <label for="content">Your comment:</label>
- <textarea name="content" id="content"></textarea>
- </p>
- <!-- 驗證碼 -->
- <p>
- <label for="code">Please type the code below:</label>
- <img alt="captcha" src="@{Application.captcha(randomID)}">
- <br/>
- <input type="text" name="code" id="code" size="18" value=""/>
- <input type="hidden" name="id" value="${randomID}">
- </p>
- <p>
- <input type="submit" value="submit your comment"/>
- </p>
- #{/form}
驗證錯誤的狀況下,爲了保持客戶端輸入的評論內容,在action中重傳客戶端輸入的內容
修改Application.postComment(),這裏有不少須要注意的地方!!!
- /**
- * 添加評論
- * 使用@Required註解,檢測author和content參數不能爲空
- *
- * @param code 客戶端輸入的驗證碼
- * @param randomID 服務端保存驗證碼時用的ID
- */
- public static void postComment(
- Long postId,
- String author,
- @Required(message="A comment is required") String content,
- @Required(message="Please type the code below") String code,
- String randomID) {
- System.out.println("Application.postComment()");
- Post post = Post.findById(postId);
- //驗證碼校驗,若是equals()返回false,則message()中的信息將傳遞到客戶端
- Logger.info("提交評論,randomID="+randomID);
- Logger.info("提交評論,驗證碼="+code);
- validation.equals(code, Cache.get(randomID)
- ).message("Invalid code,Please type it again!");
- //錯誤檢測
- if(validation.hasErrors()) {
- /**
- * 將post對象從新傳入模板中,不然新打開的show.html中的post爲null!!!
- * 若是出現錯誤,則將客戶端輸入的評論內容從新返回到客戶端進行回顯。
- * 必須將randomID從新傳回到客戶端,否則客戶端的然的randomID將取不到值
- * 最後致使提價評論後,驗證碼的ID爲空,沒法進行驗證碼的校驗操做--老是校驗錯誤,這裏很關鍵!
- * 並且,這裏render("Application/show.html")直接調用模板,不會再調用show()進行驗證碼ID的生成
- * 因此,一個客戶端在未驗證成功以前,都將使用這個ID做爲服務端緩存的key進行存儲
- */
- render("Application/show.html",post,randomID,author,content);
- }
- //保存評論信息
- post.addComment(author, content);
- //設置提交成功後的提示信息到flash做用域
- flash.success("Thanks for posting %s", author);
- //清除指定驗證碼的緩存
- Cache.delete(randomID);
- //從新顯示該篇博文即其評論
- show(postId);
- }
在show.html中,爲評論的2個輸入域增長value屬性,用來顯示回顯的內容
- <p>
- <label for="author">Your name:</label>
- <!-- ${author}:回填數據 -->
- <input type="text" name="author" id="author" value="${author}"/>
- </p>
- <p>
- <label for="content">Your comment:</label>
- <!-- ${content}:回填數據 -->
- <textarea name="content" id="content">${content}</textarea>
- </p>
刷新頁面,從新評論
後臺校驗發現驗證碼錯誤,刷新show.html,回顯上一次輸入的評論並從新生成驗證碼
輸入正確的驗證碼,提交評論,成功!
輸入正確的驗證碼以後,評論提交成功!
另外,如今對驗證碼的要求是,嚴格區分大小寫!
要實現IgnoreCase,也簡單,保存驗證碼到緩存的時候,校驗的時候作點手腳就好了!