手把手教你做JavaWeb項目:登錄模塊

作者:叩丁狼教育,鐘昕靈老師。原創(chuàng)文章,轉(zhuǎn)載請注明出處

用戶登錄

在各大信息管理系統(tǒng)中,登錄功能是必不可少的,他的作用就是驗證用戶的身份,判斷用戶是否是本站的會員,只有會員才能訪問當前系統(tǒng)

登錄的實現(xiàn)步驟:

1.用戶填寫賬號和密碼,提交到后臺
2.后臺獲取到賬號和密碼后,將其發(fā)送到數(shù)據(jù)庫中進行查詢
3.查詢結(jié)果如果為null,說明用戶填寫的賬號或者密碼有誤,應該回到登錄頁面并提示用戶重新輸入
4.查詢結(jié)果如果不為null,說明用戶填寫的賬號和密碼正確,將對應的賬戶信息共享到session中(在后面的請求,我們還需要繼續(xù)使用當前登錄的用戶信息),然后跳轉(zhuǎn)到網(wǎng)站的主頁面

按照上面的步驟,我們使用下面的代碼完成了相應功能
1.登錄頁面

<form class="form-horizontal" action="/login" method="post">
    <div class="form-group">
        <label for="inputEmail3" class="col-sm-3 control-label">用戶名</label>
        <div class="col-sm-9">
            <input type="text" name="name" class="form-control" id="inputEmail3">
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-sm-3 control-label">密&nbsp;&nbsp;&nbsp;碼</label>
        <div class="col-sm-9">
            <input type="password" name="password" class="form-control" id="inputPassword3">
        </div>
    </div>
    <div class="form-group">
        <label for="inputPassword3" class="col-sm-3 control-label"></label>
        <div class="col-sm-9">
            <button type="submit" class="btn btn-default">登錄</button>
        </div>
    </div>

頁面中存在很多布局相關(guān)的代碼,大家可以直接略過,重點看表單元素相關(guān)的標簽即可
LoginServlet

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    private IEmployeeService service = new EmployeeServiceImpl();

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String name = req.getParameter("name");
        String password = req.getParameter("password");
        Employee currentUser = service.login(name, password);
        if(currentUser==null){
            //登錄失敗
            req.setAttribute("errorMsg","親,賬戶或者密碼錯誤");
            req.getRequestDispatcher("/login.jsp").forward(req,resp);
            return;
        }else{
            //登錄成功
            req.getSession().setAttribute("USER_IN_SESSION",currentUser);
            resp.sendRedirect("/employee");
            return;
        }

SQL:

SELECT * FROM employee WHERE name = ? AND password = ? 

登錄失敗效果


image.png

登錄成功效果


image.png

用戶注銷

用戶注銷功能的主要作用是保護用戶的賬戶安全,在用戶點擊安全退出的時候,我們需要將本次會話相關(guān)的session信息刪除

刪除的方式有下面兩種:
1.刪除當前登錄的用戶信息
存在問題:本次會話的其他信息還是保存在內(nèi)存中,沒有及時清理

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    private IEmployeeService service = new EmployeeServiceImpl();
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getSession().removeAttribute("USER_IN_SESSION");
        resp.sendRedirect("/login.jsp");
    }
}

2.銷毀整個session(推薦)

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
    private IEmployeeService service = new EmployeeServiceImpl();
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getSession().invalidate();
        resp.sendRedirect("/login.jsp");
    }
}

驗證碼

驗證碼是每個系統(tǒng)必不可少的功能,主要是用來防止系統(tǒng)被惡意攻擊,如果沒有這個功能,那么我們的系統(tǒng)就如同沒有上鎖的家門一樣,小偷隨時能進來干壞事

所以我們來看看,在登錄的時候如何使用驗證碼,其他模塊如果要使用,原理一樣


image.png

1.首先,需要在登錄頁面顯示出驗證碼的圖片,用戶可以根據(jù)圖片中的文字進行填寫
2.同時,在生成驗證碼的時候,我們需要將正確的驗證碼保存到session中,供后面校驗使用
3.用戶填寫好驗證碼后,提交表單,后臺校驗
注:生成驗證碼的代碼不是我們這里的重點,以后如果需要,在網(wǎng)上一搜一籮筐,所以我就不貼出來了

校驗代碼

