You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LoginForm.java 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. package com.vaadin.ui;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.Serializable;
  4. import java.lang.reflect.Method;
  5. import java.net.URL;
  6. import java.util.HashMap;
  7. import java.util.Iterator;
  8. import java.util.Map;
  9. import com.vaadin.Application;
  10. import com.vaadin.terminal.ApplicationResource;
  11. import com.vaadin.terminal.DownloadStream;
  12. import com.vaadin.terminal.ParameterHandler;
  13. import com.vaadin.terminal.URIHandler;
  14. import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
  15. /**
  16. * LoginForm is a Vaadin component to handle common problem among Ajax
  17. * applications: browsers password managers don't fill dynamically created forms
  18. * like all those UI elements created by Vaadin.
  19. * <p>
  20. * For developer it is easy to use: add component to a desired place in you UI
  21. * and add LoginListener to validate form input. Behind the curtain LoginForm
  22. * creates an iframe with static html that browsers detect.
  23. * <p>
  24. * Login form is by default 100% width and height, so consider using it inside a
  25. * sized {@link Panel} or {@link Window}.
  26. * <p>
  27. * Login page html can be overridden by replacing protected getLoginHTML method.
  28. * As the login page is actually an iframe, styles must be handled manually. By
  29. * default component tries to guess the right place for theme css.
  30. * <p>
  31. * Note, this is a new Ajax terminal specific component and is likely to change.
  32. *
  33. * @since 5.3
  34. */
  35. @SuppressWarnings("serial")
  36. public class LoginForm extends CustomComponent {
  37. private Embedded iframe = new Embedded();
  38. private ApplicationResource loginPage = new ApplicationResource() {
  39. public Application getApplication() {
  40. return LoginForm.this.getApplication();
  41. }
  42. public int getBufferSize() {
  43. return getLoginHTML().length;
  44. }
  45. public long getCacheTime() {
  46. return -1;
  47. }
  48. public String getFilename() {
  49. return "login";
  50. }
  51. public DownloadStream getStream() {
  52. return new DownloadStream(new ByteArrayInputStream(getLoginHTML()),
  53. getMIMEType(), getFilename());
  54. }
  55. public String getMIMEType() {
  56. return "text/html";
  57. }
  58. };
  59. private ParameterHandler paramHandler = new ParameterHandler() {
  60. public void handleParameters(Map parameters) {
  61. if (parameters.containsKey("username")) {
  62. getWindow().addURIHandler(uriHandler);
  63. HashMap params = new HashMap();
  64. // expecting single params
  65. for (Iterator it = parameters.keySet().iterator(); it.hasNext();) {
  66. String key = (String) it.next();
  67. String value = ((String[]) parameters.get(key))[0];
  68. params.put(key, value);
  69. }
  70. LoginEvent event = new LoginEvent(params);
  71. fireEvent(event);
  72. }
  73. }
  74. };
  75. private URIHandler uriHandler = new URIHandler() {
  76. private final String responce = "<html><body>Login form handeled."
  77. + "<script type='text/javascript'>top.vaadin.forceSync();"
  78. + "</script></body></html>";
  79. public DownloadStream handleURI(URL context, String relativeUri) {
  80. if (relativeUri != null && relativeUri.contains("loginHandler")) {
  81. if (window != null) {
  82. window.removeURIHandler(this);
  83. }
  84. DownloadStream downloadStream = new DownloadStream(
  85. new ByteArrayInputStream(responce.getBytes()),
  86. "text/html", "loginSuccesfull");
  87. downloadStream.setCacheTime(-1);
  88. return downloadStream;
  89. } else {
  90. return null;
  91. }
  92. }
  93. };
  94. private Window window;
  95. public LoginForm() {
  96. iframe.setType(Embedded.TYPE_BROWSER);
  97. iframe.setSizeFull();
  98. setSizeFull();
  99. setCompositionRoot(iframe);
  100. }
  101. /**
  102. * Returns byte array containing login page html. If you need to override
  103. * the login html, use the default html as basis. Login page sets its target
  104. * with javascript.
  105. *
  106. * @return byte array containing login page html
  107. */
  108. protected byte[] getLoginHTML() {
  109. String defaultThemeName = AbstractApplicationServlet.getDefaultTheme();
  110. String theme = getApplication().getMainWindow().getTheme();
  111. String guessedThemeUri = getApplication().getURL() + "VAADIN/themes/"
  112. + (theme == null ? defaultThemeName : theme) + "/styles.css";
  113. String guessedThemeUri2 = getApplication().getURL()
  114. + "../VAADIN/themes/"
  115. + (theme == null ? defaultThemeName : theme) + "/styles.css";
  116. String appUri = getApplication().getURL().toString();
  117. return ("<!DOCTYPE html PUBLIC \"-//W3C//DTD "
  118. + "XHTML 1.0 Transitional//EN\" "
  119. + "\"http://www.w3.org/TR/xhtml1/"
  120. + "DTD/xhtml1-transitional.dtd\">\n" + "<html>"
  121. + "<head><script type='text/javascript'>"
  122. + "var setTarget = function() {" + "var uri = '"
  123. + appUri
  124. + "loginHandler"
  125. + "'; var f = document.getElementById('loginf');"
  126. + "document.forms[0].action = uri;document.forms[0].username.focus();};"
  127. + "</script>"
  128. + "<link rel='stylesheet' href='"
  129. + guessedThemeUri
  130. + "'/>"
  131. + "<link rel='stylesheet' href='"
  132. + guessedThemeUri2
  133. + "'/>"
  134. + "</head><body onload='setTarget();' style='margin:0;padding:0;'>"
  135. + "<div class='v-app v-app-loginpage'>"
  136. + "<iframe name='logintarget' style='width:0;height:0;"
  137. + "border:0;margin:0;padding:0;'></iframe>"
  138. + "<form id='loginf' target='logintarget'>"
  139. + "<div>Username</div><div >"
  140. + "<input class='v-textfield' style='display:block;' type='text' name='username'></div>"
  141. + "<div>Password</div>"
  142. + "<div><input class='v-textfield' style='display:block;' type='password' name='password'></div>"
  143. + "<div><input class='v-button' type='submit' value='Login'></div></form></div>" + "</body></html>")
  144. .getBytes();
  145. }
  146. @Override
  147. public void attach() {
  148. super.attach();
  149. getApplication().addResource(loginPage);
  150. getWindow().addParameterHandler(paramHandler);
  151. iframe.setSource(loginPage);
  152. }
  153. @Override
  154. public void detach() {
  155. getApplication().removeResource(loginPage);
  156. getWindow().removeParameterHandler(paramHandler);
  157. // store window temporary to properly remove uri handler once
  158. // response is handled. (May happen if login handler removes login
  159. // form
  160. window = getWindow();
  161. if (window.getParent() != null) {
  162. window = (Window) window.getParent();
  163. }
  164. super.detach();
  165. }
  166. /**
  167. * This event is sent when login form is submitted.
  168. */
  169. public class LoginEvent extends Event {
  170. private Map params;
  171. private LoginEvent(Map params) {
  172. super(LoginForm.this);
  173. this.params = params;
  174. }
  175. /**
  176. * Access method to form values by field names.
  177. *
  178. * @param name
  179. * @return value in given field
  180. */
  181. public String getLoginParameter(String name) {
  182. if (params.containsKey(name)) {
  183. return (String) params.get(name);
  184. } else {
  185. return null;
  186. }
  187. }
  188. }
  189. /**
  190. * Login listener is a class capable to listen LoginEvents sent from
  191. * LoginBox
  192. */
  193. public interface LoginListener extends Serializable {
  194. /**
  195. * This method is fired on each login form post.
  196. *
  197. * @param event
  198. */
  199. public void onLogin(LoginForm.LoginEvent event);
  200. }
  201. private static final Method ON_LOGIN_METHOD;
  202. static {
  203. try {
  204. ON_LOGIN_METHOD = LoginListener.class.getDeclaredMethod("onLogin",
  205. new Class[] { LoginEvent.class });
  206. } catch (final java.lang.NoSuchMethodException e) {
  207. // This should never happen
  208. throw new java.lang.RuntimeException(
  209. "Internal error finding methods in LoginForm");
  210. }
  211. }
  212. /**
  213. * Adds LoginListener to handle login logic
  214. *
  215. * @param listener
  216. */
  217. public void addListener(LoginListener listener) {
  218. addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  219. }
  220. /**
  221. * Removes LoginListener
  222. *
  223. * @param listener
  224. */
  225. public void removeListener(LoginListener listener) {
  226. removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  227. }
  228. }