600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > Spring boot 搭建个人博客系统(二)——登录注册功能

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

时间:2020-08-14 05:38:54

相关推荐

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

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 "-////DTD Config 3.0//EN""/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,用作对数据库执行具体的操作。

@Mapperpublic 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);}

@Mapperpublic 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("/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中,等用户下次访问时候随请求传输至服务器进行验证。这样就能直接根据凭证识别用户身份,无需再经过用户名密码的验证。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。