IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> (仿牛客论坛项目)04 - 登录、退出功能 -> 正文阅读

[大数据](仿牛客论坛项目)04 - 登录、退出功能


1、访问登录页面

  1. 点击头部的登录按钮,打开登录页面,之前在注册功能的最后顺便已经实现了,(仿牛客论坛项目)02 - 开发注册功能

在这里插入图片描述

2、登录

  • 验证账号、密码、验证码
  • 成功时,生成登录凭证,发放给客户端
  • 失败时,跳转回登录页

2.1 新建 LoginTicket 的实体类

  1. 对应 login_ticket 表:

在这里插入图片描述

public class LoginTicket {
    private int id;
    private int userId;
    private String ticket;//给登录的用户一张通行证
    private int status;//0-有效; 1-无效;
    private Date expired;//通行证有效期

    @Override
    public String toString() {
        return "LoginTicket{" +
                "id=" + id +
                ", userId=" + userId +
                ", ticket='" + ticket + '\'' +
                ", status=" + status +
                ", expired=" + expired +
                '}';
    }
}

2.2 新建 LoginTicketMapper 层

  1. 这里使用注解方法实现 sql 语句,帮你拼接字符串;
  2. 好处:少写一个文件;缺点:阅读困难,且手写没有提示,例如要实现你插入数据的功能:@insert({"",""})
  3. 注解的方式实现 sql 语句和 mapper 映射类实现 sql 语句,其中的 sql 语句没有变化;
  4. 如果要实现自增主键功能,并将这个值赋给 id :@Options(useGeneratedKeys = true, keyProperty = "id")
  5. 如果要实现动态 sql 拼接,其实还是和 mapper 映射类中差不多的方式,感觉写起来很麻烦。。。<if test=\"ticket!=null\">
  6. 一般在字符串后面记得加空格,小心字符串拼接出问题。
@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}"
    })
    //根据唯一的ticket查找登录凭证实体类
    LoginTicket selectByTicket(String ticket);

    @Update({
            "<script>",
            "update login_ticket set status=#{status} where ticket=#{ticket} ",
            "<if test=\"ticket!=null\"> ",
            "and 1=1 ",
            "</if>",
            "</script>"
    })
    //当用户退出登录时,或者用户凭证过期时,根据ticket,修改状态
    int updateStatus(String ticket, int status);
}

2.3 编写测试类测试

@Test
public void testInsertLoginTicket() {
    LoginTicket loginTicket = new LoginTicket();
    loginTicket.setUserId(101);
    loginTicket.setTicket("abc");
    loginTicket.setStatus(0);
    loginTicket.setExpired(new Date(System.currentTimeMillis() + 1000 * 60 * 10));

    loginTicketMapper.insertLoginTicket(loginTicket);
}

@Test
public void testSelectLoginTicket() {
    LoginTicket loginTicket = loginTicketMapper.selectByTicket("abc");
    System.out.println(loginTicket);

    loginTicketMapper.updateStatus("abc", 1);
    loginTicket = loginTicketMapper.selectByTicket("abc");
    System.out.println(loginTicket);
}
  • 插入数据,查找数据以及修改数据都没问题;

在这里插入图片描述

2.4 UserService 层

  1. 登录功能,有多种情况,登陆成功、账号不存在、密码错误等等多种情况,所以使用 map 来接收;
  • 空值处理:

    • 账号不能为空
    • 密码不能为空
  • 验证状态:

    • 调用 DAO 层查询用户,账号不存在
    • 账号状态是否未激活
  • 验证密码:

    • 将用户输入的密码使用 MD5 加密算法进行加密得到加密后的密码,将这个密码和从数据库查出来的密码进行比较,如果不一致,代表输入的密码错误
  • 生成登录凭证:

    • 新建一个 LoginTicket 对象,并给它除了 id 值之外的所有数值设值
    • 调用 DAO 层插入登录凭证数据
  • 将生成的 ticket 值放在 map 集合中返回,也就是最终要传给客户端的一个cookie(因为 ticket 这个信息不敏感,一串随机字符而已);

在这里插入图片描述

@Override
public Map<String, Object> login(String username, String password, long 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;
    }

    //验证密码
    password = CommunityUtil.md5(password + user.getSalt());
    if (!password.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);

    //将生成的ticket值放在map集合中返回,也就是最终要传给客户端的一个cookie
    map.put("ticket", loginTicket.getTicket());
    return map;
}

2.5 LoginController 层

  1. 实现登录功能
  • 验证码判断(先判断验证码,如果不正确,就不用和数据库进行交互了):

    • session中存放的 kaptcha 值是否为空;
    • 用户输入的 code 值是否为空;
    • 将 session 存放的验证码和 用户传入的 code 进行比较,忽略大小写的情况下是否一致
    • 上述三种情况不正确的情况下,向 model 中存放错误信息,然后返回登陆页面
  • 记录登录凭证时长:

    • 用户可能勾选记住我,或者不勾选记住我,需要判断这个登录凭证时长,在工具类中定义两种不同的存在时长;
/**
 * 默认状态的登录凭证的超时时间,12h
 */
int DEFAULT_EXPIRED_SECONDS = 3600 * 12;

/**
 * 记住状态的登录凭证超时时间,100天
 */