//校驗驗證碼是否正確
String randomcode_in_session = (String) req.getSession().getAttribute("RANDOMCODE_IN_SESSION");
String randomCode = req.getParameter("randomCode");
if (!StringUtils.isNullOrEmpty(randomCode) && !StringUtils.isNullOrEmpty(randomcode_in_session)) {
    if (!randomCode.equals(randomcode_in_session)) {
        Cookie nameCookie = new Cookie("name", name);
        Cookie passwordCookie = new Cookie("password", password);
        resp.addCookie(nameCookie);
        resp.addCookie(passwordCookie);
        handleError(req, resp, "親,驗證碼錯誤");
        return;
    }
} else {
    handleError(req, resp, "驗證碼不能為空或者驗證碼已失效");
    return;
}

private void handleError(HttpServletRequest req, HttpServletResponse resp, String errorMsg) 
throws ServletException, IOException 
    req.getSession().setAttribute("errorMsg", errorMsg);
    resp.sendRedirect("/login.jsp");
}

當用戶沒填寫驗證碼或者session中的驗證碼失效,都應該給出錯誤提示
如果用戶填寫的和session中保存的驗證碼不匹配,給出錯誤提示

如此,當驗證碼不正確的時候,我們就不會繼續(xù)做登錄校驗,必須等用戶填寫出正確的驗證碼才可以,而這一點,機器是無法做到的

記住賬號

該功能的目的主要是想在用戶一次登錄之后,下次可以不再重新填寫賬號,增加用戶體驗

想要實現(xiàn)該功能,我們需要在后臺將用戶的賬號信息進行共享

但是,我們應該使用什么來完成共享呢?

來,想想我們的需求,我今天早上十點第一次登陸系統(tǒng),使用完之后,關(guān)閉瀏覽器,下午還需要登陸幾次,明天后天...
那么我想在這種需求下都可以不再填寫賬號

在這樣的需求下,我相信大家能夠想到一個答案---cookie
Cookie是將數(shù)據(jù)保存在瀏覽器中,而且,我們可以設(shè)置保存的時間,可以在關(guān)閉瀏覽器之后仍然能夠繼續(xù)使用
所以,Cookie就是我們在這個需求中的最佳解決方案

在登錄的業(yè)務邏輯添加如下代碼,將賬號相關(guān)信息保存到使用Cookie進行保存

        //記住我
        String rememberMe = req.getParameter("rememberMe");
        if (!StringUtils.isNullOrEmpty(rememberMe)) {
            //將用戶信息保存到Cookie中
            Cookie nameCookie = new Cookie("name", name);
            nameCookie.setMaxAge(60 * 60 * 24);

            Cookie rememberMeCookie = new Cookie("rememberMe", rememberMe);
            rememberMeCookie.setMaxAge(60 * 60 * 24);
            resp.addCookie(nameCookie);
            resp.addCookie(rememberMeCookie);
        } else {
            //將用戶信息從Cookie中移除
            Cookie[] cookies = req.getCookies();
            for (Cookie cookie : cookies) {
                if ("name".equals(cookie.getName())  || "rememberMe".equals(cookie.getName())) {
                    cookie.setMaxAge(0);
                    resp.addCookie(cookie);
                }
            }
        }

然后,在登錄頁面獲取到Cookie中的數(shù)據(jù)即可

<div class="form-group">
    <label for="inputEmail3" class="col-sm-3 control-label">用戶名</label>
    <div class="col-sm-9">
        <input type="text" name="name" class="form-control" id="inputEmail3" 
               value="${cookie.name.value}">
    </div>
</div>

<label>
    <input type="checkbox" name="rememberMe" 
        ${empty cookie.rememberMe.value?"":"checked='checked'"}> Remember me
</label>

當選擇了記住我后,登錄錯誤回到登錄頁面,此時能夠自動獲取到上次的賬號信息


image.png

登錄檢查

在用戶沒有登錄的情況下,不允許訪問系統(tǒng)中除登錄以外的其他模塊,如果訪問,應該直接回到登錄頁面
在javaweb中,這個問題的最佳解決方案就是使用過濾器(Filter)

過濾器:能夠在訪問到達目標資源之前對請求做預處理,在響應離開之前對響應做預處理

在我們這個需求中,是需要對請求做預處理的,檢查用戶在請求當前資源之前,是否已經(jīng)登錄

實現(xiàn)步驟:
1.定義過濾器:CheckLoginFilter

