EE Servlet 3:使用会话和过滤器开发用户登录
我在上一篇文章中介紹了Application類,您可以在其中設置后端服務。 我添加的一個示例服務是UserService 。 該服務將加載包含用戶名和密碼集的Java用戶屬性文件; 稍后將用于對用戶進行身份驗證以登錄到Web應用程序。 現在,我將展示如何使用標準Servlet API以及此后端服務完成登錄部分。
從高層次上講,我們希望將某些Web資源(這意味著Servlet提供的某些URL,例如“ / sysprops”或“ / user”)限制為僅在我們的用戶屬性文件中已知的客戶端用戶。 用戶可以使用匹配的密碼來標識自己。 通常使用用戶登錄表單來完成此操作,對其進行身份驗證,然后將登錄令牌插入Http Session范圍空間。 然后可以使用該登錄令牌來驗證是否允許用戶訪問受限資源。 我們只對單一授權感興趣(未定義任何角色,任何登錄的用戶都可以訪問任何受保護的URL。)
在SysPropsServlet提供的我以前的SysPropsServlet ,您已經看到了一個映射到“ / sysprops” URL的示例,該示例僅生成系統信息HTML表。 這些是敏感信息,因此我們要保護此URL。 我們將需要創建一個實現javax.servlet.Filter接口的類,然后使用此過濾器添加“ / sysprops” URL,以便它可以在實際的Servlet之前對請求進行預處理。 此過濾器使我們可以檢查HTTP請求對象并在需要時中止請求,從而限制了訪問。
package zemian.servlet3example.web;import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import zemian.service.logging.Logger;@WebFilter(urlPatterns={"/sys-props", "/user"}) public class LoginRequiredFilter implements Filter {private static final Logger LOGGER = new Logger(LoginRequiredFilter.class);public static final String LOGIN_REDIRECT = "LOGIN_REDIRECT";@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {if (request instanceof HttpServletRequest) {HttpServletRequest req = (HttpServletRequest) request;LOGGER.trace("Checking LoginSession token for uri=%s", req.getRequestURI());LoginSession loginSession = LoginServlet.getOptionalLoginSession(req);if (loginSession == null) {LOGGER.debug("No LoginSession token found; forwarding request to login page.");// We need to save the old URI so we can auto redirect after login.req.setAttribute(LOGIN_REDIRECT, req.getRequestURI());req.getRequestDispatcher("/login").forward(request, response);return;} else {LOGGER.debug("Request allowed using LoginSession token=%s", loginSession.getId());}}chain.doFilter(request, response);}@Overridepublic void destroy() {}}請注意,您可以配置此過濾器以匹配要保護的多個URL。 您甚至可以使用通配符模式,例如“ / *”,它將保護您應用程序中的每個URL! 過濾器只是在Http Session空間中LoginSession我們稍后將創建的LoginSession對象。 如果找到它,則它使請求通過,否則它將重定向到LoginServlet頁面,該頁面由LoginServlet類提供服務(請注意RETURN語句將提前退出filter方法,而無需調用filter鏈!)。
LoginServlet類是一種表單處理Servlet,它將提示用戶輸入用戶名和密碼。 如果成功,那么我們將LoginSession令牌對象插入到HttpSession空間中,這就是上面的過濾器正在尋找的內容。 這是處理Servlet代碼。
package zemian.servlet3example.web;import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import zemian.service.logging.Logger; import zemian.servlet3example.service.Application; import zemian.servlet3example.service.UserService;@WebServlet("/login") public class LoginServlet extends HtmlWriterServlet {private static final Logger LOGGER = new Logger(LoginServlet.class);@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HtmlWriter html = createHtmlWriter(req, resp);String message;// Check to see if we are doing logout or not.LoginSession loginSession = getOptionalLoginSession(req);if (loginSession != null && req.getParameter("logout") != null) {logout(req);message = "Your have successfully logged out.";} else { message = (String)req.getAttribute("message");if (message == null)message = "";} // Show a login formString redirectUri = (String)req.getAttribute(LoginRequiredFilter.LOGIN_REDIRECT);String redirectHtmlTag = "";if (redirectUri != null) {redirectHtmlTag = "<input type='hidden' name='redirectUri' value='" + redirectUri + "'/>";}html.header().h(1, "Please Login").p(message).println("<form method='post' action='login'>").println(redirectHtmlTag).println("<p/>Username: <input type='text' name='username'/>").println("<p/>Password: <input type='password' name='password'/>").println("<p/><input type='submit' value='Submit'/>").println("</form>").footer();}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {LOGGER.debug("Processing login form.");if (login(req)) {// Login succeed, we should auto redirect user if exists.String redirectUri = req.getParameter("redirectUri");if (redirectUri != null) {LOGGER.debug("Redirect after login to: %s", redirectUri);resp.sendRedirect(redirectUri);return;}}// Show the form again in case login failed or user didn't provide a redirectdoGet(req, resp);} protected LoginSession createLoginSession(HttpServletRequest req, String username) {LoginSession result = new LoginSession(username);req.getSession(true).setAttribute(LoginSession.LOGIN_SESSION_KEY, result);return result;}protected void removeLoginSession(HttpServletRequest req) {HttpSession session = req.getSession(false);if (session != null) {session.removeAttribute(LoginSession.LOGIN_SESSION_KEY);}}private boolean login(HttpServletRequest req) throws IOException {String username = req.getParameter("username");String password = req.getParameter("password");UserService userService = Application.getInstance().getUserService();if (userService.validate(username, password)) {LOGGER.info("User %s logged in successfully.", username);// Create Session Data here after successful authenticated.LoginSession loginsession = getOptionalLoginSession(req);if (loginsession == null) {createLoginSession(req, username);req.setAttribute("message", "You have successfully logged in.");} else {req.setAttribute("message", "You already have logged in."); }} else {LOGGER.info("User %s failed to login.", username);req.setAttribute("message", "Invalid login.");}return true;}/** Return LoginSession if found in HttpSession scope, else return NULL value. */public static LoginSession getOptionalLoginSession(HttpServletRequest req) {LoginSession result = null;HttpSession session = req.getSession(false);if (session != null)result = (LoginSession)session.getAttribute(LoginSession.LOGIN_SESSION_KEY);return result;} }在LoginServlet類中,我們使用UserService服務來驗證用戶名和密碼。 我們顯示帶有GET請求的登錄表單,然后使用POST操作處理登錄。 檢查用戶名和密碼后,我們將創建LoginSession對象。 這只是表示會話令牌的簡單POJO。 您可以保留所需的任何用戶信息。 我不會在這里列出,但是您可以在GitHub上瀏覽它。 請注意,但是您應該使其可序列化,因為HttpSession中存儲的任何數據都可能會被應用程序服務器序列化/反序列化。
還要注意,我也已經將Logout功能實現到LoginServlet類中。 您只需傳遞“注銷”查詢參數,它將被檢測到并從會話中刪除登錄令牌。 這樣做時,請確保使HttpSession本身無效,只是為了保證安全。 我還公開了一個靜態幫助器getOptionalLoginSession , getOptionalLoginSession在少數幾個類之間使用,以檢查用戶是否已登錄。
這幾個類很簡單,但是在如何管理Session數據上展示了Servlet過濾器和Servlet的用法。 這種編程模式允許用戶通過應用程序擁有自己的瀏覽會話和隱私。
如果要在GlassFish服務器中運行我的servlet3-example ,則可以使用here中列出的任何用戶登錄。
翻譯自: https://www.javacodegeeks.com/2015/01/ee-servlet-3-developing-user-login-with-session-and-filter.html
總結
以上是生活随笔為你收集整理的EE Servlet 3:使用会话和过滤器开发用户登录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 奇富科技2023年反诈报告:Z世代遭遇裸
- 下一篇: 初始化懒惰关系以及何时使用它们的5种方法