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 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui;
  17. import java.io.ByteArrayInputStream;
  18. import java.io.InputStream;
  19. import java.io.Serializable;
  20. import java.lang.reflect.Method;
  21. import java.nio.charset.StandardCharsets;
  22. import java.util.HashMap;
  23. import java.util.Map;
  24. import com.vaadin.server.StreamResource;
  25. import com.vaadin.shared.ui.loginform.LoginFormConstants;
  26. import com.vaadin.shared.ui.loginform.LoginFormRpc;
  27. import com.vaadin.shared.ui.loginform.LoginFormState;
  28. import com.vaadin.util.ReflectTools;
  29. /**
  30. * Login form with auto-completion and auto-fill for all major browsers. You can
  31. * derive from this class and implement the
  32. * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)}
  33. * method to build the layout using the text fields and login button that are
  34. * passed to that method. The supplied components are specially treated so that
  35. * they work with password managers.
  36. * <p>
  37. * If you need to change the URL as part of the login procedure, call
  38. * {@link #setLoginMode(LoginMode)} with the argument {@link LoginMode#DEFERRED}
  39. * in your implementation of
  40. * {@link #createContent(com.vaadin.ui.TextField, com.vaadin.ui.PasswordField, com.vaadin.ui.Button)
  41. * createContent}.
  42. * <p>
  43. * To customize the fields or to replace them with your own implementations, you
  44. * can override {@link #createUsernameField()}, {@link #createPasswordField()}
  45. * and {@link #createLoginButton()}. These methods are called automatically and
  46. * cannot be called by your code. Captions can be reset by overriding
  47. * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()} and
  48. * {@link #getLoginButtonCaption()}.
  49. * <p>
  50. * Note that the API of LoginForm changed significantly in Vaadin 7.7.
  51. *
  52. * @since 5.3
  53. */
  54. public class LoginForm extends AbstractSingleComponentContainer {
  55. /**
  56. * Event sent when the login form is submitted.
  57. */
  58. public static class LoginEvent extends Component.Event {
  59. private Map<String, String> params;
  60. /**
  61. * Creates a login event using the given source and the given
  62. * parameters.
  63. *
  64. * @param source
  65. * the source of the event
  66. * @param params
  67. */
  68. private LoginEvent(LoginForm source, Map<String, String> params) {
  69. super(source);
  70. this.params = params;
  71. }
  72. @Override
  73. public LoginForm getSource() {
  74. return (LoginForm) super.getSource();
  75. }
  76. /**
  77. * Gets the login parameter with the given name.
  78. *
  79. * @param name
  80. * the name of the parameter
  81. * @return the value of the parameter or null if no such parameter is
  82. * present
  83. */
  84. public String getLoginParameter(String name) {
  85. return params.get(name);
  86. }
  87. }
  88. /**
  89. * Listener triggered when a login occurs in a {@link LoginForm}.
  90. */
  91. public interface LoginListener extends Serializable {
  92. /**
  93. * Event method invoked when the login button is pressed in a login
  94. * form.
  95. *
  96. * @param event
  97. * the login event
  98. */
  99. public void onLogin(LoginEvent event);
  100. }
  101. /**
  102. * Internal stream source for the login URL - always returns "Success" and
  103. * ignores the values received.
  104. */
  105. private static class LoginStreamSource
  106. implements StreamResource.StreamSource {
  107. @Override
  108. public InputStream getStream() {
  109. return new ByteArrayInputStream(
  110. "<html>Success</html>".getBytes(StandardCharsets.UTF_8));
  111. }
  112. }
  113. private static final Method ON_LOGIN_METHOD = ReflectTools
  114. .findMethod(LoginListener.class, "onLogin", LoginEvent.class);
  115. private boolean initialized;
  116. private String usernameCaption = "Username";
  117. private String passwordCaption = "Password";
  118. private String loginButtonCaption = "Login";
  119. /**
  120. * Customize the user name field. Only for overriding, do not call.
  121. *
  122. * @return the user name field
  123. * @since 7.7
  124. */
  125. protected TextField createUsernameField() {
  126. throwIfInitialized();
  127. TextField field = new TextField(getUsernameCaption());
  128. field.focus();
  129. return field;
  130. }
  131. /**
  132. * Gets the caption set with {@link #setUsernameCaption(String)}. Note that
  133. * this method might not match what is shown to the user if
  134. * {@link #createUsernameField()} has been overridden.
  135. *
  136. * @return the user name field caption
  137. */
  138. public String getUsernameCaption() {
  139. return usernameCaption;
  140. }
  141. /**
  142. * Sets the caption of the user name field. Note that the caption can only
  143. * be set with this method before the login form has been initialized
  144. * (attached).
  145. * <p>
  146. * As an alternative to calling this method, the method
  147. * {@link #createUsernameField()} can be overridden.
  148. *
  149. * @param usernameCaption
  150. * the caption to set for the user name field
  151. */
  152. public void setUsernameCaption(String usernameCaption) {
  153. this.usernameCaption = usernameCaption;
  154. }
  155. /**
  156. * Customize the password field. Only for overriding, do not call.
  157. *
  158. * @return the password field
  159. * @since 7.7
  160. */
  161. protected PasswordField createPasswordField() {
  162. throwIfInitialized();
  163. return new PasswordField(getPasswordCaption());
  164. }
  165. /**
  166. * Gets the caption set with {@link #setPasswordCaption(String)}. Note that
  167. * this method might not match what is shown to the user if
  168. * {@link #createPasswordField()} has been overridden.
  169. *
  170. *
  171. * @return the password field caption
  172. */
  173. public String getPasswordCaption() {
  174. return passwordCaption;
  175. }
  176. /**
  177. * Set the caption of the password field. Note that the caption can only be
  178. * set with this method before the login form has been initialized
  179. * (attached).
  180. * <p>
  181. * As an alternative to calling this method, the method
  182. * {@link #createPasswordField()} can be overridden.
  183. *
  184. * @param passwordCaption
  185. * the caption for the password field
  186. */
  187. public void setPasswordCaption(String passwordCaption) {
  188. this.passwordCaption = passwordCaption;
  189. }
  190. /**
  191. * Customize the login button. Only for overriding, do not call.
  192. *
  193. * @return the login button
  194. * @since 7.7
  195. */
  196. protected Button createLoginButton() {
  197. throwIfInitialized();
  198. return new Button(getLoginButtonCaption());
  199. }
  200. /**
  201. * Gets the caption set with {@link #setLoginButtonCaption(String)}. Note
  202. * that this method might not match what is shown to the user if
  203. * {@link #createLoginButton()} has been overridden.
  204. *
  205. * @return the login button caption
  206. */
  207. public String getLoginButtonCaption() {
  208. return loginButtonCaption;
  209. }
  210. /**
  211. * Sets the caption of the login button. Note that the caption can only be
  212. * set with this method before the login form has been initialized
  213. * (attached).
  214. * <p>
  215. * As an alternative to calling this method, the method
  216. * {@link #createLoginButton()} can be overridden.
  217. *
  218. * @param loginButtonCaption
  219. * new caption
  220. */
  221. public void setLoginButtonCaption(String loginButtonCaption) {
  222. this.loginButtonCaption = loginButtonCaption;
  223. }
  224. @Override
  225. protected LoginFormState getState() {
  226. return (LoginFormState) super.getState();
  227. }
  228. @Override
  229. protected LoginFormState getState(boolean markAsDirty) {
  230. return (LoginFormState) super.getState(markAsDirty);
  231. }
  232. @Override
  233. public void attach() {
  234. super.attach();
  235. init();
  236. }
  237. private void throwIfInitialized() {
  238. if (initialized) {
  239. throw new IllegalStateException(
  240. "Already initialized. The create methods may not be called explicitly.");
  241. }
  242. }
  243. /**
  244. * Create the content for the login form with the supplied user name field,
  245. * password field and the login button. You cannot use any other text fields
  246. * or buttons for this purpose. To replace these components with your own
  247. * implementations, override {@link #createUsernameField()},
  248. * {@link #createPasswordField()} and {@link #createLoginButton()}. If you
  249. * only want to change the default captions, override
  250. * {@link #getUsernameFieldCaption()}, {@link #getPasswordFieldCaption()}
  251. * and {@link #getLoginButtonCaption()}. You do not have to use the login
  252. * button in your layout.
  253. *
  254. * @param userNameField
  255. * the user name text field
  256. * @param passwordField
  257. * the password field
  258. * @param loginButton
  259. * the login button
  260. * @return content component
  261. * @since 7.7
  262. */
  263. protected Component createContent(TextField userNameField,
  264. PasswordField passwordField, Button loginButton) {
  265. VerticalLayout layout = new VerticalLayout();
  266. layout.setSpacing(true);
  267. layout.setMargin(true);
  268. layout.addComponent(userNameField);
  269. layout.addComponent(passwordField);
  270. layout.addComponent(loginButton);
  271. return layout;
  272. }
  273. private void init() {
  274. if (initialized) {
  275. return;
  276. }
  277. LoginFormState state = getState();
  278. state.userNameFieldConnector = createUsernameField();
  279. state.passwordFieldConnector = createPasswordField();
  280. state.loginButtonConnector = createLoginButton();
  281. StreamResource resource = new StreamResource(new LoginStreamSource(),
  282. LoginFormConstants.LOGIN_RESOURCE_NAME);
  283. resource.setMIMEType("text/html; charset=utf-8");
  284. resource.setCacheTime(-1);
  285. setResource(LoginFormConstants.LOGIN_RESOURCE_NAME, resource);
  286. registerRpc((LoginFormRpc) this::login);
  287. initialized = true;
  288. setContent(createContent(getUsernameField(), getPasswordField(),
  289. getLoginButton()));
  290. }
  291. private TextField getUsernameField() {
  292. assert initialized;
  293. return (TextField) getState().userNameFieldConnector;
  294. }
  295. private PasswordField getPasswordField() {
  296. assert initialized;
  297. return (PasswordField) getState().passwordFieldConnector;
  298. }
  299. private Button getLoginButton() {
  300. assert initialized;
  301. return (Button) getState().loginButtonConnector;
  302. }
  303. /**
  304. * Handles the login.
  305. * <p>
  306. * In deferred mode, this method is called after the dummy POST request that
  307. * triggers the password manager has been completed. In direct mode (the
  308. * default setting), it is called directly when the user hits the enter key
  309. * or clicks on the login button. In the latter case, you cannot change the
  310. * URL in the method or the password manager will not be triggered.
  311. */
  312. private void login() {
  313. HashMap<String, String> params = new HashMap<>();
  314. params.put("username", getUsernameField().getValue());
  315. params.put("password", getPasswordField().getValue());
  316. LoginEvent event = new LoginEvent(LoginForm.this, params);
  317. fireEvent(event);
  318. }
  319. /**
  320. * Adds a {@link LoginListener}.
  321. * <p>
  322. * The listener is called when the user presses the login button.
  323. *
  324. * @param listener
  325. * the listener to add
  326. */
  327. public void addLoginListener(LoginListener listener) {
  328. addListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  329. }
  330. /**
  331. * Removes a {@link LoginListener}.
  332. *
  333. * @param listener
  334. * the listener to remove
  335. */
  336. public void removeLoginListener(LoginListener listener) {
  337. removeListener(LoginEvent.class, listener, ON_LOGIN_METHOD);
  338. }
  339. }