public class CheckLoginFilter implements Filter {
    private List<String> needCheckURIs;

    public void init(FilterConfig filterConfig) throws ServletException {
        //獲取到需要校驗的資源名稱(如果需要校驗的資源較多,可以配置不需要校驗的資源)
        String needCheckURI = filterConfig.getInitParameter("needCheckURI");
        String[] split = needCheckURI.split(",");
        //將所有的資源名存放到集合中,待后面進行校驗
        needCheckURIs = Arrays.asList(split);
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) 
                throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        //獲取當前請求的資源名
        String requestURI = req.getRequestURI();
        //如果當前請求的資源是不需要校驗的,直接放行
        if(!needCheckURIs.contains(requestURI)){
            filterChain.doFilter(req, resp);
            return;
        }
        //如果需要校驗,判斷用戶是否登錄,是,則放行,反之回到登錄頁面
        Object currentUser = req.getSession().getAttribute("USER_IN_SESSION");
        if (currentUser == null) {
            resp.sendRedirect("/login.jsp");
            return;
        }
        filterChain.doFilter(req, resp);
    }

    public void destroy() {

    }
}

2.將過濾器交給Tomcat服務器管理

<!--登錄檢查過濾器-->
<filter>
    <filter-name>CheckLoginFilter</filter-name>
    <filter-class>cn.wolfcode.javaweb.web.filter.CheckLoginFilter</filter-class>
    <init-param>
        <param-name>needCheckURI</param-name>
        <param-value>/employee,/department</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CheckLoginFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

有了該過濾器,用戶就不能再沒有登錄的時候,直接訪問相關(guān)資源了,做到了一個基本的安全控制

生成系統(tǒng)賬戶

當系統(tǒng)啟動后,我們需要在用戶表中存在一個最起碼的管理員賬戶,這樣,用戶才能登錄進來來管理整個系統(tǒng)

那么,如何實現(xiàn)在啟動服務器的時候,完成這個需求呢?

其實解決方案很多,大家也都應該能想到
1.Servlet
Servlet默認情況下是在第一次訪問的時候執(zhí)行初始化操作
但是也可以調(diào)整到啟動服務器的時候,<load-on-startup>0</load-on-startup>
初始化Servlet的時候,會執(zhí)行當前Servlet的init方法
所以,我們完全在該方法中來完成這個需求
2.Filter
過濾器的初始化就是在啟動服務器的時候
和Servlet一樣,初始化的時候會執(zhí)行Filter的init方法
所以,也可以在Filter的init方法中完成該需求
3.Listener
前面學習過WEB中的監(jiān)聽器,知道他能夠?qū)ψ饔糜?創(chuàng)建/銷毀)和作用域中的屬性(添加/刪除/修改)進行監(jiān)聽
我們的需求是,在啟動服務器的時候創(chuàng)建默認賬戶
而在啟動服務器的時候,application作用域?qū)ο髸谶@個時候創(chuàng)建
綜上,我們可以創(chuàng)建一個application作用對象監(jiān)聽器,在創(chuàng)建該對象的時候,完成默認賬戶的創(chuàng)建

上面三種方式都能完成我們的需求,但最終從責任分離原則方面考慮,我們應該選擇使用監(jiān)聽器,實現(xiàn)如下
創(chuàng)建監(jiān)聽器

public class SystemManagerCreaterListener implements ServletContextListener {
    private IEmployeeService service = new EmployeeServiceImpl();
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //查詢系統(tǒng)默認賬戶是否存在,如果不存在,創(chuàng)建一個默認賬戶
        Employee manager = service.selectSystemManager();
        if(manager==null){
            manager = new Employee();
            manager.setName("admin");
            manager.setPassword("1");
            manager.setAdmin(true);
            service.save(manager);
        }
    }

    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

注冊監(jiān)聽器

<listener>
    <listener-class>cn.wolfcode.javaweb.web.listener.SystemManagerCreaterListener</listener-class>
</listener>

如此,在啟動服務器的時候,先會到員工表中查詢,是否存在默認的管理員賬戶。

點擊查看JavaWeb系列的其它文章

手把手教你做JavaWeb項目:項目需求分析
手把手教你做JavaWeb項目:前臺界面
手把手教你做JavaWeb項目:多條件過濾

前往叩丁狼官網(wǎng)獲取更多技術(shù)視頻

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容