SpringBoot 进阶
JWT
JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传递信息。信息以 JSON 对象的形式进行编码,经过签名保证安全性,通常用于身份验证和信息交换。
一个 JWT 实际上是一个字符串,它由三部分组成,每部分之间用点(.
)分隔,然后经过 Base64 处理:
- Header(头部)
- Payload(负载)
- Signature(签名)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. Header
头部通常包括两部分:令牌的类型(即 JWT)和所使用的签名算法(例如 HMAC SHA256 或 RSA)。
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
负载部分包含所要传递的信息,这些信息称为 claims(声明)。声明是关于实体(通常是用户)和其他数据的语句。有三种类型的声明:注册声明、公共声明和私有声明。
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"iat": 1516239022
}
3. Signature
要创建签名部分,必须使用头部指定的算法(例如 HMAC SHA256)对 header 和 payload 进行编码,并使用一个密钥。签名用于确认消息在传输过程中未被篡改、并验证发送方的身份。
拦截器
定义拦截器
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class MyInterceptor extends HandlerInterceptor {
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
// 接收到请求时触发
}
@Override
public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response,
@NonNull Object handler, @Nullable Exception ex) {
// 请求处理后触发
}
}
注册拦截器
实现 WebMvcConfigurer 类,通过重写 addInterceptors 方法添加拦截器
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Resource
private MyInterceptor interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor).excludePathPatterns("/login");
}
}
在拦截器中处理 JWT
一般来说,JWT 参数添加在请求头中
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
// 获取请求头的Authorization字段内容
String header = request.getHeader("Authorization");
if (header == null || !header.startsWith("Bearer ")) {
throw new IllegalStateException("Please login first");
}
String token = header.substring(7);
// 处理token
return true;
}
JWT 工具类
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
@Component
public class JwtUtil {
private final Algorithm algorithm;
private final JWTVerifier verifier;
private final Duration defaultExpireTime;
public JwtUtil(@Value("${app.security.jwt.secret}") String secret,
@Value("${app.security.jwt.expiresIn}") Long expiresInMinutes) {
this.algorithm = Algorithm.HMAC512(secret);
this.verifier = JWT.require(algorithm).build();
this.defaultExpireTime = Duration.ofMinutes(expiresInMinutes);
}
public DecodedJWT verify(String token) {
try {
return verifier.verify(token);
} catch (JWTVerificationException e) {
throw new IllegalArgumentException("Invalid JWT token");
}
}
public String generateAccessToken(String id) {
return generateAccessToken(id, defaultExpireTime);
}
public String generateAccessToken(String id, Duration expireTime) {
Instant now = Instant.now();
return JWT.create()
.withSubject(id)
.withIssuedAt(now)
.withExpiresAt(now.plus(expireTime))
.sign(algorithm);
}
public String generateAccessTokenWithClaims(String id, Map<String, String> claims) {
Instant now = Instant.now();
var jwtBuilder = JWT.create()
.withSubject(id)
.withIssuedAt(now)
.withExpiresAt(now.plus(defaultExpireTime));
claims.forEach(jwtBuilder::withClaim);
return jwtBuilder.sign(algorithm);
}
public boolean isTokenExpired(DecodedJWT decodedJWT) {
Instant expiresAt = decodedJWT.getExpiresAt().toInstant();
return Instant.now().isAfter(expiresAt);
}
}
MyBatis
MyBatis 是一个持久层框架。它将 SQL 语句从 Java 代码中分离出来,并通过 XML 配置文件或注解的方式进行管理。
基本概念
- <mapper>:定义了命名空间以及 SQL 语句和映射定义。
- <select>、<insert>、<update>、<delete>:定义具体的 SQL 语句。通过
id
属性标识,对应 mapper 接口的方法名。 - <resultMap>:定义了如何从数据库结果集中映射数据为 Java 对象。
- #{id}:表示参数占位符。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="fun.sast.sasttodo.mapper.TaskMapper">
<!-- 结果映射定义 -->
<resultMap id="taskResultMap" type="fun.sast.sasttodo.entity.Task">
<id property="id" column="id" />
<result property="title" column="title" />
...
</resultMap>
<!-- 查询操作 -->
<select id="selectTask" resultMap="taskResultMap">
SELECT id, title FROM task WHERE id = #{id}
</select>
<!-- 插入操作 -->
<insert id="insertTask">
INSERT INTO task (id, title) VALUES (#{id}, #{title})
</insert>
<!-- 更新操作 -->
<update id="updateTask">
UPDATE task SET title = #{title} WHERE id = #{id}
</update>
<!-- 删除操作 -->
<delete id="deleteTask">
DELETE FROM task WHERE id = #{id}
</delete>
</mapper>