创建springboot项目

选择Spring Web,MySQL Driver,Lombok


添加jwt和json依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- JWT依赖 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>

<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>

添加knife4j swagger依赖

1
2
3
4
5
6
7
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<!--springboot2换成knife4j-openapi3-spring-boot-starter 版本为4.3.0-->
<version>4.4.0</version>
</dependency>

添加mybatis-plus依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!--MybatisPlus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.3</version>
<!--springboot2 版本为2.0.6-->
</dependency>

<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>

根据依赖编写application.yml

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
spring.application.name: {项目名}

server:
port: 8080

spring:
main:
allow-circular-references: true # 允许依赖循环
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/{数据库名}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: j

mybatis-plus:
type-aliases-package: com.{项目名}.pojo.Entity # 别名扫描包
mapper-locations: "classpath*:/mapper/**/*.xml" # Mapper.xml文件地址,默认值
configuration:
map-underscore-to-camel-case: false # 是否开启下划线和驼峰的映射
cache-enabled: false # 是否开启二级缓存
global-config:
db-config:
update-strategy: not_null # 更新策略:只更新非空字段

logging:
level:
com:
{项目名}:
mapper: debug
service: info
controller: info

# jwt令牌信息
jwt:
token-name: token
ttl: 432000000000
secret-key: yumefusaka


# knife4j配置信息
springdoc:
api-docs:
enabled: true
path: /v3/api-docs
swagger-ui:
enabled: true
path: /swagger-ui.html


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
30
31
32
33
34
35
36
// common/result/Result.java
@Data
public class Result<T> implements Serializable {

private Integer code; //编码:200为成功,其它数字为失败
private String msg; //错误信息
private T data; //数据

public static <T> Result<T> success() {
Result<T> result = new Result<T>();
result.code = 200;
return result;
}

public static <T> Result<T> success(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 200;
return result;
}

public static <T> Result<T> noToken(T object) {
Result<T> result = new Result<T>();
result.data = object;
result.code = 401;
return result;
}

public static <T> Result<T> error(String msg) {
Result result = new Result();
result.msg = msg;
result.code = 500;
return result;
}

}

Jwt设置与组件

1
2
3
4
5
6
7
8
9
10
// common/properties/JwtProperties.java

@Data
@Component
@ConfigurationProperties(prefix="jwt")
public class JwtProperties {
private String secretKey;
private long ttl;
private String tokenName;
}
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
// utils/JwtUtils.java

public class JwtUtils {

/**
* 生成JWT令牌
* @param claims JWT第二部分负载 payload 中存储的内容
* @return
*/
public static String createToken(String signKey,long expire,Map<String, Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)//自定义信息(有效载荷)
.signWith(SignatureAlgorithm.HS256, signKey)//签名算法(头部)
.setExpiration(new Date(System.currentTimeMillis() + expire))//过期时间
.compact();
return jwt;
}

/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseToken(String signKey,String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)//指定签名密钥
.parseClaimsJws(jwt)//指定令牌Token
.getBody();
return claims;
}
}

token登录自定义拦截器

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
// interceptor/LoginCheckInterceptor.java

//自定义拦截器
@Component //当前拦截器对象由Spring创建和管理
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {

@Autowired
private JwtProperties jwtProperties;

//前置方式
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle .... ");
//1.获取请求url
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
//3.获取请求头中的令牌(token)
String token = request.getHeader("token");
log.info("从请求头中获取的令牌:{}", token);

//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if (!StringUtils.hasLength(token)) {
log.info("Token不存在");

//创建响应结果对象
Result responseResult = Result.noToken("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
//设置状态码
response.setStatus(401);
//设置响应头(告知浏览器:响应的数据类型为json、响应的数据编码表为utf-8)
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);

return false;//不放行
}

//5.解析token,如果解析失败,返回错误结果(未登录)
try {
Claims claims = JwtUtils.parseToken(jwtProperties.getSecretKey(), token);
BaseContext.setCurrentId((String) claims.get("id"));
} catch (Exception e) {
log.info("令牌解析失败!");

//创建响应结果对象
Result responseResult = Result.noToken("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
//设置响应头
response.setContentType("application/json;charset=utf-8");
//设置状态码
response.setStatus(401);
//响应
response.getWriter().write(json);
return false;
}

//6.放行
return true;
}
}

当前用户信息存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// common/context/BaseContent.java
public class BaseContext {

public static ThreadLocal<String> threadLocal = new ThreadLocal<>();

public static void setCurrentId(String id) {
threadLocal.set(id);
}

public static String getCurrentId() {
return threadLocal.get();
}

public static void removeCurrentId() {
threadLocal.remove();
}
}

全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
// handler/GlobalExceptionHandler.java

@RestControllerAdvice(basePackages = {"com.{项目名}.controller"},annotations = {RestController.class})
public class GlobalExceptionHandler {

//处理异常
@ExceptionHandler(Exception.class) //指定能够处理的异常类型
public Result ex(Exception e){
e.printStackTrace();//打印堆栈中的异常信息
//捕获到异常之后,响应一个标准的Result
return Result.error(e.getMessage());
}
}

Web统一配置类并注册自定义拦截器

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
// config/WebMvcConfiguration.java

@Configuration
@Slf4j
public class WebMvcConfiguration implements WebMvcConfigurer {

//自定义的拦截器对象
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;

//注册自定义拦截器
public void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
registry.addInterceptor(loginCheckInterceptor)
.addPathPatterns("/user/**")
.excludePathPatterns("/*/login");
}


//设置静态资源映射
public void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始设置静态资源映射");
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

跨域过滤器配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// config/GlobalCorsConfig.java

@Configuration
public class GlobalCorsConfig {

@Bean
public CorsFilter corsFilter() {
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
// 放行哪些原始域
config.addAllowedOrigin("*");
// 放行哪些请求方式
config.addAllowedMethod("*");
// 放行哪些原始请求头部信息
config.addAllowedHeader("*");
// 暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
//3. 返回新的CorsFilter
return new CorsFilter(corsConfigurationSource);
}
}

时间转字符串配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// config/LocalDateTimeConfig.java

import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;


@Configuration
public class LocalDateTimeConfig {

@Bean
public LocalDateTimeSerializer localDateTimeDeserializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}

@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
}
}

完结撒花