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是否存在,可以判断是否已经邓璐成功。
posted @ 2021-05-31 19:50  白清欢  阅读(1278)  评论(0编辑  收藏  举报