Spring boot 搭建个人博客系统(二)——登录注册功能

一直想用Spring boot 搭建一个属于自己的博客系统,刚好前段时间学习了叶神的牛客项目课受益匪浅,乘热打铁也主要是学习,好让自己熟悉这类项目开发的基本流程。系统采用Spring boot+MyBatis+MySQL的框架进行项目开发。

项目源码:Jblog
个人主页:tuzhenyu’s page
原文地址:Spring boot 搭建个人博客系统(二)——登录注册功能

0. 思路

  用户登录注册功能主要实现用户的添加,验证和记住密码一段时间内的免密码登录。用户的注册是往数据库中插入用户的用户名和密码等信息,用户的验证是从数据库中取出用户的用户名和密码等信息进行比对。明文密码存储有很大的风险,采用在密码后加salt再经过MD5加密的形式存储,这样一方面避免了用户密码信息泄露的风险,同时也防止了暴力破解密码的可能。
  登录成功后生成一串字符作为ticket存储在数据库和cookie中,用于下次登录的免密码登录验证。一段时间后数据库中的ticket失效,用户需要重新登录。

1. 数据模型

  系统登录注册功能需要验证用户信息,需要操纵数据库的user表和login_ticket表,使用MyBatis作为系统的ORM框架用来简化数据操作。
  

1.1 引入MyBatis ORM框架

(1) 在pom.xml中添加MyBatis依赖jar包和MySQL连接相关的jar包,引入MyBatis相关类库和数据库连接相关类库。

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.1.1</version>
</dependency>

(2) 添加MyBatis配置文件,设置MyBatis相关参数

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><settings><setting name="cacheEnabled" value="true"/><setting name="defaultStatementTimeout" value="3000"/><setting name="mapUnderscoreToCamelCase" value="true"/><setting name="useGeneratedKeys" value="true"/></settings></configuration>

(3) 在系统配置文件application.properities中配置数据库URL,用户名密码等信息,同时设置MyBatis配置文件位置。

spring.datasource.url=
spring.datasource.username=
#spring.datasource.password=
spring.datasource.password=mybatis.config-location=classpath:mybatis-config.xml

1.2 添加数据库表实体类

  根据user表的具体字段设置实体类的对应字段,MyBatis的mapUnderscoreToCamelCase参数设定为true,说明数据库字段的下划线分割自动对应实体类的驼峰形式。

public class User {private int id;private String name;private String password;private String salt;private String headUrl;private String role;public User(){}public User(String name){this.name = name;this.password = "";this.salt = "";this.headUrl = "";this.role = "user";}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getSalt() {return salt;}public void setSalt(String salt) {this.salt = salt;}public String getHeadUrl() {return headUrl;}public void setHeadUrl(String headUrl) {this.headUrl = headUrl;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}
}

根据login_ticket表的具体字段设置实体类的对应字段

public class LoginTicket {private int id;private int userId;private Date expired;private int status;private String ticket;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 Date getExpired() {return expired;}public void setExpired(Date expired) {this.expired = expired;}public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}public String getTicket() {return ticket;}public void setTicket(String ticket) {this.ticket = ticket;}
}

1.3 添加数据库操作DAO类

  根据MyBatis的使用特点,创建数据库操作接口UserDao.class和LoginTicket.class,用作对数据库执行具体的操作。

@Mapper
public interface UserDao {String TABLE_NAEM = " user ";String INSERT_FIELDS = " name, password, salt, head_url ,role ";String SELECT_FIELDS = " id, " + INSERT_FIELDS;@Insert({"insert into",TABLE_NAEM,"(",INSERT_FIELDS,") values (#{name},#{password},#{salt},#{headUrl},#{role})"})public void insertUser(User user);@Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"where id=#{id}"})public User seletById(int id);@Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"where name=#{name}"})public User seletByName(@Param("name") String name);@Delete({"delete from",TABLE_NAEM,"where id=#{id}"})public void deleteById(int id);
}
@Mapper
public interface LoginTicketDao {String TABLE_NAEM = " login_ticket ";String INSERT_FIELDS = " user_id, ticket, expired, status ";String SELECT_FIELDS = " id, " + INSERT_FIELDS;@Insert({"insert into",TABLE_NAEM,"(",INSERT_FIELDS,") values (#{userId},#{ticket},#{expired},#{status})"})void insertLoginTicket(LoginTicket loginTicket);@Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"where id=#{id}"})LoginTicket seletById(int id);@Select({"select",SELECT_FIELDS,"from",TABLE_NAEM,"where ticket=#{ticket}"})LoginTicket seletByTicket(String ticket);@Update({"update",TABLE_NAEM,"set status = #{status} where ticket = #{ticket}"})void updateStatus(@Param("ticket") String ticket, @Param("status") int status);@Delete({"delete from",TABLE_NAEM,"where id=#{id}"})void deleteById(int id);
}

