创建springboot项目
选择Spring Web,MySQL Driver,Lombok
添加jwt和json依赖
1 2 3 4 5 6 7 8 9 10 11 12 13
| <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency>
|
添加knife4j swagger依赖
1 2 3 4 5 6 7
| <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <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
| <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> </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" 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: token-name: token ttl: 432000000000 secret-key: yumefusaka
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
| @Data public class Result<T> implements Serializable {
private Integer code; 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
|
@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
|
public class JwtUtils {
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; }
public static Claims parseToken(String signKey,String jwt){ Claims claims = Jwts.parser() .setSigningKey(signKey) .parseClaimsJws(jwt) .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
|
@Component @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 .... "); String token = request.getHeader("token"); log.info("从请求头中获取的令牌:{}", token);
if (!StringUtils.hasLength(token)) { log.info("Token不存在");
Result responseResult = Result.noToken("NOT_LOGIN"); String json = JSONObject.toJSONString(responseResult); response.setStatus(401); response.setContentType("application/json;charset=utf-8"); response.getWriter().write(json);
return false; }
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"); String json = JSONObject.toJSONString(responseResult); response.setContentType("application/json;charset=utf-8"); response.setStatus(401); response.getWriter().write(json); return false; }
return true; } }
|
当前用户信息存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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
|
@RestControllerAdvice(basePackages = {"com.{项目名}.controller"},annotations = {RestController.class}) public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class) public Result ex(Exception e){ e.printStackTrace(); 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
|
@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
|
@Configuration public class GlobalCorsConfig {
@Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); config.addAllowedMethod("*"); config.addAllowedHeader("*"); config.addExposedHeader("*"); UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); corsConfigurationSource.registerCorsConfiguration("/**",config); 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
|
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()); } }
|
完结撒花