博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
仿牛客社区项目2.5登录模块———登录退出功能
阅读量:2055 次
发布时间:2019-04-28

本文共 8565 字,大约阅读时间需要 28 分钟。

在这里插入图片描述

user登录的属性

在这里插入图片描述
id是主键
ticket凭证
status登录状态0有效1无效
expired过期时间
ticket由服务器发给浏览器,作为cookie保存在浏览器,其他信息在服务端保存。下次服务端收到了cookie识别出是来自哪个浏览器,并可取出其他信息。

写程序的顺序:数据访问层->业务->表现层

一、登录功能

1、实体类entity:LoginTicket.class

5个属性id、userId、ticket、status、expired,getset方法,重写toString

public class LoginTicket {
private int id; private int userId; private String ticket;//凭证字符串 private int status;//状态0有效1无效 private Date expired;//到期日期 public int getId() {
return id; } public void setId(int id) {
this.id = id; } public int getUserId() {
return userId; } public void setUserId(int userId) {
this.userId = userId; } public String getTicket() {
return ticket; } public void setTicket(String ticket) {
this.ticket = ticket; } public int getStatus() {
return status; } public void setStatus(int status) {
this.status = status; } public Date getExpired() {
return expired; } public void setExpired(Date expired) {
this.expired = expired; } @Override public String toString() {
return "LoginTicket{" + "id=" + id + ", userId=" + userId + ", ticket='" + ticket + '\'' + ", status=" + status + ", expired=" + expired + '}'; }}

2、数据访问层接口LoginTicketMapper.java

三个方法:插入、查询、退出

@Mapperpublic interface LoginTicketMapper {
//实现sql语句:可以在xml里写,也可以通过注解,字符串拼接成,书写方便,每行字符串加一个空格,主键自动生成 @Insert({
"insert into login_ticket(user_id,ticket,status,expired) ", "values(#{userId},#{ticket},#{status},#{expired})" }) @Options(useGeneratedKeys = true, keyProperty = "id")//希望主键id是自动生成的,生成的值注入给对象,指定属性id int insertLoginTicket(LoginTicket loginTicket);//插入输出,影响行数 @Select({
"select id,user_id,ticket,status,expired ", "from login_ticket where ticket=#{ticket}" }) LoginTicket selectByTicket(String ticket);//查询,依据ticket,ticket是凭证,发送给cookie浏览器存,其他的在服务器存,下次cookie给服务器,服务器查到其他的数据,ticket唯一标识 //退出,状态改变 @Update({
"" })//动态sql,if怎么用?

测试sql是否写正确了,在MapperTests写测试,先@Autowired注入loginTicketMapper:

@Test    public void testInsertLogin() {
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); }

控制台打印:结果正确

3、业务层UserService.java

注入mapper

输入:用户名、密码、多久后失效
输出:Map<String, Object>,用Map装多个情况,登录失败的原因(账号没输入、没存在、没激活),登录成功的凭证
处理逻辑:

  • 空值处理,username是否为空,返回“usernameMsg”消息,同理密码。
  • 验证账号,username看库里是否有,如果有库里账号是否一致,是否被激活,密码是否一致(加密后(加盐)看与库里存的是否一致)。
  • 登录成功,生成登录凭证,创建loginTicket实体,存入信息,往数据库存mapper.insert(loginTicket)。凭证是随机生成字符串CommunityUtil.generateUUID()
  • 把凭证发给客户端,放loginTicket对象和tiket凭证都可以
    map.put(“ticket”, loginTicket.getTicket());

补充在UserService.java的登录代码:

public Map
login(String username, String password, int expiredSeconds) {
Map
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 (!user.getPassword().equals(password)) {
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); map.put("ticket", loginTicket.getTicket()); return map; }

4、表现层LoginController.java

收到表单(POST)给UserService处理,登录成功回首页、登录失败回登录页面.

补充在LoginController.java

  • 访问路径相同时,method的方法不同也能区分开,一个是GET,一个是POST.

    @RequestMapping(path = "/login", method = RequestMethod.POST)
  • 表单传入条件:账号username 密码password 验证码code “记住我”rememberme;返回数据、响应时需要model,页面传的验证码code要和服务器生成的的验证码(在session里)比较,要从session中取出来需要HttpSession session,登录成功要把ticket发给客户端让它保存,用cookie保存需要HttpServletResponse response

    public String login(String username, String password, String code, boolean rememberme,                        Model model, HttpSession session, HttpServletResponse response) {
  • 先检查验证码(从session里get),为空或不匹配,返回登录页面

    // 检查验证码        String kaptcha = (String) session.getAttribute("kaptcha");        if (StringUtils.isBlank(kaptcha) || StringUtils.isBlank(code) || !kaptcha.equalsIgnoreCase(code)) {
    model.addAttribute("codeMsg", "验证码不正确!"); return "/site/login"; }
  • 调用userService的login方法,返回map

    int expiredSeconds = rememberme ? REMEMBER_EXPIRED_SECONDS : DEFAULT_EXPIRED_SECONDS;		Map
    map = userService.login(username, password, expiredSeconds);
  • 含有ticket凭证,给ticket创建cookie,设置路径,最长登录时间,加到responsed对象里,响应时发送给浏览器,登录成功返回首页。

    if (map.containsKey("ticket")) {
    Cookie cookie = new Cookie("ticket", map.get("ticket").toString()); cookie.setPath(contextPath); cookie.setMaxAge(expiredSeconds); response.addCookie(cookie); return "redirect:/index";
  • 不含有ticket凭证,返回账号、密码的错误信息,从map里面get。map。get返回null也不会有影响,不是usernameMsg有问题,就是passwordMsg有问题。

    } else {
    model.addAttribute("usernameMsg", map.get("usernameMsg")); model.addAttribute("passwordMsg", map.get("passwordMsg")); return "/site/login"; }

