PageRenderTime 28ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/blog/2010/10/28/roll-your-own-ajax-based-captcha-in-grails/index.html

https://bitbucket.org/cavneb/cavneb.github.io
HTML | 355 lines | 220 code | 131 blank | 4 comment | 0 complexity | 79aa04bfbf58497dd461890a58c5f7ea MD5 | raw file
  1. <!DOCTYPE html>
  2. <!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
  3. <!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
  4. <!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
  5. <head>
  6. <meta charset="utf-8">
  7. <title>Roll your own Ajax-Based Captcha in Grails - coderberry</title>
  8. <meta name="author" content="Eric Berry">
  9. <meta name="description" content="Recently, I was asked to come up with a better solution to our
  10. captcha needs. We have been using ReCaptcha, which is great
  11. but difficult to read at &hellip;">
  12. <!-- http://t.co/dKP3o1e -->
  13. <meta name="HandheldFriendly" content="True">
  14. <meta name="MobileOptimized" content="320">
  15. <meta name="viewport" content="width=device-width, initial-scale=1">
  16. <link rel="canonical" href="http://coderberry.me/blog/2010/10/28/roll-your-own-ajax-based-captcha-in-grails">
  17. <link href="/favicon.png" rel="icon">
  18. <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
  19. <link href="/atom.xml" rel="alternate" title="coderberry" type="application/atom+xml">
  20. <script src="/javascripts/modernizr-2.0.js"></script>
  21. <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  22. <script>!window.jQuery && document.write(unescape('%3Cscript src="./javascripts/lib/jquery.min.js"%3E%3C/script%3E'))</script>
  23. <script src="/javascripts/octopress.js" type="text/javascript"></script>
  24. <!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
  25. <link href="http://fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
  26. <link href="http://fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
  27. <script type="text/javascript">
  28. var _gaq = _gaq || [];
  29. _gaq.push(['_setAccount', 'UA-26417518-1']);
  30. _gaq.push(['_trackPageview']);
  31. (function() {
  32. var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  33. ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  34. var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  35. })();
  36. </script>
  37. </head>
  38. <body >
  39. <header role="banner"><hgroup>
  40. <h1><a href="/">coderberry</a></h1>
  41. </hgroup>
  42. </header>
  43. <nav role="navigation"><ul class="subscription" data-subscription="rss">
  44. <li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
  45. </ul>
  46. <form action="http://google.com/search" method="get">
  47. <fieldset role="search">
  48. <input type="hidden" name="q" value="site:coderberry.me" />
  49. <input class="search" type="text" name="q" results="0" placeholder="Search"/>
  50. </fieldset>
  51. </form>
  52. <ul class="main-navigation">
  53. <li><a href="/">Blog</a></li>
  54. <li><a href="/blog/archives">Archives</a></li>
  55. </ul>
  56. </nav>
  57. <div id="main">
  58. <div id="content">
  59. <div>
  60. <article class="hentry" role="article">
  61. <header>
  62. <h1 class="entry-title">Roll Your Own Ajax-Based Captcha in Grails</h1>
  63. <p class="meta">
  64. <time datetime="2010-10-28T00:00:00-06:00" pubdate data-updated="true">Oct 28<span>th</span>, 2010</time>
  65. | <a href="#disqus_thread">Comments</a>
  66. </p>
  67. </header>
  68. <div class="entry-content"><p>Recently, I was asked to come up with a better solution to our
  69. captcha needs. We have been using <a href="http://www.google.com/recaptcha">ReCaptcha</a>, which is great
  70. but difficult to read at times, and has caused frustrated
  71. customers and lost sales. I found a great solution at
  72. <a href="http://www.jkdesign.org/captcha">http://www.jkdesign.org/captcha</a> which displays a number of
  73. graphics and let&#8217;s the user choose the right one to prove they
  74. are human. Here is a screenshot of my implementation:</p>
  75. <p><img src="/images/posts/Using-Criteria-Builder-with-Projections-1.png"></p>
  76. <p>To make this work within Grails, I had to make several tweaks. The following files are required:</p>
  77. <ul>
  78. <li><a href="http://jqueryui.com/">JQuery 1.2+</a> (I am using version - 1.4.2)</li>
  79. <li><a href="http://jqueryui.com/">JQuery UI</a> (I am using version - 1.8.2)</li>
  80. <li><a href="http://gist.github.com/651605">jquery.simpleCaptcha-0.2.js</a></li>
  81. <li><a href="/files/captchaImages.zip">Captcha Images</a> placed in images/captchaImages</li>
  82. <li><a href="http://gist.github.com/651613">BCrypt.java</a> by Damien Miller</li>
  83. <li>CaptchaController.groovy (below)</li>
  84. </ul>
  85. <p>Create a new controller called Captcha. This can really be named anything, but if you do rename it, it will have to be updated in the jquery.simpleCaptcha-0.2.js file or passed in as an option via the javascript.</p>
  86. <div><script src='https://gist.github.com/1291660.js'></script>
  87. <noscript><pre><code></code></pre></noscript></div>
  88. <p>What this controller does is return a JSON object with the data needed to generate the captcha. The JSON appears like so:</p>
  89. <div><script src='https://gist.github.com/1291662.js'></script>
  90. <noscript><pre><code></code></pre></noscript></div>
  91. <p>Now we just need to implement this in our GSP file and controller. Suppose we have a page like shown above with a pickup code and the last 4 digits of the persons phone number. With adding our captcha div and required javascript, our GSP would look like this:</p>
  92. <div><script src='https://gist.github.com/1291664.js'></script>
  93. <noscript><pre><code></code></pre></noscript></div>
  94. <p>Finally, we need to perform the validation on the controller side. The modified authentication action would look like the following:</p>
  95. <figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
  96. <span class='line-number'>2</span>
  97. <span class='line-number'>3</span>
  98. <span class='line-number'>4</span>
  99. <span class='line-number'>5</span>
  100. <span class='line-number'>6</span>
  101. <span class='line-number'>7</span>
  102. <span class='line-number'>8</span>
  103. <span class='line-number'>9</span>
  104. <span class='line-number'>10</span>
  105. <span class='line-number'>11</span>
  106. <span class='line-number'>12</span>
  107. </pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">def</span> <span class="n">pickup</span> <span class="o">=</span> <span class="o">{</span>
  108. </span><span class='line'>
  109. </span><span class='line'> <span class="c1">// Determine if the captcha is picked correctly</span>
  110. </span><span class='line'> <span class="k">if</span> <span class="o">(</span><span class="n">params</span><span class="o">.</span><span class="na">captchaSelection</span> <span class="o">!=</span> <span class="n">session</span><span class="o">.</span><span class="na">selectedCaptchaText</span><span class="o">)</span> <span class="o">{</span>
  111. </span><span class='line'>
  112. </span><span class='line'> <span class="c1">// They selected the correct Captcha image. Continue with Authentication</span>
  113. </span><span class='line'>
  114. </span><span class='line'> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
  115. </span><span class='line'> <span class="n">flash</span><span class="o">.</span><span class="na">message</span> <span class="o">=</span> <span class="s">&quot;You did not click the correct image below. Please Try Again.&quot;</span>
  116. </span><span class='line'> <span class="o">}</span>
  117. </span><span class='line'>
  118. </span><span class='line'><span class="o">}</span>
  119. </span></code></pre></td></tr></table></div></figure>
  120. <p>So there ya go. It&#8217;s actually pretty easy and customers seem to like choosing an image much more than typing a word that is difficult to read.</p>
  121. </div>
  122. <footer>
  123. <p class="meta">
  124. <span class="byline author vcard">Posted by <span class="fn">Eric Berry</span></span>
  125. <time datetime="2010-10-28T00:00:00-06:00" pubdate data-updated="true">Oct 28<span>th</span>, 2010</time>
  126. <span class="categories">
  127. <a class='category' href='/blog/categories/ajax/'>Ajax</a>, <a class='category' href='/blog/categories/grails/'>Grails</a>, <a class='category' href='/blog/categories/security/'>Security</a>
  128. </span>
  129. </p>
  130. <div class="sharing">
  131. <a href="http://twitter.com/share" class="twitter-share-button" data-url="http://coderberry.me/blog/2010/10/28/roll-your-own-ajax-based-captcha-in-grails/" data-via="cavneb" data-counturl="http://coderberry.me/blog/2010/10/28/roll-your-own-ajax-based-captcha-in-grails/" >Tweet</a>
  132. <div class="fb-like" data-send="true" data-width="450" data-show-faces="false"></div>
  133. </div>
  134. <p class="meta">
  135. <a class="basic-alignment left" href="/blog/2010/10/12/uploading-files-in-grails/" title="Previous Post: Uploading Files in Grails">&laquo; Uploading Files in Grails</a>
  136. <a class="basic-alignment right" href="/blog/2010/11/24/paginator-for-those-suffering-from-postgresql-count-star-speed-issues/" title="Next Post: Paginator for those suffering from PostgreSQL count(*) speed issues">Paginator for those suffering from PostgreSQL count(*) speed issues &raquo;</a>
  137. </p>
  138. </footer>
  139. </article>
  140. <section>
  141. <h1>Comments</h1>
  142. <div id="disqus_thread" aria-live="polite"><noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
  143. </div>
  144. </section>
  145. </div>
  146. <aside class="sidebar">
  147. <section>
  148. <h1>Recent Posts</h1>
  149. <ul id="recent_posts">
  150. <li class="post">
  151. <a href="/blog/2013/04/23/angularjs-on-rails-4-part-2/">AngularJS on Rails 4 - Part 2</a>
  152. </li>
  153. <li class="post">
  154. <a href="/blog/2013/04/22/angularjs-on-rails-4-part-1/">AngularJS on Rails 4 - Part 1</a>
  155. </li>
  156. <li class="post">
  157. <a href="/blog/2013/02/20/rubymine-memory-config-vmoptions/">What is the best memory config for RubyMine?</a>
  158. </li>
  159. </ul>
  160. </section>
  161. <section>
  162. <h1>GitHub Repos</h1>
  163. <ul id="gh_repos">
  164. <li class="loading">Status updating...</li>
  165. </ul>
  166. <a href="https://github.com/cavneb">@cavneb</a> on GitHub
  167. <script type="text/javascript">
  168. $(document).ready(function(){
  169. if (!window.jXHR){
  170. var jxhr = document.createElement('script');
  171. jxhr.type = 'text/javascript';
  172. jxhr.src = '/javascripts/libs/jXHR.js';
  173. var s = document.getElementsByTagName('script')[0];
  174. s.parentNode.insertBefore(jxhr, s);
  175. }
  176. github.showRepos({
  177. user: 'cavneb',
  178. count: 3,
  179. skip_forks: true,
  180. target: '#gh_repos'
  181. });
  182. });
  183. </script>
  184. <script src="/javascripts/github.js" type="text/javascript"> </script>
  185. </section>
  186. </aside>
  187. </div>
  188. </div>
  189. <footer role="contentinfo"><p>
  190. Copyright &copy; 2013 - Eric Berry -
  191. <span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
  192. </p>
  193. </footer>
  194. <script type="text/javascript">
  195. var disqus_shortname = 'coderberry';
  196. // var disqus_developer = 1;
  197. var disqus_identifier = 'http://coderberry.me/blog/2010/10/28/roll-your-own-ajax-based-captcha-in-grails/';
  198. var disqus_url = 'http://coderberry.me/blog/2010/10/28/roll-your-own-ajax-based-captcha-in-grails/';
  199. var disqus_script = 'embed.js';
  200. (function () {
  201. var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
  202. dsq.src = 'http://' + disqus_shortname + '.disqus.com/' + disqus_script;
  203. (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
  204. }());
  205. </script>
  206. <div id="fb-root"></div>
  207. <script>(function(d, s, id) {
  208. var js, fjs = d.getElementsByTagName(s)[0];
  209. if (d.getElementById(id)) {return;}
  210. js = d.createElement(s); js.id = id; js.async = true;
  211. js.src = "//connect.facebook.net/en_US/all.js#appId=212934732101925&xfbml=1";
  212. fjs.parentNode.insertBefore(js, fjs);
  213. }(document, 'script', 'facebook-jssdk'));</script>
  214. <script type="text/javascript">
  215. (function(){
  216. var twitterWidgets = document.createElement('script');
  217. twitterWidgets.type = 'text/javascript';
  218. twitterWidgets.async = true;
  219. twitterWidgets.src = 'http://platform.twitter.com/widgets.js';
  220. document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
  221. })();
  222. </script>
  223. </body>
  224. </html>