SpringBootのPost登录注册
前言
本文参考并根据最新版本修改于 快速上手 Springboot 项目(登录注册保姆级教程)
注意事项:所有示例代码建议手敲一遍,因为有一些导入语句(import)会自动补全生成,而复制粘贴不会,所以若复制粘贴则记得复制导入语句
创建项目
需要注意的点:
语言[Language] 为
Java
类型[Type] 为
Maven
- 打包[Packaging] 为
Jar
选择依赖
Web
=>Spring Web
创建WebApi
SQL
=>Spring Data JPA
数据库持久层使用JPA
SQL
=>MySQL Driver
数据库使用MySQL
目录结构
/src/main/java/[软件包名称]/[项目名称]Application
为项目入口,为了初始化整个应用程序/src/main/resources/application.properties
为配置文件,用于指定数据库连接信息,服务器端口等配置
配置数据库
配置数据库
进入
/src/main/resources/application.properties
添加以下代码1
2
3
4
5
6
7
8
9# 配置数据库
# 配置驱动
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 若连接的是云数据库则将localhost改为云端ip
spring.datasource.url=jdbc:mysql://localhost:3306/logindemo?serverTimezone=UTC
# Mysql用户
spring.datasource.username=root
# Mysql对应用户密码
spring.datasource.password=123456789spring.datasource.driver-class-name
: 指定数据库驱动的类名,这里是 MySQL 数据库的驱动类。spring.datasource.url
: 指定数据库的连接 URL,其中localhost:3306
是数据库的地址和端口,logindemo
是数据库的名称,serverTimezone=UTC
是为了解决时区问题。spring.datasource.username
: 指定连接数据库的用户名,这里是 MySQL 用户名为root
。spring.datasource.password
: 指定连接数据库的用户密码,这里是 MySQL 用户密码为123456789
。
IDEA 连接数据库(非必要,仅为开发方便)
数据库
=>+
=>数据源
=>MySQL
配置数据源
主机[Host] 为数据库 IP 地址
用户[User] 为 MySqL 用户名
- 密码[Password] 为数据库密码
- 数据库[Database] 为数据库名
- 填完后点击 测试连接[Test Connection] 成功即可点击 确定[OK]
添加数据表
你可以使用第三方的可视化工具进行创建,如 Navicat Premium 15
, idea
等,本文只提供 MySQL
的代码
- 使用命令行进入
MySQL
- 创建数据表
1
2
3
4
5
6
7CREATE TABLE user
(
uid int(10) primary key NOT NULL AUTO_INCREMENT,
uname varchar(30) NOT NULL,
password varchar(255) NOT NULL,
UNIQUE (uname)
);
uid: 用户编号,主键,自增(按照本教程如果不添加自增会报错)
uname: 用户名,作为登录的账号(业务主键),不可重复
password: 密码,因为可能要加密,所以长度设了较长的 255
项目架构
视图层[View]
=>控制层[Controller]
=>业务逻辑层[Service]
=>数据持久层[DAO]
=>数据库
数据持久层
负责将 Java 对象与数据库表之间建立映射,实现数据的存储和检索,它包含了存放能对数据库表进行操作的类。在项目中将创建个实体类
User
映射到数据仓库的user
表repository
: 存放一些数据访问类(也就是一些能操纵数据库的类)的包,比如存放能对user
表进行增删改查的类domain
:存放实体类的包,比如User
类,其作为对应数据库user
表的一个实体类
业务逻辑层
处理业务逻辑。比如在项目中,就在业务逻辑层实现登录注册的逻辑,像是判断是否有用户名重复,密码是否正确等逻辑
service
: 存放业务逻辑接口的包serviceImpl
: 存放业务逻辑实现类的包,其中的类实现service
中的接口
控制层
接收视图层的请求,调用业务逻辑层的方法,并将结果返回给视图层。控制层负责协调整个应用程序的工作流。比如视图层请求登录并发来了用户的账号和密码,那么控制层就调用业务逻辑层的登录方法,并将账号密码作为参数传入,在将结果返回给视图层。
controller
: 存放控制器的包。比如UserController
视图层 的作用是展现数据。
最佳开发方式是自下向上开发,因为包之间的调用是上层调用下层
目录结构
在项目入口 /src/main/java/[软件包名称]/[项目名称]Application
中新建以下目录与文件
domain
repository
service
serviceImpl
controller
utils
存放工具类,一些自己封装的工具config
存放配置类,一些配置如登录拦截器,安全配置等
所有类与接口目录位置
项目实现
实现 User 实体类
在
domain
目录下新建名为User
类在
public
前添加@Table
和@Entity
注解@Table(name = “userlist”)
说明此实体类对应于数据库的 userlist 表,此处你的数据库表名为什么就写什么@Entity
说明此类是个实体类
主键 uid 上要加上
@Id
与@GeneratedValue(strategy = GenerationType.IDENTITY)
注解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.example.demo.domain;
import jakarta.persistence.*;
// 说明此实体类对应于数据库的user表
// 说明此类是个实体类
public class User {
private long uid;
// 注意属性名要与数据表中的字段名一致
// 主键自增int(10)对应long
private String username;
// 用户名属性varchar对应String
private String password;
// 密码属性varchar对应String
}为属性快捷生成 get、set 方法
将光标移至要插入 get、set 方法的位置
右键 => 生成… => Getter 和 Setter => 全选 => 确定
实现 UserDao
在
repository
目录下新建名为UserDao
接口添加数据库访问数据库的方法,在这里添加根据用户名查询用户方法
- 添加
@Repository
注解,用于标识数据访问层(DAO)组件,通常与持久层的操作相关,比如数据库操作。 - 接口要继承
JpaRepository
,这样 JPA 就能帮助我们完成对数据库的映射User
:实体类的类型,这里是User
类。Long
:主键的数据类型,这里是Long
类型。通常,实体类的主键类型会是实体的唯一标识,一般选择Long
或Integer
。
- 添加
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.example.postdemo.repository;
import com.example.postdemo.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
/*此处代码为根据用户名查询用户方法*/
public interface UserDao extends JpaRepository<User, Long> {
User findByUsername(String username); //通过用户名Username查找用户,注意使用驼峰命名法
User findByUsernameAndPassword(String username, String password); //通过用户名和密码查询用户
}
实现 UserSer
在
service
目录下新建名为UserService
接口添加登录注册用到的业务逻辑
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.example.postdemo.service;
import com.example.postdemo.domain.User;
public interface UserService {
/**
* 登录逻辑
* @param username 用户名
* @param password 密码
* @return
*/
User loginService(String username, String password);
/**
* 注册逻辑
* @param user 要注册的User对象
* @return
*/
User registService(User user);
}
实现 UserServiceImpl
- 要在
UserServiceImpl
中实现UserService
中的方法
在
service/serviceImpl
目录下新建名为UserServiceImpl
类添加需要实现的方法
- 在
public class UserServiceImpl
后添加implements UserService
(此时会报错,原因是方法没实现) - 右击报错条目
- 点击快速修复
- 选择实现方法
- 在
实现登录逻辑
因为要用到 UserDao 中的方法,所以先通过
@Resource
注解帮助我们实例化 UserDao 对象@Resource
是 Java EE 中的注解,用于实现依赖注入。
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private UserDao userDao;// 引入UserDao
public User loginService(String username, String password) {
// 如果账号密码都对则返回登录的用户对象,如有错误,返回null
User user = userDao.findByUsernameAndPassword(username, password);
// 重要信息置空
if(user != null) {
user.setPassword("");// 避免将密码返回给客户端
}
return user;// 返回登录用户
}
实现注册逻辑
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public User registService(User user) {
// 当新用户的用户名已存在
if (userDao.findByUsername(user.getUsername()) != null) {
return null;// 返回null表示用户名已存在,注册失败
} else {
// 返回创建好的用户对象(带uid)
User newUser = userDao.save(user);
if (newUser != null) {
newUser.setPassword("");// 避免将密码返回给客户端
}
return newUser;// 返回新注册的用户对象
}
}
在
public class UserServiceImpl implements UserService
上添加@Service
注解@Service
注解用于标识一个类是业务逻辑层(Service 层)的组件
最终完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44package com.example.postdemo.service.serviceImpl;
import com.example.postdemo.domain.User;
import com.example.postdemo.repository.UserDao;
import com.example.postdemo.service.UserService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
public class UserServiceImpl implements UserService {
private UserDao userDao;// 引入UserDao
public User loginService(String username, String password) {
// 如果账号密码都对则返回登录的用户对象,如有错误,返回null
User user = userDao.findByUsernameAndPassword(username, password);
// 重要信息置空
if(user != null) {
user.setPassword("");// 避免将密码返回给客户端
}
return user;// 返回登录用户
}
public User registService(User user) {
// 当新用户的用户名已存在
if (userDao.findByUsername(user.getUsername()) != null) {
return null;// 返回null表示用户名已存在,注册失败
} else {
// 返回创建好的用户对象(带uid)
User newUser = userDao.save(user);
if (newUser != null) {
newUser.setPassword("");// 避免将密码返回给客户端
}
return newUser;// 返回新注册的用户对象
}
}
}
实现工具类 Result
工具类 Result
的作用是作为返回给前端的统一后的对象。也就是说返回给前端的都是 Result 对象,只是对象中的属性不太一样,这样方便前端固定接收格式。
在
utils
目录下新建Result
类在
public class Result
后面加上<T>
public class Result<T>
创建私有变量
1
2
3
4
5private String code;
private String msg;
private T data;生成
Getter 和 Setter
回车
右键 => 生成… => Getter 和 Setter => 全选 => 确定
- 详情看 实现 User 实体类
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66package com.example.postdemo.utils;
public class Result<T> {
private String code;
private String msg;
private T data;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Result() {
}
public Result(T data) {
this.data = data;
}
public static Result success() {
Result result = new Result<>();
result.setCode("0");
result.setMsg("请求成功");
return result;
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg("请求成功");
return result;
}
public static <T> Result<T> success(T data,String msg) {
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg(msg);
return result;
}
public static Result error(String code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}可以看出
Result
是个模板类,因此想要返回什么数据类型给前端都行,如Result<User>
可以直接用类名.方法名
调用。
实现 UserController
在
controller
目录下创建UserController
类添加
@RestController
与@RequestMapping("/user")
注解,注入 UserService@RequestMapping("/user")
是这个控制器的基路由,如/user
就是http://IP:端口/user
@RequestParam
用于从请求的参数中获取数据,如http://IP:端口/user/login?value=1&value2=2
,其中 value 和 val2 就是所带的参数@RequestBody
用于从请求体中获取数据代码片段
1
2
3
4
5
6
7
8+ @RestController
+ @RequestMapping("/user")
public class UserController {
// 注入UserService
+ @Resource
+ private UserService userService;
}
实现登录控制
添加路由
@PostMapping("/login")
,表示处理Post
请求,路由为http://IP:端口/user/login
代码片段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//登录控制
public Result<User> loginCotroller( { User login)
String username = login.getUsername();
String password = login.getPassword();
User user = userService.loginService(username, password);
if (user != null) {
return Result.success(user, "登录成功!");
} else {
return Result.error("500", "账号或密码错误");
}
}
实现注册控制
添加路由
@PostMapping("/register")
,表示处理Post
请求,路由为http://IP:端口/user/register
代码片段
1
2
3
4
5
6
7
8
9
10
11
12// 注册控制
public Result<User> registController( { User newUser)
User user = userService.registService(newUser);
if (user != null) {
return Result.success(user, "注册成功");
} else {
return Result.error("501", "用户名已存在");
}
}
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package com.example.postdemo.controller;
import com.example.postdemo.domain.User;
import com.example.postdemo.service.UserService;
import com.example.postdemo.utils.Result;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;
//
public class UserController {
// 注入UserService
private UserService userService;
// 登录控制
public Result<User> loginCotroller( { User login)
String username = login.getUsername();
String password = login.getPassword();
User user = userService.loginService(username, password);
if (user != null) {
return Result.success(user,"登录成功!");
} else {
return Result.error("500", "账号或密码错误");
}
}
// 注册控制
public Result<User> registController( { User newUser)
User user = userService.registService(newUser);
if (user != null) {
return Result.success(user, "注册成功");
} else {
return Result.error("501", "用户名已存在");
}
}
}
处理跨域问题
跨域问题就是前端后端的IP 地址和端口与后端的 IP 地址和端口不同就会导致前端无法获取数据
在
config
目录下新建GlobalCorsConfig
类添加
@Configuration
注解,用于标识这个类是配置类,常用于定义Spring
应用程序上下文中的bean
配置信息注意:SpringBoot2.4.0 以后下方
allowedOrigins
需要被allowedOriginPatterns
代替!!!!完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30package com.example.postdemo.config;
/*
处理跨域
SpringBoot2.4.0 以后下方 allowedOrigins 需要被 allowedOriginPatterns 代替!!!!
*/
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class GlobalCorsConfig {
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
//.allowedOrigins("*")
.allowCredentials(true)
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.exposedHeaders("*");
}
};
}
}
至此,所有代码就写完了
测试
使用 Postman
进行测试
注册测试
向路由
http://localhost:8080/user/register
发送post
请求,并添加请求体1
2
3
4{
"username": "test4",
"password": "123456"
}点击发送请求
成功返回
1
2
3
4
5
6
7
8
9{
"code": "0",
"msg": "注册成功",
"data": {
"uid": 4,
"username": "test04",
"password": ""
}
}失败返回
1
2
3
4
5{
"code": "501",
"msg": "用户名已存在",
"data": null
}
登录测试
向路由
http://localhost:8080/user/login
发送post
请求,并添加参数1
http://localhost:8080/user/login?username=test01&password=123456
点击发送请求
成功返回
1
2
3
4
5
6
7
8
9{
"code": "0",
"msg": "登录成功!",
"data": {
"uid": 1,
"username": "test01",
"password": ""
}
}失败返回
1
2
3
4
5{
"code": "500",
"msg": "账号或密码错误",
"data": null
}