int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
  • 检查账号密码:
    • 调用 UserService 层来进行登录账户密码验证返回一个map,判断map中的所有数据
    • 如果 map 中包含 ticket 表示没有问题,生成cookie,设置有效路径为整个项目,设置有效时长,并传给客户端,返回到首页页面
    • 如果出问题,有可能是用户名错误,密码错误,放到 model 中返回到登录界面提示用户。
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(String username, String password, String code, boolean rememberme, Model model, HttpSession session,HttpServletResponse response) {
    //先判断验证码,需要从session中取之前我们自己保存的一份
    String kaptcha = (String) session.getAttribute("kaptcha");
    if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
        model.addAttribute("codeMsg", "验证码不正确!");
        return "/site/login";
    }
    int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;
    Map<String, Object> map = userService.login(username, password, expiredSeconds);
    //账号密码是否正确,正确发送cookie,错误反馈错误信息
    if (map.containsKey("ticket")){//代表正确的情况
        Cookie cookie = new Cookie("ticket",(String) map.get("ticket"));
        cookie.setMaxAge(expiredSeconds);
        cookie.setPath(contextPath);
        response.addCookie(cookie);
        return "redirect:/index";
    }else {
        model.addAttribute("usernameMsg", map.get("usernameMsg"));
        model.addAttribute("passwordMsg", map.get("passwordMsg"));
        return "/site/login";
    }
}

2.6 修改 login.html 页面

  1. 将账户、密码、验证码、记住我的勾的name属性进行添加,这个值必须和 Controller 中形参定义的一致,否则对应不上的话,服务器形参获取到的值为空;

  2. 错误信息展现:

    • 用户输入的值还显示在页面上,可以从 request 中取值th:value="${param.username}"
    • 记住我那个勾还和用户原来的选择保持一致:th:checked="${param.rememberme}",这里是boolean值,返回 true 或者 false 也可以用来判断是否勾选;
    • 显示错误信息:th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"

注意:

  • 因为我们在 controller 中将 form 表单请求中传过来的值直接定义在了形参上,以常见数据类型 String 等等不会封装在 model 中;
  • 也就是说 model 中可以自动封装形参中的实体类对象,而不能封装常用数据类型,所以我们要从请求中获取;
<form class="mt-5" th:action="@{/login}" method="post">
   <div class="form-group row">
       <label for="username" class="col-sm-2 col-form-label text-right">账号:</label>
       <div class="col-sm-10">
           <input type="text" th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
                  name="username" th:value="${param.username}"
                  id="username" placeholder="请输入您的账号!" required>
           <div class="invalid-feedback" th:text="${usernameMsg}">
               该账号不存在!
           </div>
       </div>
   </div>
   <div class="form-group row mt-4">
       <label for="password" class="col-sm-2 col-form-label text-right">密码:</label>
       <div class="col-sm-10">
           <input type="password" th:class="|form-control ${passwordMsg!=null?'is-invalid':''}|"
                  name="password" th:value="${param.password}"
                  id="password" placeholder="请输入您的密码!" required>
           <div class="invalid-feedback" th:text="${passwordMsg}">
               密码长度不能小于8位!
           </div>
       </div>
   </div>
   <div class="form-group row mt-4">
       <label for="verifycode" class="col-sm-2 col-form-label text-right">验证码:</label>
       <div class="col-sm-6">
           <input type="text" th:class="|form-control ${codeMsg!=null?'is-invalid':''}|"
                  name="code"
                  id="verifycode" placeholder="请输入验证码!">
           <div class="invalid-feedback" th:text="${codeMsg}">
               验证码不正确!
           </div>
       </div>
       <div class="col-sm-4">
           <img th:src="@{/kaptcha}" id="kaptcha" style="width:100px;height:40px;" class="mr-2"/>
           <a href="javascript:refresh_kaptcha();" class="font-size-12 align-bottom">刷新验证码</a>
       </div>
   </div>
   <div class="form-group row mt-4">
       <div class="col-sm-2"></div>
       <div class="col-sm-10">
           <input type="checkbox" name="rememberme"
                  id="remember-me" th:checked="${param.remenberme}">
           <label class="form-check-label" for="remember-me">记住我</label>
           <a href="forget.html" class="text-danger float-right">忘记密码?</a>
       </div>
   </div>
   <div class="form-group row mt-4">
       <div class="col-sm-2"></div>
       <div class="col-sm-10 text-center">
           <button type="submit" class="btn btn-info text-white form-control">立即登录</button>
       </div>
   </div>
</form>

2.7 测试页面

  1. 首次访问登录页面,什么信息都不显示,而且点击刷新验证码可以改变图片;

在这里插入图片描述

  1. 输入错误的用户名、错误的密码、错误的验证码查看一下是否会提示错误信息;

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 输入正确的查看是否成功(看一下数据库中是否生成了数据凭证)

在这里插入图片描述

在这里插入图片描述

3、退出

  • 将登录凭证修改为失效状态。
  • 跳转至网站首页。

3.1 UserService层:

  1. 实现退出功能,即修改登录凭证的状态码为 1;
@Override
public void logout(String ticket) {
    loginTicketMapper.updateStatus(ticket,1);
}

3.2 LoginController 层

  1. 要根据客户端的 cookie 中获取 ticket 信息
  2. 调用 UserService 层的 logout 方法修改 ticket 对应的状态码信息,并且返回到登陆页面
@RequestMapping(value = "/logout",method = RequestMethod.GET)
public String logout(@CookieValue("ticket")String ticket){
    userService.logout(ticket);
    return "redirect:/login";
}

3.3 修改index.html页面:

  1. 所有页面服用的头部代码中的退出登录超链接:th:href="@{/logout}"

3.4 测试页面

  1. 查看数据库中的状态码 status 是否变为1

在这里插入图片描述

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 22:59:49  更:2022-07-04 23:01:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 16:48:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码