创建springboot+Vue项目

1、创建springboot项目

2、准备数据表

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
-- 创建数据库
create database big_event;

-- 使用数据库
use big_event;

-- 用户表
create table user (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) comment '密码',
nickname varchar(10) default '' comment '昵称',
email varchar(128) default '' comment '邮箱',
user_pic varchar(128) default '' comment '头像',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '用户表';

-- 分类表
create table category(
id int unsigned primary key auto_increment comment 'ID',
category_name varchar(32) not null comment '分类名称',
category_alias varchar(32) not null comment '分类别名',
create_user int unsigned not null comment '创建人ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
constraint fk_category_user foreign key (create_user) references user(id) -- 外键约束
);

-- 文章表
create table article(
id int unsigned primary key auto_increment comment 'ID',
title varchar(30) not null comment '文章标题',
content varchar(10000) not null comment '文章内容',
cover_img varchar(128) not null comment '文章封面',
state varchar(3) default '草稿' comment '文章状态: 只能是[已发布] 或者 [草稿]',
category_id int unsigned comment '文章分类ID',
create_user int unsigned not null comment '创建人ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
constraint fk_article_category foreign key (category_id) references category(id),-- 外键约束
constraint fk_article_user foreign key (create_user) references user(id) -- 外键约束
)

3、创建Springboot工程,引入对应的依赖(web、mybatis、mysql驱动)

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
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.3</version>
</parent>


<groupId>org.jjq</groupId>
<artifactId>big-event</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>big-event</name>
<url>http://maven.apache.org</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<!-- web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mybatis依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<!-- mysql驱动依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
</project>

4、配置文件application.yml中引入mybatis的配置信息

1
2
3
4
5
6
7
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/big_event
username: root
password: jinjiaqi123

5、创建包结构并准备实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class Article {
private Integer id;//主键ID
private String title;//文章标题
private String content;//文章内容
private String coverImg;//封面图像
private String state;//发布状态 已发布|草稿
private Integer categoryId;//文章分类id
private Integer createUser;//创建人ID
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}


1
2
3
4
5
6
7
8
9
10
public class Category {
private Integer id;//主键ID
private String categoryName;//分类名称
private String categoryAlias;//分类别名
private Integer createUser;//创建人ID
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}


1
2
3
4
5
6
7
8
9
10
11
public class User {
private Integer id;//主键ID
private String username;//用户名
private String password;//密码
private String nickname;//昵称
private String email;//邮箱
private String userPic;//用户头像地址
private LocalDateTime createTime;//创建时间
private LocalDateTime updateTime;//更新时间
}

开发流程

1明确需求
2阅读接口文档
3思路分析
4开发
5测试

用户相关

6、编写Result实体类

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
package org.jjq.pojo;


import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

//统一响应结果
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;//业务状态码 0-成功 1-失败
private String message;//提示信息
private T data;//响应数据

//快速返回操作成功响应结果(带响应数据)
public static <E> Result<E> success(E data) {
return new Result<>(0, "操作成功", data);
}

//快速返回操作成功响应结果
public static Result success() {
return new Result(0, "操作成功", null);
}

public static Result error(String message) {
return new Result(1, message, null);
}
}

7、编写Controller

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
package org.jjq.controller;

import org.jjq.pojo.Result;
import org.jjq.pojo.User;
import org.jjq.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

@Autowired
private UserService userService;

@RequestMapping("/register")
public Result register(String username,String password){
//查询用户
User u = userService.findByUserName(username);
//判断是否存在用户
if (u==null){
//没有占用
//注册
userService.register(username,password);
return Result.success();
}
else {
//占用
return Result.error("用户名已被占用");
}
}

}

8编写UserService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.jjq.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.jjq.pojo.User;

@Mapper
public interface UserMapper {
//根据用户名查询用户
@Select("select * from user where username=#{username}")
User findByUserName(String username);
//添加用户
@Insert("insert into user(username,password,create_time,update_time)"+
"values(#{username},#{password},now(),now())")
void add(String username, String password);
}


9编写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
package org.jjq.service.Impl;

import org.jjq.mapper.UserMapper;
import org.jjq.pojo.User;
import org.jjq.service.UserService;
import org.jjq.utils.Md5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

@Autowired
private UserMapper userMapper;

@Override
public User findByUserName(String username) {
User u = userMapper.findByUserName(username);
return u;
}

@Override
public void register(String username, String password) {
//加密密码
String md5String = Md5Util.getMD5String(password);
//添加
userMapper.add(username,md5String);
}
}

10编写UserMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.jjq.mapper;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.jjq.pojo.User;

@Mapper
public interface UserMapper {
//根据用户名查询用户
@Select("select * from user where username=#{username}")
User findByUserName(String username);
//添加用户
@Insert("insert into user(username,password,create_time,update_time)"+
"values(#{username},#{password},now(),now())")
void add(String username, String password);
}