2. 用户注册

  ORM框架的作用就是将对象与关系型数据库的表进行关联性绑定,根据插入的对象解析出对应的字段插入数据库中。用户注册是在验证注册用户名密码可用的情况下生成一个User对象插入数据库中,同时为了保护用户信息安全在密码存储时,随机生成固定长度的字符串作为salt与密码组合后讲过MD5加密存储在数据库字段中。
  用户密码如果直接散列后存储在数据库中,黑客可以通过获得这个密码散列值,然后通过查散列值字典(彩虹表)的方式暴力破解,得到用户的密码;通过加salt加密的方式可以一定程度上解决这一问题,因为salt值由系统随机生成,也只有系统知道。即便黑客获取了密码的散列值但在不知道salt值的前提下暴力破解散列值的几率大大降低。
 加salt加密并不能完全杜绝用户密码的泄露,因为一旦密码散列值和salt值同时泄露,黑客通过salt值重建彩虹表,依旧能够获取用户密码。只是这样的计算成本会大大增加,假设每个用户一个salt, 散列值字典的字段有10万, 一次基于salt值的字典重建就要重新生成10万个字段,那么10个密码就需要生成10个散列字典,也就是100万 个字段。因此,从某种意义上来讲,增加salt的长度也就增加了散列值字典字段的数目,也可以提高安全性。还有一种提高安全性的方式,就是salt值的动态生成。通过一定算法动态生成salt值,这样可以大大降低salt值泄露的风险。

public Map<String,String> register(String username, String password){Map<String,String> map = new HashMap<>();Random random = new Random();if (StringUtils.isBlank(username)){map.put("msg","用户名不能为空");return map;}if (StringUtils.isBlank(password)){map.put("msg","密码不能为空");return map;}User u = userDao.seletByName(username);if (u!=null){map.put("msg","用户名已经被占用");return map;}User user = new User();user.setName(username);user.setSalt(UUID.randomUUID().toString().substring(0,5));user.setHeadUrl(String.format("https://images.nowcoder.com/head/%dm.png",random.nextInt(1000)));user.setPassword(JblogUtil.MD5(password+user.getSalt()));user.setRole("user");userDao.insertUser(user);String ticket = addLoginTicket(user.getId());map.put("ticket",ticket);return map;}

3. 用户登录

  用户登录主要是进行用户名和密码的验证,由于用户在注册时候会生成随机的salt值进行密码存储加密,在密码验证时需要读取用户对应的salt值组合进行散列化后与数据库中用户密码进行比对,如果一致则登录成功。
  用户登录时明文传输密码存在风险,黑客可以通过抓包的方式截取用户信息。一般为了降低这种Web应用登录密码传输过程中泄露的风险,可以采用https方式传输和通过公匙和私匙的非对称加密的方式。

public Map<String,String> login(String username, String password){Map<String,String> map = new HashMap<>();Random random = new Random();if (StringUtils.isBlank(username)){map.put("msg","用户名不能为空");return map;}if (StringUtils.isBlank(password)){map.put("msg","密码不能为空");return map;}User u = userDao.seletByName(username);if (u==null){map.put("msg","用户名不存在");return map;}if (!JblogUtil.MD5(password+u.getSalt()).equals(u.getPassword())){map.put("msg","密码错误");return map;}String ticket = addLoginTicket(u.getId());map.put("ticket",ticket);return map;}

