Liferay 创建新用户页面中随机文本验证分析

 Liferay 6.1的创建新用户页面如图:

这个页面的代码在/html/portlet/login/create_account.jsp中: 

  
  
  
  
  1. ... 
  2. <portlet:actionURL var="createAccoutURL"> 
  3.     <portlet:param name="saveLastPath" value="0" /> 
  4.     <portlet:param name="struts_action" value="/login/create_account" /> 
  5. </portlet:actionURL> 
  6.  
  7. <aui:form action="<%= createAccoutURL %>" method="post" name="fm"> 
  8.     <aui:input name="<%= Constants.CMD %>" type="hidden" value="<%= Constants.ADD %>" /> 
  9.     <aui:input name="redirect" type="hidden" value="<%= redirect %>" /> 
  10.     <aui:input name="openId" type="hidden" value="<%= openId %>" /> 
  11.  
  12. .. 
  13.  
  14.         <
  15.         UserPasswordException upe = (UserPasswordException)errorException; 
  16.         %> 
  17.  
  18.         <c:if test="<%= upe.getType() == UserPasswordException.PASSWORD_CONTAINS_TRIVIAL_WORDS %>"> 
  19.             <liferay-ui:message key="that-password-uses-common-words-please-enter-in-a-password-that-is-harder-to-guess-i-e-contains-a-mix-of-numbers-and-letters" /> 
  20.         </c:if> 
  21.  
  22.         <c:if test="<%= upe.getType() == UserPasswordException.PASSWORD_INVALID %>"> 
  23.             <liferay-ui:message key="that-password-is-invalid-please-enter-in-a-different-password" /> 
  24.         </c:if> 
  25.  
  26.         <c:if test="<%= upe.getType() == UserPasswordException.PASSWORD_LENGTH %>"> 
  27.             <%= LanguageUtil.format(pageContext, "that-password-is-too-short-or-too-long-please-make-sure-your-password-is-between-x-and-512-characters", String.valueOf(passwordPolicy.getMinLength()), false) %> 
  28.         </c:if> 
  29.  
  30.         <c:if test="<%= upe.getType() == UserPasswordException.PASSWORD_TOO_TRIVIAL %>"> 
  31.             <liferay-ui:message key="that-password-is-too-trivial" /> 
  32.         </c:if> 
  33.  
  34.         <c:if test="<%= upe.getType() == UserPasswordException.PASSWORDS_DO_NOT_MATCH %>"> 
  35.             <liferay-ui:message key="the-passwords-you-entered-do-not-match-each-other-please-re-enter-your-password" /> 
  36.         </c:if> 
  37.     </liferay-ui:error> 
  38.  
  39.     <liferay-ui:error exception="<%= UserScreenNameException.class %>" message="please-enter-a-valid-screen-name" /> 
  40.     <liferay-ui:error exception="<%= WebsiteURLException.class %>" message="please-enter-a-valid-url" /> 
  41.  
  42.     <c:if test='<%= SessionMessages.contains(request, "missingOpenIdUserInformation") %>'> 
  43.         <div class="portlet-msg-info"> 
  44.             <liferay-ui:message key="you-have-successfully-authenticated-please-provide-the-following-required-information-to-access-the-portal" /> 
  45.         </div> 
  46.     </c:if> 
  47.  
  48.     <aui:model-context model="<%= Contact.class %>" /> 
  49.  
  50.     <aui:fieldset> 
  51.         <aui:column> 
  52.             <aui:input model="<%= User.class %>" name="firstName" /> 
  53.  
  54.             <aui:input model="<%= User.class %>" name="middleName" /> 
  55.  
  56.             <aui:input model="<%= User.class %>" name="lastName"> 
  57.                 <c:if test="<%= PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys.USERS_LAST_NAME_REQUIRED, PropsValues.USERS_LAST_NAME_REQUIRED) %>"> 
  58.                     <aui:validator name="required" /> 
  59.                 </c:if> 
  60.             </aui:input> 
  61.  
  62.             <c:if test="<%= !PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys.USERS_SCREEN_NAME_ALWAYS_AUTOGENERATE) %>"> 
  63.                 <aui:input model="<%= User.class %>" name="screenName" /> 
  64.             </c:if> 
  65.  
  66.             <aui:input model="<%= User.class %>" name="emailAddress"> 
  67.                 <c:if test="<%= PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys.USERS_EMAIL_ADDRESS_REQUIRED, PropsValues.USERS_EMAIL_ADDRESS_REQUIRED) %>"> 
  68.                     <aui:validator name="required" /> 
  69.                 </c:if> 
  70.             </aui:input> 
  71.         </aui:column> 
  72.  
  73.         <aui:column> 
  74.             <c:if test="<%= PropsValues.LOGIN_CREATE_ACCOUNT_ALLOW_CUSTOM_PASSWORD %>"> 
  75.                 <aui:input label="password" name="password1" size="30" type="password" value="" /> 
  76.  
  77.                 <aui:input label="enter-again" name="password2" size="30" type="password" value=""> 
  78.                     <aui:validator name="equalTo"> 
  79.                         '#<portlet:namespace />password1' 
  80.                     </aui:validator> 
  81.                 </aui:input> 
  82.             </c:if> 
  83.  
  84.             <c:choose> 
  85.                 <c:when test="<%= PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys.FIELD_ENABLE_COM_LIFERAY_PORTAL_MODEL_CONTACT_BIRTHDAY) %>"> 
  86.                     <aui:input name="birthday" value="<%= birthday %>" /> 
  87.                 </c:when> 
  88.                 <c:otherwise> 
  89.                     <aui:input name="birthdayMonth" type="hidden" value="<%= Calendar.JANUARY %>" /> 
  90.                     <aui:input name="birthdayDay" type="hidden" value="1" /> 
  91.                     <aui:input name="birthdayYear" type="hidden" value="1970" /> 
  92.                 </c:otherwise> 
  93.             </c:choose> 
  94.  
  95.             <c:if test="<%= PrefsPropsUtil.getBoolean(company.getCompanyId(), PropsKeys.FIELD_ENABLE_COM_LIFERAY_PORTAL_MODEL_CONTACT_MALE) %>"> 
  96.                 <aui:select label="gender" name="male"> 
  97.                     <aui:option label="male" value="1" /> 
  98.                     <aui:option label="female" selected="<%= !male %>" value="0" /> 
  99.                 </aui:select> 
  100.             </c:if> 
  101.  
  102.             <c:if test="<%= PropsValues.CAPTCHA_CHECK_PORTAL_CREATE_ACCOUNT %>"> 
  103.                 <portlet:actionURL windowState="<%= LiferayWindowState.EXCLUSIVE.toString() %>" var="captchaURL"> 
  104.                     <portlet:param name="struts_action" value="/login/captcha" /> 
  105.                 </portlet:actionURL> 
  106.  
  107.                 <liferay-ui:captcha url="<%= captchaURL %>" /> 
  108.             </c:if> 
  109.         </aui:column> 
  110.     </aui:fieldset> 
  111.  
  112.     <aui:button-row> 
  113.         <aui:button type="submit" /> 
  114.     </aui:button-row> 
  115. </aui:form> 
  116.  
  117. <liferay-util:include page="/html/portlet/login/navigation.jsp" /> 
  118.  
  119. <c:if test="<%= windowState.equals(WindowState.MAXIMIZED) %>"> 
  120.     <aui:script> 
  121.         Liferay.Util.focusFormField(document.<portlet:namespace />fm.<portlet:namespace />firstName); 
  122.     </aui:script> 
  123. </c:if> 

 

