2.23 开发登录、退出功能

在这里插入图片描述
1.登录
1.1 验证账号,密码,验证码

生成登录凭证 是 一个key

先来查看一下 “登录凭证”是什么样子的

//查询的依据是ticket,因为ticket是凭证,是核心数据,最终是把ticket字符串发送给浏览器报存。LoginTicket是返回值
在这里插入图片描述
查看一下备注,expied 表示什么时候过期
在这里插入图片描述
先写一个实体类LoginTicket以封装表中的数据,
在这里插入图片描述
实体类就是这样

然后再写增删改查相关逻辑,在DAO下新建接口LoginTicketMapper

之前实现mapper,都是在mapper文件夹下实现配置文件
在这里插入图片描述
现在换一种方式,通过在mapper类LoginTicketMapper中写注解,通过注解声明该方法是什么SQL。代码如下:

package com.nowcoder.community.dao;

import com.nowcoder.community.entity.LoginTicket;
import org.apache.ibatis.annotations.*;

@Mapper
public interface LoginTicketMapper {

    @Insert({//insertLoginTicket方法是Insert语句,所以用Insert语句。把诸如user_id,ticket,status,expired等多个字符串拼成一个sql
            "insert into login_ticket(user_id,ticket,status,expired) ",//login_ticket千万不能写错。user_id,ticket每个逗号后面都要有一个空格。login_ticket是mysql的一个属性,详见博客
            "values(#{userId},#{ticket},#{status},#{expired})"//values是自动生成的
    })
    @Options(useGeneratedKeys = true, keyProperty = "id")//是否自动生成组件useGeneratedKeys = true,把生成的值注入给id属性
    int insertLoginTicket(LoginTicket loginTicket);//插入数据方法。  参数就是之前定义的entity中 loginTicket实体类

    @Select({//selectByTicket是查询,所以用Select注解
            "select id,user_id,ticket,status,expired ",//expired "和"之间要空格
            "from login_ticket where ticket=#{ticket}"//where ticket=#{ticket}"是指查询条件
    })
    LoginTicket selectByTicket(String ticket);//查询的依据是ticket,因为ticket是凭证,是核心数据,最终是把ticket字符串发送给浏览器报存。LoginTicket是返回值

    @Update({//updateStatus方法用Update注解
            "<script>",//script表示脚本,意思是这里面是html脚本。下面的语法和html一样
            "update login_ticket set status=#{status} where ticket=#{ticket} ",
            "<if test=\"ticket!=null\"> ",//if开始  \"表示转义后的双引号
            "and 1=1 ",
            "</if>",//if闭合
            "</script>"//script闭合
    })
    int updateStatus(String ticket, int status);//修改凭证方法,返回int。退出的时候凭证失效,互联网行业很少删除某项东西,只是改变其状态。把ticket传进来,然后要告诉我要变为什么status

}

接下来在MapperTests测试类中
将写好的loginTicketMapper注入

@Autowired
private LoginTicketMapper loginTicketMapper;

并进行测试:

@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));//setExpired设置过期时间。1000 * 60 * 10单位是毫秒。10min以后到期

    loginTicketMapper.insertLoginTicket(loginTicket);//将设置好的loginTicket传入进去
}

运行结果:
在这里插入图片描述
在这里插入图片描述
/根据Ticket 来查询数据,查完以后直接修改

@Test
public void testSelectLoginTicket() {//根据Ticket 来查询数据,查完以后直接修改
    LoginTicket loginTicket = loginTicketMapper.selectByTicket("abc");//根据Ticket为abc来查询数据,得到loginTicket这个对象。
    System.out.println(loginTicket);//打印查看有没有问题

    loginTicketMapper.updateStatus("abc", 1);//根据Ticket 把状态改为1
    loginTicket = loginTicketMapper.selectByTicket("abc");//接着检查一遍,看看数据对不对
    System.out.println(loginTicket);
}

运行结果:
在这里插入图片描述
在这里插入图片描述
下面开发业务层service:

@Autowired
private LoginTicketMapper loginTicketMapper;

下面开始实现登录功能,即追加一个方法:

登录方法