4. 免密码登录

  免密码登录功能主要是通过一种自动身份验证的方式实现。用户登录或注册成功后,在一定时间内(如2个小时)再次访问同一个Web程序的任一个页面时都无需再次登录,而是直接进入界面(仅限于本机)。实现这个功能关键就是服务端要识别客户的身份。使用Cookie是最简单的身从验证。
  Cookie是web服务器存放在客户端的一个文件,客户端访问特定URL时会查询该文件,将与该URL相关的Cookie字段传输至服务端用作特定处理。Cookie可以设置失效时间,当Cookie过了失效时间后会自动消失不再随请求传输到服务器。
  用户在登录成功或注册成功后随机生成一个ticket作为用户后续操作无需密码验证的凭证,往login_ticket表中插入一条记录将ticket与具体的用户绑定,这样用户在操作时候就能通过ticket凭证辨别身份。登录成功或注册成功后将ticket凭证放入Cookie,保存在用户浏览器中,在下次访问时候会随请求传输到服务器用作身份验证。
(1) 往login_ticket表中插入一条记录用于绑定ticket凭证和用户身份

public String addLoginTicket(int userId){LoginTicket loginTicket = new LoginTicket();loginTicket.setUserId(userId);Date date = new Date();date.setTime(date.getTime()+1000*3600*30);loginTicket.setExpired(date);loginTicket.setStatus(0);loginTicket.setTicket(UUID.randomUUID().toString().replaceAll("-",""));loginTicketDao.insertLoginTicket(loginTicket);return loginTicket.getTicket();
}

(2) 登录成功后将ticket凭证放入cookie,保存在用户浏览器;ticket有时效,过凭证期后用户需重新登录。

@RequestMapping("/login")public String login(Model model, HttpServletResponse httpResponse,@RequestParam String username,@RequestParam String password,@RequestParam(value = "next",required = false)String next){Map<String,String> map = userService.login(username,password);if (map.containsKey("ticket")) {Cookie cookie = new Cookie("ticket",map.get("ticket"));cookie.setPath("/");httpResponse.addCookie(cookie);if (StringUtils.isNotBlank(next)){return "redirect:"+next;}return "redirect:/";}else {model.addAttribute("msg", map.get("msg"));return "login";}}

(3) 注册成功后将ticket凭证放入cookie,保存在用户浏览器;ticket有时效,过凭证期后用户需重新登录。

@RequestMapping("/register")public String register(Model model, HttpServletResponse httpResponse,@RequestParam String username, @RequestParam String password,@RequestParam(value = "next",required = false)String next){Map<String,String> map = userService.register(username,password);if (map.containsKey("ticket")){Cookie cookie = new Cookie("ticket",map.get("ticket"));cookie.setPath("/");httpResponse.addCookie(cookie);if (StringUtils.isNotBlank(next))return "redirect:"+next;elsereturn "redirect:/";}else {model.addAttribute("msg",map.get("msg"));return "login";}}

5. 总结

  系统的注册登录功能主要是将用户注册的用户名密码存储在数据库,在下次登录时进行比对。同时为了免密码登录,在登录成功或注册成功时后生成用户凭证ticket与用户身份绑定在一起放入cookie中,等用户下次访问时候随请求传输至服务器进行验证。这样就能直接根据凭证识别用户身份,无需再经过用户名密码的验证。