这里我们讲一个比较有趣的地方-随机文本验证:

 

随机文本验证:

这个对应的页面元素为:

  
  
  
  
  1. <c:if test="<%= PropsValues.CAPTCHA_CHECK_PORTAL_CREATE_ACCOUNT %>"> 
  2.                 <portlet:actionURL windowState="<%= LiferayWindowState.EXCLUSIVE.toString() %>" var="captchaURL"> 
  3.                     <portlet:param name="struts_action" value="/login/captcha" /> 
  4.                 </portlet:actionURL> 
  5.  
  6.                 <liferay-ui:captcha url="<%= captchaURL %>" /> 
  7.             </c:if> 

 

它的外观部分展示在liferay-ui.tld中:

  
  
  
  
  1. <tag> 
  2.         <name>captcha</name> 
  3.         <tag-class>com.liferay.taglib.ui.CaptchaTag</tag-class> 
  4.         <body-content>JSP</body-content> 
  5.         <attribute> 
  6.             <name>url</name> 
  7.             <required>true</required> 
  8.             <rtexprvalue>true</rtexprvalue> 
  9.         </attribute> 
  10.  </tag> 

看出,它的<body-content>是一段jsp代码段:

它只需要一个参数,是从上面的<portlet:param name="struts_action" value="/login/captcha" /> 传过来的。

 

而对应的是tag的类是 CaptchaTag,查看这个类

  
  
  
  
  1. ... 
  2.     private static final String _PAGE = "/html/taglib/ui/captcha/page.jsp"
  3. .. 

 

可以知道captcha标记包含的jsp代码位于/html/taglib/ui/captcha/page.jsp中:

  
  
  
  
  1. <%@ include file="/html/taglib/ui/captcha/init.jsp" %> 
  2.  
  3. <liferay-util:include page="<%= CaptchaUtil.getTaglibPath() %>" /> 

 