public Map<String, Object> login(String username, String password, int expiredSeconds) {//expiredSeconds过期时间/s

Map<String, Object> map = new HashMap<>();

// 空值处理
if (StringUtils.isBlank(username)) {//调用工具 判断username 是否为空
    map.put("usernameMsg", "账号不能为空!");//返回 账号不能为空 这个消息
    return map;
}
if (StringUtils.isBlank(password)) {
    map.put("passwordMsg", "密码不能为空!");
    return map;
}

// 验证账号合法性
User user = userMapper.selectByName(username);//检查库里有没有username
if (user == null) {
    map.put("usernameMsg", "该账号不存在!");
    return map;
}

// 验证状态
if (user.getStatus() == 0) {//账号注册但没有激活
    map.put("usernameMsg", "该账号未激活!");
    return map;
}

// 验证密码
password = CommunityUtil.md5(password + user.getSalt());//把传入的“明文密码+盐值” 按照相同的规则md5 进行加密,得到加密后的password
if (!user.getPassword().equals(password)) {//判断一下此加密后的密码 和我们 库里的是否一样
    map.put("passwordMsg", "密码不正确!");
    return map;
}

// 生成登录凭证
LoginTicket loginTicket = new LoginTicket();//创建LoginTicket 这个实体
loginTicket.setUserId(user.getId());
loginTicket.setTicket(CommunityUtil.generateUUID());//凭证 是一个随机生成的字符串
loginTicket.setStatus(0);//设置有效状态0
loginTicket.setExpired(new Date(System.currentTimeMillis() + expiredSeconds * 1000));//设置过期时间 currentTimeMillis当前时间往后推移  expiredSeconds * 1000秒
loginTicketMapper.insertLoginTicket(loginTicket);

map.put("ticket", loginTicket.getTicket());//因为最终要把凭证发给客户端。可以把loginTicket放进去,也可以只发送Ticket
return map;

}

在这里插入图片描述

接下来编写表现层的逻辑

得在controller里面写 方法处理页面的请求
因为登录页面 要传入三个值
在这里插入图片描述
在LoginController里写 能够处理登录请求的方法:
代码如下:

  @RequestMapping(path = "/login", method = RequestMethod.POST)//声明方法访问路径,之前也有访问路径为login,但是两者的请求方式不能一样。比如请求方式都是POST,这样会报错
    public String login(String username, String password, String code, boolean rememberme,//方法返回String,方法名login。参数rememberme表示。code表示输入的验证码
                        Model model, HttpSession session, HttpServletResponse response) {//因为创建cookie所以需要HttpServletResponse
        // 检查验证码
        String kaptcha = (String) session.getAttribute("kaptcha");//从session中取出验证码
        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {//equalsIgnoreCase方法 与equals相比,其忽略大小写的影响。code为空 或者 kaptcha 为空 或者 两者不等 都不对
            model.addAttribute("codeMsg", "验证码不正确!");
            return "/site/login";//返回登录页面
        }

        // 检查账号,密码
        int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;//如果 选中记住我  则
        Map<String, Object> map = userService.login(username, password, expiredSeconds);
        if (map.containsKey("ticket")) {//如果map包含了ticket,则说明登录成功了。因为成功了才会存在ticket
            Cookie cookie = new Cookie("ticket", map.get("ticket").toString());//把带有ticket的cookie 发送给客户端。map.get("ticket")是对象,.toString()将其变为字符串。
            cookie.setPath(contextPath);//设置有效路径
            cookie.setMaxAge(expiredSeconds);//设置cookie 有效时间
            response.addCookie(cookie);//响应时发送给浏览器
            return "redirect:/index";//重新回到首页
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));//用户名错误
            model.addAttribute("passwordMsg", map.get("passwordMsg"));//密码错误
            return "/site/login";//回到登录页面
        }
    }

其中参数 boolean rememberme 表示:
在这里插入图片描述
其中 在“检查账号,密码”部分,

REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS 两个常量
在下面这个接口
在这里插入图片描述
定义:
在这里插入图片描述
12小时
100天

同样 在“检查账号,密码”部分,
cookie.setPath(contextPath);//设置有效路径

其中contextPath已经定义过
在这里插入图片描述
且在之前注入到contextPath

@Value("${server.servlet.context-path}")
private String contextPath;

接下来如何配置这个页面显示

在这里插入图片描述
需要在site文件夹下的 login.html下进行编辑

初始页面如下:
在这里插入图片描述
故意输错,首先提示验证码错误
在这里插入图片描述
验证码正确后,提示:
在这里插入图片描述
账号正确后,提示:
在这里插入图片描述
密码正确后,登录成功
在这里插入图片描述
目前还没有做后续处理,登录成功以后,这些地方应该都要隐去的。
在这里插入图片描述

最后处理退出逻辑

在这里插入图片描述
点这个退出登录
在这里插入图片描述

修改失效状态:

在业务层(Service)UserService中添加:

public void logout(String ticket) {
    loginTicketMapper.updateStatus(ticket, 1);//修改凭证为失效
}

然后在Controller里面LoginController,可以处理页面的请求

ticket是在cookie下的
在这里插入图片描述
本次课程利用了之前讲过的 验证码以及 cookie

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值