Spring boot 搭建个人博客系统(二)——登录注册功能相关推荐

  1. 基于Spring Boot+Vue的博客系统 16——热门文章功能简单的实现

    废弃说明: 这个专栏的文章本意是记录笔者第一次搭建博客的过程,文章里里有很多地方的写法都不太恰当,现在已经废弃,关于SpringBoot + Vue 博客系列,笔者重新写了这个系列的文章,不敢说写的好 ...

  2. java 搭建个人博客_Spring boot 搭建个人博客系统(一)——整体思路

    Spring boot 搭建个人博客系统(一)--整体思路 一直想用Spring boot 搭建一个属于自己的博客系统,刚好前段时间学习了叶神的牛客项目课受益匪浅,乘热打铁也主要是学习,好让自己熟悉这 ...

  3. 基于Spring Boot技术栈博客系统企业级前后端实战之课程导论(零)

    零.说明(必读) 一.课程概述 1.1 名称 1.2 功能 1.3 技术点 1.4 目标 二.核心功能 2.1 用户管理 2.2 安全设置 2.3 博客管理 2.4 评论管理 2.5 点赞管理 2.6 ...

  4. 基于Spring Boot的个人博客系统(源码+数据库)

    目录 一.系统功能框架图 二.开发技术 三.开发环境 四.页面展示 1.登录页面 2.首页 3.文章详情页面 4.文章评论页面 ​5.后台页面 6.后台文件编辑页面 ​7.后台文章管理列表页面 五.文 ...

  5. 基于Spring Boot的个人博客系统的设计与实现毕业设计源码271611

    目  录 摘要 1 绪论 1.1研究意义 1.2开发背景 1.3系统开发技术的特色 1.4论文结构与章节安排 2个人博客系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1数据增加流程 ...

  6. halo java_Halo - 轻快,简洁,功能强大,使用Spring Boot开发的博客系统

    Halo 是一款现代化的个人独立博客系统,给习惯写博客的同学多一个选择. 简介 Halo [ˈheɪloʊ],意为光环.当然,你也可以当成拼音读(哈喽). 一个优秀的开源博客发布应用,值得一试. 声明 ...

  7. 手把手带你搭建个人博客系统(二)

    ⭐️前言⭐️ 因文章篇幅较长,所以整个流程分两篇文章来完成.

  8. Spring MVC + Hibernate JPA + Bootstrap 搭建的博客系统

    Spring MVC + Hibernate JPA + Bootstrap 搭建的博客系统 Demo 相关阅读: 1.Spring MVC+Hibernate JPA+ Bootstrap 搭建的博 ...

  9. 【Microsoft Azure 的1024种玩法】七十一.基于Azure Virtual Machines快速上手搭建Typecho博客系统

    [简介] Typecho 是基于 PHP5 构建的开源跨平台博客系统,Typecho开源跨平台博客系统相较于wordpress .hexo有一定的性能优势,是我们记录文章内容的最佳首选博客,那么本篇文 ...

最新文章

  1. Unity Pro builder创建模块化仓库建筑学习教程
  2. mysql选择行_在mysql中选择特殊行
  3. mysql的show profile使用总结
  4. vue商城项目开发:axios发送请求及列表数据展示
  5. db2 最近三个月_昙花一现,PA、PC月跌1800,通用料一蹶不振,救不起的塑市!
  6. 【阿里云MPS】MPS(原MTS)概述
  7. 深度学习优化器 optimizer 的选择
  8. Teams数据统计 - 聊天消息
  9. 模仿mongodb采用xml+json实现小型数据库
  10. 人能为赚钱吃多少苦?
  11. 为什么说只有深度思考才能让你持续赚到钱?
  12. Nginx + php-fpm + PHP 5.4 + MySQL 5.5 + Zend
  13. iOS:实现邮件和短信发送的简单示例
  14. 驻云CEO教你0门槛搭建电商网站,精选产品组合限量神券 低价买
  15. 蚂蚁金服实习生4.16面试
  16. 传奇爆率你了解多少?传奇爆率小技巧
  17. Eclipse:Build not configured correctly问题
  18. 计算机工程师标准着装,工程师穿什么样的衣服才好看
  19. CSS3 盒子设置border和padding不撑开盒子
  20. 关于线性代数:方程组同解

热门文章

  1. 使用Pyparsing为嵌入式开发定义自己的脚本语言
  2. 枯木:天猫双11项目组织协同
  3. 如何使用数码相机拍摄夜空
  4. python与matlab的优缺点
  5. 显卡核心 短路 原因是什么?
  6. word添加日期快捷键当前时间不自动更新
  7. 蓝牙调试器app HC05、HC06(功能非常强大)
  8. 短视频拍摄要注意啥细节?从服化道到拍摄思路,注意细节方能成功
  9. 计算机房的能耗指标,机房能耗管理
  10. Andoroid/IOS上架所需物料