最终找到的页面是/html/taglib/ui/captcha/simplecaptcha.jsp

  
  
  
  
  1. ...
  2.  
  3. <%@ include file="/html/taglib/ui/captcha/init.jsp" %> 
  4.  
  5. <
  6. String url = (String)request.getAttribute("liferay-ui:captcha:url"); 
  7.  
  8. boolean captchaEnabled = false
  9.  
  10. try { 
  11.     if (portletRequest != null) { 
  12.         captchaEnabled = CaptchaUtil.isEnabled(portletRequest); 
  13.     } 
  14.     else { 
  15.         captchaEnabled = CaptchaUtil.isEnabled(request); 
  16.     } 
  17. catch (CaptchaMaxChallengesException cmce) { 
  18.     captchaEnabled = true
  19. %> 
  20.  
  21. <c:if test="<%= captchaEnabled %>"> 
  22.     <div class="taglib-captcha"> 
  23.         <img alt="<liferay-ui:message key="text-to-identify" />class="captcha" src="<%= url %>" /> 
  24.  
  25.         <aui:input label="text-verification" name="captchaText" size="10" type="text" value=""> 
  26.             <aui:validator name="required" /> 
  27.         </aui:input> 
  28.     </div> 
  29. </c:if> 

 

对比页面源码,果然这两段是一致的:

 

再深入下去,还能找到更多有趣的发现,比如这个url,是在liferay-ui;captcha:url中指定的:

  
  
  
  
  1. String url = (String)request.getAttribute("liferay-ui:captcha:url"); 

 

这个url可以回溯到create_account.jsp中的定义:

  
  
  
  
  1. <portlet:actionURL windowState="<%= LiferayWindowState.EXCLUSIVE.toString() %>" var="captchaURL"> 
  2.                 <portlet:param name="struts_action" value="/login/captcha" /> 
  3.             </portlet:actionURL> 
  4.  
  5.             <liferay-ui:captcha url="<%= captchaURL %>" /> 

 

所以这个url不是页面url,而是局部url,这个url指向了随即生成的数字图片地址。

它会提交给struts框架,我们在struts-config.xml中找到与/login/captcha url映射匹配的处理类:

  
  
  
  
  1. <action path="/login/captcha" type="com.liferay.portal.captcha.CaptchaPortletAction" /> 

 

我们找到这个类研究下,发现:

  
  
  
  
  1. public class CaptchaPortletAction extends PortletAction { 
  2.  
  3.     @Override 
  4.     public void processAction( 
  5.             ActionMapping mapping, ActionForm form, PortletConfig portletConfig, 
  6.             ActionRequest actionRequest, ActionResponse actionResponse) 
  7.         throws Exception { 
  8.  
  9.         try { 
  10.             CaptchaUtil.serveImage(actionRequest, actionResponse); 
  11.  
  12.             setForward(actionRequest, ActionConstants.COMMON_NULL); 
  13.         } 
  14.         catch (Exception e) { 
  15.             _log.error(e); 
  16.         } 
  17.     } 
  18.  
  19. .. 

它最终会让CaptchaUtil来负责绘制图形:

  
  
  
  
  1. public static void serveImage( 
  2.             PortletRequest portletRequest, PortletResponse portletResponse) 
  3.         throws IOException { 
  4.  
  5.         getCaptcha().serveImage(portletRequest, portletResponse); 
  6.     } 

而CaptchaUtil最终又会让Captcha类(最终是SimpleCaptchaImpl类)来绘制图形:

  
  
  
  
  1. public void serveImage( 
  2.             PortletRequest portletRequest, PortletResponse portletResponse) 
  3.         throws IOException { 
  4.  
  5.         PortletSession portletSession = portletRequest.getPortletSession(); 
  6.  
  7.         nl.captcha.Captcha simpleCaptcha = getSimpleCaptcha(); 
  8.  
  9.         portletSession.setAttribute( 
  10.             WebKeys.CAPTCHA_TEXT, simpleCaptcha.getAnswer()); 
  11.  
  12.         HttpServletResponse response = PortalUtil.getHttpServletResponse( 
  13.             portletResponse); 
  14.  
  15.         CaptchaServletUtil.writeImage( 
  16.             response.getOutputStream(), simpleCaptcha.getImage()); 
  17.     } 

而绘制图形的实际代码在CaptchaServletUtil中,它位于simplecaptcha.jar包中:

  
  
  
  
  1. public static void writeImage(HttpServletResponse httpservletresponse, BufferedImage bufferedimage) 
  2.    { 
  3.        httpservletresponse.setHeader("Cache-Control""private,no-cache,no-store"); 
  4.        httpservletresponse.setContentType("image/png"); 
  5.        try 
  6.        { 
  7.            writeImage(((OutputStream) (httpservletresponse.getOutputStream())), bufferedimage); 
  8.        } 
  9.        catch(IOException ioexception) 
  10.        { 
  11.            ioexception.printStackTrace(); 
  12.        } 
  13.    } 

可以看出,这个图片格式为 image/png,并且这个图片永远是不会被浏览器缓存的。

 

最终实际承担画图任务的是ImageIO类:

  
  
  
  
  1. ImageIO.write(bufferedimage, "png", outputstream); 

 

 

 

你可能感兴趣的:(captcha,liferay,随机图片,用户注册页面分析)