可以使用Spring Security來實現這種功能,但我認為將基于請求的身份驗證框架與基于組件的Web框架結合使用并不是最好的主意。 這兩個世界不能很好地融合在一起,所以我更喜歡使用我自己的烘焙解決方案,我將在下面介紹。
基礎項目
我們從一個簡單的Web應用程序開始,該應用程序使用最新的仍很熱門的Apache Wicket 6編寫。 您可以從GitHub下載完整的源代碼,并使用mvn clean compile jetty:run啟動應用程序。
基本應用程序包含兩個頁面:
- 主頁:顯示已登錄和未登錄用戶的歡迎消息,或者注銷或登錄鏈接。
- 登錄頁面:允許用戶基于簡單的用戶內存集合進行登錄。 一些有效的登錄名/密碼對:John / john,Lisa / lisa,Tom / tom。



記住我的功能
實現“記住我”功能的標準方法如下:
- 詢問用戶是否希望他將來被記住并自動登錄。
- 如果是這樣,請在他的計算機上使用登錄名和密碼保存cookie。
- 對于每個訪問我們網站的新用戶,請檢查是否存在步驟2中的cookie,如果存在,則為自動登錄用戶。
- 當他手動注銷時,請刪除cookie,以便可以清除用于自動登錄的數據。
第二點需要一些解釋。 在此示例應用程序中,我們將保存登錄信息,而不是哈希值,即 cookie中未加密的密碼。 在實際情況下,這是不可接受的。 取而代之的是,您應該考慮存儲散列和加鹽的密碼,這樣,即使有人攔截了用戶Cookie,密碼仍然是秘密的,需要更多的工作來對其進行解碼。
更新: Micha? Mat?oka發布了兩個非常有趣的鏈接,這些鏈接如何在實際系??統中完成。 這些方法甚至不使用密碼或密碼哈希。 有關更多詳細信息,請查看此帖子下方的他的評論。
第1步:作為用戶,我想決定是否要使用“記住我”功能
鏈接以提交此步驟
為了允許用戶通知應用程序他想使用“記住我”功能,我們只需在登錄頁面添加一個復選框即可。 因此,我們需要稍微修改LoginPage Java和html文件(突出顯示新內容):
<form wicket:id='form' class='form-horizontal'><fieldset><legend>Please login</legend></fieldset><div class='control-group'><div wicket:id='feedback'></div></div><div class='control-group'><label class='control-label' for='login'>Login</label><div class='controls'><input type='text' id='login' wicket:id='login' /></div></div><div class='control-group'><label class='control-label' for='password'>Password</label><div class='controls'><input type='password' id='password' wicket:id='password' /></div></div><div class='control-group'><div class='controls'><label class='checkbox'><input type='checkbox' wicket:id='rememberMe'> Remember me on this computer</label></div></div><div class='form-actions'><input type='submit' wicket:id='submit' value='Login' title='Login' class='btn btn-primary'/></div></form>
private String login;private String password;private boolean rememberMe;public LoginPage() {Form<Void> loginForm = new Form<Void>('form');add(loginForm);loginForm.add(new FeedbackPanel('feedback'));loginForm.add(new RequiredTextField<String>('login', new PropertyModel<String>(this, 'login')));loginForm.add(new PasswordTextField('password', new PropertyModel<String>(this, 'password')));loginForm.add(new CheckBox('rememberMe', new PropertyModel<Boolean>(this, 'rememberMe')));Button submit = new Button('submit') {// (...)};loginForm.add(submit);}
現在我們準備好下一步。
步驟2:作為系統,我想將登錄名和密碼保存在Cookie中
鏈接以提交此步驟
首先,我們需要一個CookieService,它將封裝負責處理cookie的所有邏輯:在需要時保存,列出和清除cookie。 代碼非常簡單,我們使用WebResponse和WebRequest類來修改用戶瀏覽器中的cookie。
public class CookieService {public Cookie loadCookie(Request request, String cookieName) {List<Cookie> cookies = ((WebRequest) request).getCookies();if (cookies == null) {return null;}for (Cookie cookie : cookies) {if(cookie.getName().equals(cookieName)) {return cookie;}}return null;}public void saveCookie(Response response, String cookieName, String cookieValue, int expiryTimeInDays) {Cookie cookie = new Cookie(cookieName, cookieValue);cookie.setMaxAge((int) TimeUnit.DAYS.toSeconds(expiryTimeInDays));((WebResponse)response).addCookie(cookie);}public void removeCookieIfPresent(Request request, Response response, String cookieName) {Cookie cookie = loadCookie(request, cookieName);if(cookie != null) {((WebResponse)response).clearCookie(cookie);}}
}
然后,當用戶在LoginPage上選中“記住我”時,我們必須在其瀏覽器中保存cookie:
Button submit = new Button('submit') {@Overridepublic void onSubmit() {UserService userService = WicketApplication.get().getUserService();User user = userService.findByLoginAndPassword(login, password);if(user == null) {error('Invalid login and/or password. Please try again.');}else {UserSession.get().setUser(user);if(rememberMe) {CookieService cookieService = WicketApplication.get().getCookieService();cookieService.saveCookie(getResponse(), REMEMBER_ME_LOGIN_COOKIE, user.getLogin(), REMEMBER_ME_DURATION_IN_DAYS);cookieService.saveCookie(getResponse(), REMEMBER_ME_PASSWORD_COOKIE, user.getPassword(), REMEMBER_ME_DURATION_IN_DAYS);}setResponsePage(HomePage.class);}}};
第3步:作為用戶,我想在返回Web應用程序時自動登錄
鏈接以提交此步驟
為了檢查用戶進入我們的應用程序是否是“使用戶自動登錄”,我們必須豐富負責創建新用戶會話的邏輯。 當前,它是在WicketApplication類中完成的,該類在被請求時創建新的WebSession實例。 因此,每次創建新會話時,我們都必須檢查cookie是否存在,以及它們是否為有效的用戶名/密碼對,請自動登錄該用戶。
因此,讓我們開始將與會話相關的邏輯提取到名為SessionProvider的單獨的類中。 它將需要UserService和CookieService來檢查現有用戶和cookie,因此我們將它們作為構造函數中的引用傳遞。
public class WicketApplication extends WebApplication {private UserService userService = new UserService();private CookieService cookieService = new CookieService();private SessionProvider sessionProvider = new SessionProvider(userService, cookieService);@Overridepublic Session newSession(Request request, Response response) {return sessionProvider.createNewSession(request);}
}
SessionProvider的作用是創建新的UserSession,檢查是否存在正確的cookie,如果存在,則設置登錄用戶。 此外,我們添加了反饋消息,以通知用戶他已被自動記錄。 因此,讓我們看一下代碼:
public class SessionProvider {public SessionProvider(UserService userService, CookieService cookieService) {this.userService = userService;this.cookieService = cookieService;}public WebSession createNewSession(Request request) {UserSession session = new UserSession(request);Cookie loginCookie = cookieService.loadCookie(request, REMEMBER_ME_LOGIN_COOKIE);Cookie passwordCookie = cookieService.loadCookie(request, REMEMBER_ME_PASSWORD_COOKIE);if(loginCookie != null && passwordCookie != null) {User user = userService.findByLoginAndPassword(loginCookie.getValue(), passwordCookie.getValue());if(user != null) {session.setUser(user);session.info('You were automatically logged in.');}}return session;}
}
為了在HomePage.java上顯示反饋消息,我們必須在該處添加FeedbackPanel,但是為了簡潔起見,我將在本文中省略它。 您可以閱讀commit來檢查如何做。
因此,經過三步,我們應該使“記住我”成為可能。 要快速檢查它,請通過添加以下內容來修改web.xml文件中的會話超時:
<session-config><session-timeout>1</session-timeout></session-config>
然后啟動應用程序mvn clean compile jetty:run ,進入登錄頁面,登錄,關閉瀏覽器,并在1分鐘后(會話終止時)在http:// localhost:8080上再次打開它。 您應該會看到以下內容:

這樣就可以了。 但是我們還需要做一件事:允許用戶刪除Cookie并關閉自動登錄。
第4步:作為用戶,我希望能夠注銷并清除我的Cookie
鏈接以提交此步驟 在最后一步,我們必須允許用戶清除其數據并禁用其帳戶的“記住我”。 這將通過在用戶明確單擊“注銷”鏈接時清除兩個cookie來實現。
Link<Void> logoutLink = new Link<Void>('logout') {@Overridepublic void onClick() {CookieService cookieService = WicketApplication.get().getCookieService();cookieService.removeCookieIfPresent(getRequest(), getResponse(), SessionProvider.REMEMBER_ME_LOGIN_COOKIE);cookieService.removeCookieIfPresent(getRequest(), getResponse(), SessionProvider.REMEMBER_ME_PASSWORD_COOKIE);UserSession.get().setUser(null);UserSession.get().invalidate();}};logoutLink.setVisible(UserSession.get().userLoggedIn());add(logoutLink);
摘要
就是這樣。 在此端口中,我們已經在使用Apache Wicket編寫的Web應用程序中實現了簡單的“記住我”功能,而無需使用任何外部身份驗證庫。
祝您編程愉快,別忘了分享!
參考:來自Code Hard Go Pro博客的JCG合作伙伴 Tomasz Dziurko的Apache Wicket中的“記住我”功能 。
翻譯自: https://www.javacodegeeks.com/2012/09/apache-wicket-remember-me-functionality.html