SpringBoot项目——配置Mysql与session注册登录验证实现
回顾:
SpringBoot项目——创建菜单与游戏页面
SpringBoot项目——配置git环境与项目创建
文章目录
1. 配置 MySQL,创建所需数据库
mysql基本命令
-
启动:
net start mysql80
; -
关闭:
net stop mysql80
; -
连接:
mysql -uroot -p
链接用户名为root,密码为123456的数据库服务; -
退出:
exit
-
列出所有数据库:
show databases;
-
创建数据库:
create database kob;
-
删除数据库:
drop database kob;
扫描二维码关注公众号,回复: 17096508 查看本文章 -
使用数据库kob:
use kob;
-
列出当前数据库的所有表:
show tables;
-
创建名称为user的表,表中包含id和username两个属性:
create table user(id int, username varchar(100))
-
删除表:
drop table user;
-
在表中插入数据:
insert into user values(1, ' ');
-
查询表中数据:
select * from user where id=2;
-
删除某行数据:
delete from user where id = 2;
2. 配置 SpringBoot 操控 MySQL
2.1 安装依赖
-
在pom.xml文件中添加依赖:
- Spring Boot Starter JDBC ——用数据库
- Project Lombok —— 自动写好通用方法,包括set、get、toString、equal、hashCode方法
- MySQL Connector/J
- mybatis-plus-boot-starter —— MyBatis Plus 写好mapper层sql语句
- mybatis-plus-generator
-
spring-boot-starter-security —— 实现登录授权机制
-
jjwt-api
-
jjwt-impl
-
jjwt-jackson
-
在application.properties中添加数据库配置:
spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
2.2 SpringBoot 框架基本结构
-
pojo
层:将数据库中的表对应成Java中的Class -
mapper
层(Dao层):数据访问层,将pojo层的class中的操作,映射成sql语句 -
service
层:业务逻辑层,写具体的业务逻辑,组合使用mapper中的操作 -
controller
层:调度service,负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面 -
类似 SpringMVC、三层架构。
2.3 SpringBoot 代码介绍
SpringBoot入口: 启动SpringBoot应用,SpringApplication.run
。
package com.kob.backend;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}
pojo层: com.kob.backend.pojo.User.class
package com.kob.backend.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// 加入依赖的注解后可帮助写Set、Get、toString等方法。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
mapper层: com.kob.backend.mapper.UserMapper.class
package com.kob.backend.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kob.backend.pojo.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
// mybatisplus 帮助写sql语句
public interface UserMapper extends BaseMapper<User> {
}
service层:
// 目前业务逻辑较简单,直接写在了Controller层
controller层: package com.kob.backend.controller.user.UserController.class
package com.kob.backend.controller.user;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kob.backend.mapper.UserMapper;
import com.kob.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired // 如果要用到数据库接口的mapper的话,需要加此注解
UserMapper userMapper;
// @RequestMapping会将所有请求映射,也可指明类型。
// @GetMapping映射Get类型请求。@PostMapping会映射Post类型请求
// 查询所有
@GetMapping("/user/all/")
public List<User> getAll() {
return userMapper.selectList(null); //null表示查询所有的
}
// 按id查找
@GetMapping("/user/{userId}/")
public List<User> getUser(@PathVariable int userId){
//@PathVariable 映射 URL 绑定的占位符
// 按id查找
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",2);
return userMapper.selectList(queryWrapper);
// 上面三行等价与直接return userMapper.selectById(userId);
}
// 插入
@GetMapping("/user/add/{userId}/{username}/{password}/")
public String addUser(@PathVariable int userId,
@PathVariable String username,
@PathVariable String password){
User user = new User(userId, username, password);
userMapper.insert(user);
return "Add User Successfully!";
}
// 按id删除
@GetMapping("/user/delete/{userId}")
public String deleteUser(@PathVariable int userId){
userMapper.deleteById(userId);
return "Delete User Successfully!";
}
}
2.4 SpringBoot 主要注解介绍
-
@RestController注解
-
@RestController = @Controller + @ResponseBody
。将java对象转为json格式的数据,后端通过api将json数据传到前端 -
代表返回的是json格式的数据,这个注解是Spring4之后新加的注解,原来返回json格式的数据需要@ResponseBody配合@Controller一起使用;
-
如果我们在项目中使用的是@Conrtroller注解的话,不加@Response注解,则当直接返回一个字符串的时候,就好比返回的是一个模板页面,类似我们返回一个jsp页面一样。所以我们需要加上模板引擎。(
这种返回html一类的模板的开发方式现在一般不会再用了,因为现在都是前后端分离式的开发,后台服务器一般只需要返回json格式的数据即可,所以了解即可
)
-
-
@Autowired 如果要用到数据库接口的mapper的话,需要加此注解
-
@RequestMapping 会将所有请求映射,也可指明类型。
- @GetMapping映射Get类型请求。
- @PostMapping映射Post类型请求
-
@PathVariable 映射 URL 绑定的占位符
3. 修改Spring Security,实现 session 登录授权机制
- 装依赖
spring-boot-starter-security
验证过程: 登录时用户端输入用户名密码,后端查找数据库,若存在且密码一致则返回给用户端sessionID。sessionID存在本地cookie里,登录成功后,每次访问后端数据库都会自动将sessionID传递进去验证,一段时间内有效,过期需要再次登陆验证。数据库通过sessionId对应信息。
3.1 密码明文验证
-
编写
service.impl.UserDetailsServiceImpl
类,继承自UserDetailsService
接口,用来接入数据库信息。所写loadUserByUsername
方法,按用户名查找。
UserDetailsService
接口用来接入数据库信息,从数据库中获取信息。 -
如果想让security对接自己写的数据库,需要把username在数据库中对应的的用户找出来,返回他的密码(
这里的数据库存储中指明明文{noop}
),需要用到数据库操作mapper,能写private就写private,用数据库记得加上autowired.package com.kob.backend.service.impl; import ...; // 实现service.impl.UserDetailsServiceImpl类,继承自UserDetailsService接口 // UserDetailsService 用来接入数据库信息,从数据库中获取信息。 // 调用loadUserByUsername,按用户名查找。 @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired private UserMapper userMapper; @Override // 传入用户名,返回用户名和密码(要求数据库中用户名唯一) public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username",username); User user = userMapper.selectOne(queryWrapper); if(user == null){ throw new RuntimeException("用户不存在"); } return new UserDetailsImpl(user); } }
UserDetails介绍: 这个接口代表了最详细的用户信息,这个接口涵盖了一些必要的用户信息字段,具体的实现类对它进行了扩展。 它和Authentication接口很类似,比如它们都拥有username,authorities。
Authentication的getCredentials()与UserDetails中的getPassword()需要被区分对待,前者是用户提交的密码凭证,后者是用户正确的密码,认证器其实就是对这两者的比对。Authentication中的getAuthorities()实际是由UserDetails的getAuthorities()传递而形成的。Authentication接口中的getUserDetails()方法吗,其中的UserDetails用户详细信息便是经过了AuthenticationProvider之后被填充的package com.kob.backend.service.impl.utils; import ... @Data @NoArgsConstructor @AllArgsConstructor public class UserDetailsImpl implements UserDetails { private User user; @Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; } @Override public String getPassword() { return user.getPassword(); // 返回User中user的password } @Override public String getUsername() { return user.getUsername(); // 返回User中user的name } // 是否没被锁定 @Override public boolean isAccountNonExpired() { return true; } // 是否没被锁定 @Override public boolean isAccountNonLocked() { return true; } // 授权是否过期 @Override public boolean isCredentialsNonExpired() { return true; } // 用户是否被启用 @Override public boolean isEnabled() { return true; } }
3.2 密码是密文(经过加密算法加密)时如何实现?
若数据库中所存密码是P1,加密后是SP1。每次将输入的密码P2,加密成SP2,看与SP是否匹配来验证登录。即无序知道具体内容,只需知道是否匹配即可。
测试如下:
-
实现
config.SecurityConfig
类,用来实现用户密码的加密存储,此时数据库中需要存密文,而非明文。
package com.kob.backend.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public PasswordEncoder passwordEncoder() { // 返回所用加密方法(包括明文转密文,明文跟密文是否匹配) return new BCryptPasswordEncoder(); } }
-
注册插入用户信息时,便可以实现密码存储为密文
修改UserController中插入用户函数:// 插入 @GetMapping("/user/add/{userId}/{username}/{password}/") public String addUser(@PathVariable int userId, @PathVariable String username, @PathVariable String password){ PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();//指明编码方式 String encodePassword = passwordEncoder.encode(password); User user = new User(userId, username, encodePassword); userMapper.insert(user); return "Add User Successfully!"; }
结果如下: