play手把手教你创建一个博客项目-05.添加图形验证码功能captcha
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
05.添加图形验证码功能captcha
由于谁都可以在我们的博客里发表评论,因此我们需要对其进行保护,以防止垃圾邮件入侵。
最简单的方式就是在窗体里添加一个图形验证码(captcha)功能。
生成验证码图形
下面,我们将开始学习如何使用play来生成验证码图形。
通常情况下,除了其能够返回一个二进制流来代替HTML响应外,它就是一个action。
既然play是一个全堆栈的web框架,那么我们将试着为web应用程序的大多数特定需求引入内建的概念,生成图形验证码功能就是其中之一。
我们可以使用play.libs.Images工具来简单生成一个验证码图形,然后写到HTTP响应中。
通常情况下,我们将从一个简单的实现开始,首先在Application控制器里添加一个captcha action:
public static void captcha() {
Images.Captcha captcha = Images.captcha();
renderBinary(captcha);
}
请注意,我们可以直接把captcha对象传递给renderBinary()方法,这是因为Images.Captcha类已经实现了java.io.InputStream接口。
别忘了导入play.libs.*。
接下来在/yabe/conf/routes文件里添加一条新路由:
GET /captcha Application.captcha 打开http://localhost:9000/captcha地址可以验证captcha action是否正常:
每次刷新,都会显示一个随机生成的文本图形。
我们该如何去管理它的状态呢?
到现在为止还比较简单,但更复杂的部分马上到来。
为了验证captcha,我们需要保存写入到captcha图形的随机文本,并且在窗体提交的时候进行检测。
当然,我们可以把图形生成时的文本放入用户的session里,在需要的时候再取回来。
但是这个方法有两个弊端:
首先, Play session是以cookie方式存储的。
它解决了java结构限制产生的许多问题。
但也存在一些问题,写入session cookie的数据都是已签名的(因此用户不能对其进行修改),但未加密。
如果我们把captcha文本写到session 里,那么所有人都能很容易通过读取session cookie获得这个captcha文本。
其次,请记住play是一个无状态框架,我们打算以纯粹的无状态方式管理所有事情。
特别是,当一个用户同时打开两个带有不同captcha图形的博客页面时会发生什么?我们就不得不跟踪每个窗体的captcha文本。
因此,要想解决这些问题,我们需要做两件事。
首先,我们得把captcha secret key存储在服务器端,因为这是一个短暂的数据,所以我们可以用play缓存进行存储,而且缓存的数据有一个有限的生成时间,这就使其更加安全(比如我们让captcha文本只存在10mn)。
然后,在之后的应用中为了能够找到captcha文本,我们需要生成一个唯一的ID,这个ID将以隐藏域的方式添加到每个窗体里,并且暗中引用一个生成的captcha文本。
通过这种方式,我们优雅的解决了状态问题。
修改captcha action:
public static void captcha(String id) {
Images.Captcha captcha = Images.captcha();
String code = captcha.getText("#E4EAFD");
Cache.set(id, code, "10mn");
renderBinary(captcha);
}
请注意, getText() 方法可以用任何颜色值作为参数,play将会使用这个给定的颜色来显示文本。
别忘了导入play.cache.*。
添加验证码图形到评论窗体
现在,在显示一个评论窗体前,我们将生成一个唯一ID,然后修改窗体使用这个ID来集成验证码图形,并且添加这个ID到另外一隐藏域:
首先重写Application.show action:
public static void show(Long id) {
Post post = Post.findById(id);
String randomID = Codec.UUID();
render(post, randomID);
}
接下来修改/yable/app/views/Application/show.html模板:
…
<p>
<label for="content">Your message: </label>
<textarea name="content" id="content">${params.content}</textarea> </p>
<p>
<label for="code">Please type the code below: </label>
<img src="@{Application.captcha(randomID)}" />
<br />
<input type="text" name="code" id="code" size="18" value="" />
<input type="hidden" name="randomID" value="${randomID}" />
</p>
<p>
<input type="submit" value="Submit your comment" /> </p>
…
OK,现在评论窗体就拥有一个验证码图形。
对验证码图形进行验证
由于我们已经在窗体里添加了隐藏域randomID,因此我们就可以在postComment action里得到它的值,然后就可以从Cache里得到captcha文本进行比较。
接下来,让我们修改postComment action:
public static void postComment(
Long postId,
@Required(message="Author is required") String author,
@Required(message="A message is required") String content, @Required(message="Please type the code") String code,
String randomID)
{
Post post = Post.findById(postId);
validation.equals(
code, Cache.get(randomID)
).message("Invalid code. Please type it again");
if(validation.hasErrors()) {
render("Application/show.html", post, randomID);
}
post.addComment(author, content);
flash.success("Thanks for posting %s", author);
Cache.delete(randomID);
show(postId);
}
由于现在已经拥有了一时更多的错误信息,因此我们可以修改show.html模板用于显示错误消息(是的,我们只需显示第一条错误信息就足够了):
..
#{ifErrors}
<p class="error">
${errors[0]}
</p>
#{/ifErrors}
…
对于更复杂的窗体,错误信息并不通过这种方式进行管理,而是通过messages 文件,把错误信息打印到每个对应的字段里的错误信息进行打印。
验证码图形功能制作完成!。