Java Web 項目學習(二) 開發登錄、退出功能


 

 

 登錄

 DAO 層

  • 構建實體 
    數據庫中有表login_ticket。因此構建實體LoginTicket在entity包下。屬性一一對應。get。set。toString方法

    public class LoginTicket {
        private int id;
        private int userId;
        private String ticket;
        private int status;
        private Date expired;
    
        get....
        set....
        toString...
    }
    View Code

     

  • 構建Mapper接口(LoginTicketMapper) 

    @Mapper
    public interface LoginTicketMapper {
    
        int insertLoginTicket(LoginTicket loginTicket);
        LoginTicket selectByTicket(String ticket);
        int updataStatus(String ticket, int status);
        
    }
  • 配置mapper接口
    有兩種實現方式,

    • 通過resources下mapper下的新建對應xml文件配置。(前面一直使用這種方式)

    • 同過直接在mapper接口寫注解的方式實現。 

      @Mapper
      public interface LoginTicketMapper {
      
          @Insert({
                  "insert into login_ticket(user_id, ticket, status, expired) ",
                  "values(#{userId},#{ticket},#{status},#{expired}) "
          })
          @Options(useGeneratedKeys = true,keyProperty = "id")
          int insertLoginTicket(LoginTicket loginTicket);
      
          @Select({
                  "select id, user_id, ticket, status, expired ",
                  "from login_ticket ",
                  "where ticket= #{ticket}"
          })
          LoginTicket selectByTicket(String ticket);
      
          @Update({"update login_ticket set status=#{status} ",
                  "where ticket= #{ticket} "
          })
          int updataStatus(String ticket, int status);
      
      }
      • 使用注解  @Update({})  小括號內大括號
      • 引號內寫SQL語句, 變量同xml一樣采用 #{} 表示。可以拼接,用逗號隔開方便閱讀。最好在末尾加空格方便拼接。
      • 同樣可以加if條件語言。但需要用在"<script>""</script>"內部。 同時需要注意引號需要采用轉義字符
          @Update({
                    "<script>",
                    "update login_ticket set status=#{status} ",
                    "where ticket= #{ticket} ",
                    "<if test=\"ticket!=null\"> ",
                    "and 1=1",
                    "</if> ",
                    "</script> "
            })

         

  • 測試
    因為沒有提示,所以比較容易寫錯。推薦寫完之后做個測試。在繼續往下做。

        @Autowired
        private LoginTicketMapper loginTicketMapper;
    
        @Test
        public void testInsertLoginTicket(){
            LoginTicket loginTicket = new LoginTicket();
            loginTicket.setUserId(0001);
            loginTicket.setStatus(0);
            loginTicket.setTicket("aabc");
            loginTicket.setExpired(new Date(System.currentTimeMillis()+1000 *60 *10));
    
            loginTicketMapper.insertLoginTicket(loginTicket);
        }
    
        @Test
        public void testSelectLoginTicket(){
            LoginTicket ticket =loginTicketMapper.selectByTicket("aabc");
            System.out.println(ticket);
    
            loginTicketMapper.updataStatus("aabc",1);
    
            ticket =loginTicketMapper.selectByTicket("aabc");
            System.out.println(ticket);
    
    
        }
    View Code

     

業務層Service

(與注冊的邏輯相同。判空,判合法性,執行....)同樣也是針對用戶行為,因此寫在UserService中。
這里需要注意! 驗證碼的判斷在Controller進行就可以,不需要放在Service中。可以這么理解,需要與數據庫中存的信息做對比或者往數據庫中存得數據在Service中。其他可以在Controller中處理。

@Autowired
private LoginTicketMapper loginTicketMapper; 

public Map<String,Object> login(String username, String password,  int expiredSeconds){
        Map<String,Object> map = new HashMap<>();
        //空值處理
        if(StringUtils.isBlank(username)){
           map.put("usernameMsg","賬號不能為空!");
           return map;
        }
        if(StringUtils.isBlank(password)){
            map.put("passwordMsg","密碼不能為空!");
            return map;
        }
  
        //合法性處理
        User user = userMapper.selectByName(username);
        if(user == null){
            map.put("usernameMsg","該賬號不存在!");
            return map;
        }
        if(user.getStatus() ==0){
            map.put("usernameMsg","該賬號未激活!");
            return map;
        }
        //驗證密碼
        String salt = user.getSalt();
        String psw  = CommunityUtil.md5(password + salt);
        if(!psw.equals(user.getPassword())){
            map.put("passwordMsg","密碼不正確!");
            return map;
        }

        //生成登錄憑證
        LoginTicket loginTicket = new LoginTicket();
        loginTicket.setUserId(user.getId());
        loginTicket.setTicket(CommunityUtil.generateUUID());
        loginTicket.setStatus(0);
        loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000 ));
        loginTicketMapper.insertLoginTicket(loginTicket);
        //如果登錄成功了,要把憑證放進去,最終要將他發給客戶端。(只需要放loginTicket.ticket字符串就可)
        map.put("ticket",loginTicket.getTicket());
        return map;
    }
View Code

 