一些细节处理:

  • 是否点击“记住我”:在util包下补充CommunityConstant.java ,记录常量:登录凭证的超时时间(默认12小时,“记住我”3个月),该类实现CommunityConstant接口可以利用里面的常量public class LoginController implements CommunityConstant
    /**     * 默认状态的登录凭证的超时时间     */    int DEFAULT_EXPIRED_SECONDS = 3600 * 12;    /**     * 记住状态的登录凭证超时时间     */    int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
  • cookie要设置路径,在resources的application.properties配置文件中,有server.servlet.context-path=/community,即所有路径都在/community下,可以通过注解注入或repueat对象获得,这里用注解注入方式@Value(),将变量server.servlet.context-path给到属性contextPath.
    @Value("${server.servlet.context-path}")        private String contextPath;

5、设置登录页面login.html

每一个框上加name="username",name="password",name="code"和LoginController中login的方法的参数要一致。

如果出错,服务器给浏览器的默认值还是上一次请求中的值:th:value="${param,username}"th:value="${param.password}"th:checked="${param.rememberme}"
规则:如果参数是实体,这里是user,Spring MVC会把user放到model里,页面上可以直接得到user对象的数据(这是从model里面获得的);如果是普通参数,如基本类型,字符串,不会把参数放到model里,可以认为放到model里,但是是存在request对象里的,可以通过request.getParameter获得,因为是请求里携带过来的,当程序执行到html文件时,可以从request里面取值。
thymleaf里语法是th:value="${param,username}"th:value="${param.password}"
每次验证码要刷新,所以不用给它默认值
“记住我”出错,√应该还在,是通过check设置的,动态判断th:checked="${param.rememberme}",从request取参数。

其他错误提示:账号相关提示,动态

该账号不存在!
密码长度不能小于8位!
验证码不正确!

是否显示该样式’is-invalid’:如果确实有问题Msg才显示这个样式’is-invalid’,否则为空。class不应该写死,是固定样式+动态样式。

二、写退出功能

1、数据层接口写过了LoginTicketMapper.java

写过了,这里再看一下

//退出,状态改变    @Update({
"

2、业务层UserService.java

public void logout(String ticket) {
loginTicketMapper.updateStatus(ticket, 1); }

3、表现层LoginController.java

  • 通过注解@CookieValue()从浏览器的cookie接收,key为ticket,String ticket为用该参数接收key为ticket的cookie。
  • 调用业务层的logout方法,登出
  • 返回首页(重定向,getpost默认get请求)
@RequestMapping(path = "/logout", method = RequestMethod.GET)    public String logout(@CookieValue("ticket") String ticket) {
userService.logout(ticket); return "redirect:/login"; }

修改index.html中退出登录按键,改退出路径th:href=:@{/logout}

三、测试

先检查验证码是否正确,验证码正确后才检查账号,密码。

在这里插入图片描述

在浏览器登录后,开检查选项,在Application下选择cookies选项,看到对应的ticket

在这里插入图片描述
在这里插入图片描述
status是是否登录状态,为1的是退出后的(不会删除数据),0是当前登录的。

注:html中,@{}路径 ${}变量 #{}取id

转载地址:http://ysnlf.baihongyu.com/

你可能感兴趣的文章
Eclipse使用(十一)—— 使用Eclipse创建简单的Maven JavaWeb项目
查看>>
Intellij IDEA使用(十三)—— 在Intellij IDEA中配置Maven
查看>>
面试题 —— 关于main方法的十个面试题
查看>>
集成测试(一)—— 使用PHP页面请求Spring项目的Java接口数据
查看>>
使用Maven构建的简单的单模块SSM项目
查看>>
Intellij IDEA使用(十四)—— 在IDEA中创建包(package)的问题
查看>>
Redis学习笔记(四)—— redis的常用命令和五大数据类型的简单使用
查看>>
深入分析JavaWeb技术内幕(一)—— 深入Web请求过程
查看>>
深入分析JavaWeb技术内幕(二)—— 深入分析Java I/O的工作机制
查看>>
使用Java将PDF解析成HTML页面进行展示并从页面中提取Json数据设置到Table中
查看>>
Redis学习笔记(五)—— 在Linux下搭建Redis集群
查看>>
Redis学习笔记(六)—— 解决安装ruby出现的问题:redis requires Ruby version &amp;gt;= 2.2.2.
查看>>
从原理上搞懂编码——究竟什么是编码?什么是解码?什么是字节流?
查看>>
前端上传文件组件Plupload使用指南
查看>>
单点登录原理与简单实现
查看>>
使用zxing生成彩色或带图片的二维码
查看>>
在Linux下安装JDK8
查看>>
面试题 —— HTTP请求中get请求和post请求的区别以及底层原理
查看>>
面试题 —— HashMap、HashTable、HashSet的实现原理和底层数据结构
查看>>
C语言学习笔记(三)—— 数据和C
查看>>