11运行SpringBoot并在postman中进行测试

测试成功数据库中出现测试的数据就表示用户查询和注册功能已经完成了

12参数校验

传统方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RequestMapping("/register")
public Result register(String username,String password){
if(username!=null&&username.length()>=5&&username.length()<=16&&
password!=null&&password.length()>=5&&password.length()<=16)
{
//查询用户
User u = userService.findByUserName(username);
//判断是否存在用户
if (u==null){
//没有占用
//注册
userService.register(username,password);
return Result.success();
}
else {
//占用
return Result.error("用户名已被占用");
}
}
else {
return Result.error("参数不合法");
}
}

Spring Validation

Spring提供的一个参数校验框架,使用预定义的注解完成参数校验

1、引入Spring Validation依赖

1
2
3
4
5
6
<!--validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2、在参数前面添加@Pattern注解

1
2
3
4
5
6
public Result register(@Pattern(regexp = "^\\S{5,16}$") 
String username,
@Pattern(regexp = "^\\S{5,16}$")
String password){
}

3、在Controller类上添加@Validation注解

1
2
3
4
5
6
@Validated
@RestController
@RequestMapping("/user")
public class UserController {
}

参数校验失败异常处理

1
2
3
4
5
6
7
8
9
10
11
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage())? e.getMessage():"操作失败");
}

}

登录

2.1、登录功能

编写Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RequestMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username,@Pattern(regexp = "^\\S{5,16}$") String password){
//根据用户名查询用户
User loginUser = userService.findByUserName(username);
//判断该用户是否存在
if (loginUser==null){
return Result.error("用户名错误");
}
//判断密码是否正确,loginUser对象中的的password是密文
if (Md5Util.getMD5String(password).equals(loginUser.getPassword())){
return Result.success("JWT token令牌");
}

return Result.error("密码错误");
}

编写异常处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.jjq.exception;

import org.jjq.pojo.Result;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
public Result handleException(Exception e){
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage())? e.getMessage():"操作失败");
}

}

在postman中测试

1
2
3
4
5
{
"code": 0,
"message": "操作成功",
"data": "JWT token令牌"
}

2.2、登录认证

令牌:令牌就是一段字符串

  • 承载业务数据,减少后续请求查询数据库的次数
  • 防篡改,保证信息的合法性和有效性

JWT:

简介·:

  • 全称:JSON Web Token(https://jwt.io/)
  • 定义了一种简介的、自包含的格式,用于通信双方以json数据格式安全的传输信息
  • 组成
    • 第一部分:Header(头),记录令牌类型、签名算法等。例如:{“alg”:HS256,“type”:“JWT”}
    • 第二部分:Payload(有效载荷),携带一些自定义信息,默认信息等。例如:{“id”:1,“username”:“Tom”}
    • 第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来

JWT-生成:

在Maven中引入JWT

1
2
3
4
5
6
7
8
9
10
11
12
<!--jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<!--单元测试的坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>

注意事项:

  • JWT校验时使用的签名秘钥,必须和生成JWT令牌时使用的秘钥是配套的
  • 如果JWT令牌解析校验时报错,则说明JWT令牌被篡改或失效了,令牌非法

导入工具类JwtUtil

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
package org.jjq.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtil {

private static final String KEY = "iloveyou";
//接收业务数据,生成token并返回
public static String genToken(Map<String, Object> claims) {
return JWT.create()
.withClaim("claims", claims)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
.sign(Algorithm.HMAC256(KEY));
}

//接收token,验证token,并返回业务数据
public static Map<String, Object> parseToken(String token) {
return JWT.require(Algorithm.HMAC256(KEY))
.build()
.verify(token)
.getClaim("claims")
.asMap();
}
}

在Controller文件夹下创建ArticleController

1
2
3
4
5
6
7
8
9
10
11
@RestController
@RequestMapping("/article")
public class ArticleController {

@GetMapping("/list")
public Result<String> list(){
//验证token
return Result.success("所有文章的数据");
}
}

创建拦截器LoginInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class LoginInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//令牌验证
String token = request.getHeader("Authorization");
//验证token
try{
Map<String, Object> claims = JwtUtil.parseToken(token);
//放行
return true;
}catch (Exception e){
//http响应状态码为401
response.setStatus(401);
//不放行
return false;
}
}
}

创建config文件夹并且创建WebConfig类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private LoginInterceptor loginInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
//登录接口和注册接口不拦截
registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
}
}


测试:

在postman的请求中添加Authorization将用户登录生成的token输到postman的Authorization中

取消Authorization后显示401状态码

——————————————————————-分割———————————————————————-

学习总结

端口号和虚拟目录

配置文件 第二种更好

配置信息的书写

打包插件