Controller

  • LoginController

    • 對於 @RequestMapping(path = "/login", method = RequestMethod.POST) path可以相同,但是path相同的情況下method不能相同
    • 登錄界面參數比較多。需要model用來返回頁面信息,需要傳遞給客戶端cookie通過Response攜帶,驗證碼的信息需要從Session中取得。記住我標簽對勾與否也需區分。因此, public String login(String username, String password, String code, boolean rememberme, Model model, HttpSession session, HttpServletResponse response)
    • 檢查驗證碼時忽略大小寫,同時注意在session中取得的值也不能為空。
    • 設置過期時間常量在CommunityConstant中定義。在LoginController implements CommunityConstant 使用
    •  聲明一個固定的值到變量中。@Value("${}")  代碼中其值在配置文件application.properties中
    • 重定向采用  return "redirect:/login"; 
    • 對於實體參數,SpringMVC會自動把實體參數裝入model中。Java自帶的普通參數類型例如String則不會自動裝入model中。因此有兩種方式獲取到:1通過人為主動的裝入model中。2這些對象是存在於request對象中的。可以從request中取值。(對后面修改html文件,訪問還未終止,因此可以從request中取值)
  •     @Value("${server.servlet.context-path}")
        private String contextPath;
    
        /**
         * 給瀏覽器返回一個html,這個html包含一個圖片的路徑。瀏覽器依據路徑再次訪問服務器獲得圖片。
         */
        @RequestMapping(path = "/login", method = RequestMethod.GET)
        public String getLoginPage(){
            return "site/login";
        }
    
        /**
         * 登錄。
         */
      @RequestMapping(path = "/login", method = RequestMethod.POST)
        public String login(String username, String password, String code, boolean rememberme,
                            Model model, HttpSession session, HttpServletResponse response){
            //檢查驗證碼
            String kaptcha =(String) session.getAttribute("kaptcha");
            if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)){
                model.addAttribute("codeMag","驗證碼不正確");
                return "/site/login";
            }
            //檢查賬號、密碼
            int expriedSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
            Map<String ,Object> map = userService.login(username, password,expriedSeconds);
            if (map.containsKey("ticket")){
                //成功登錄,給客戶端發一個帶ticket的cookie
                Cookie cookie = new Cookie("ticket",map.get("ticket").toString());
                cookie.setPath(contextPath);
                cookie.setMaxAge(expriedSeconds);
                response.addCookie(cookie);
                return "redirect:/index";
            }else{
                //登錄失敗,把錯誤信息帶給登錄界面
                model.addAttribute("usernameMsg",map.get("usernameMsg"));
                model.addAttribute("passwordMsg",map.get("passwordMsg"));
                return "/site/login";
            }
    
        }
  • 修改對應的HTML

    • 提交的方式內容  <form class="mt-5" method="post" th:action="@{/login}">
    • 給每一個賦name,與controller方法中的一致(用戶名,密碼、驗證碼同理)記住我的input也需要name ,rememberme
      <input type="text" class="form-control is-invalid" 
       id="username" name="username" placeholder="請輸入您的賬號!" required>
    • 回到頁面對錯誤的值有一個顯示
    •  th:value="${param.username}"  相當於request.gerParameters(username)。
      <input type="text" class="form-control is-invalid"
             th:value="${param.username}"
             id="username" name="username" placeholder="請輸入您的賬號!" required>

      密碼同理。驗證碼就不需要給默認值了。對於記住我。是通過check來決定的。因此,

      <input type="checkbox" id="remember-me" name="rememberme"
            th:checked="{param.rememberme}">
    • 錯誤提示(修改顯示內容text,與是否顯示——動態class)   賬號、密碼、驗證碼
      <input type="text" th:class="|form-control ${usernameMsg==null?'':'is-invalid'}|"
             th:value="${param.username}"
             id="username" name="username" placeholder="請輸入您的賬號!" required>
      <div class="invalid-feedback" text="${usernameMsg}">  該賬號不存在!  </div>
  • 各種錯誤
    • 出錯基本都是咋HTML頁面。錯誤基本都是忘記加${}符號,忘記加 th:  忘記寫name=“”  再就是拼寫錯誤,這種的。寫的時候還是要仔細一點,找錯太費勁了。

 退出

 將登錄憑證轉為失效狀態。

Seivice

userService

    /**
     * 登出
     */
    public void logout(String ticket){
        loginTicketMapper.updataStatus(ticket,1);
    }

 

Controller

  • 重定向采用  return "redirect:/login"; 
       /**
         * 登出
         */
        @RequestMapping(path = "/logout",method = RequestMethod.GET)
        public String logout(@CookieValue("ticket") String ticket){
            userService.logout(ticket);
            return "redirect:/login"; //重定向默認get方法對應的。
    
        }
  • 修改對應的HTML文件。因為在頭部,所有的都是復用的index的header,去index修改
    <a class="dropdown-item text-center" th:href="@{/logout}">退出登錄</a>

    與LogController中RequestMapping聲明的path=“logout”對應

 

 

關於如何查看是否登錄登出成功

  • 在數據庫中的login_ticket 查看狀態0,1
  • 在客戶端瀏覽器中查看cookie是否存在,可以判斷是否已經鄧璐成功。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM