diff --git a/pom.xml b/pom.xml
index aa06b7b..5c852f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,6 +52,10 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
com.auth0
java-jwt
diff --git a/src/main/java/com/example/config/SecurityConfiguration.java b/src/main/java/com/example/config/SecurityConfiguration.java
index c32e2c2..8ac01c2 100644
--- a/src/main/java/com/example/config/SecurityConfiguration.java
+++ b/src/main/java/com/example/config/SecurityConfiguration.java
@@ -23,6 +23,7 @@ import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import java.io.IOException;
+import java.io.PrintWriter;
@Configuration
public class SecurityConfiguration {
@@ -54,10 +55,6 @@ public class SecurityConfiguration {
//用户登录了但没有权限访问一些资源
.accessDeniedHandler(this::onAccessDeny)
)
-
-
-
-
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(conf -> conf
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
@@ -106,6 +103,13 @@ public class SecurityConfiguration {
public void onLogoutSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
-
+ response.setContentType("application/json;charset=utf-8");
+ PrintWriter writer = response.getWriter();
+ String authorization = request.getHeader("Authorization");
+ if (utils.invalidateJwt(authorization)){
+ writer.write(RestBean.success().asJsonString());
+ }else {
+ writer.write(RestBean.failure(400,"退出登录失败").asJsonString());
+ }
}
}
\ No newline at end of file
diff --git a/src/main/java/com/example/utils/Const.java b/src/main/java/com/example/utils/Const.java
new file mode 100644
index 0000000..09193a8
--- /dev/null
+++ b/src/main/java/com/example/utils/Const.java
@@ -0,0 +1,5 @@
+package com.example.utils;
+
+public class Const {
+ public static final String JWT_BLACK_LIST = "jwt:blacklist:";
+}
diff --git a/src/main/java/com/example/utils/JwtUtils.java b/src/main/java/com/example/utils/JwtUtils.java
index 2597b63..5c998fc 100644
--- a/src/main/java/com/example/utils/JwtUtils.java
+++ b/src/main/java/com/example/utils/JwtUtils.java
@@ -6,8 +6,10 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
+import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
@@ -16,6 +18,8 @@ import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
@Component
public class JwtUtils {
@@ -25,16 +29,48 @@ public class JwtUtils {
@Value("${spring.security.jwt.expire}")
int expire;
+ @Resource
+ StringRedisTemplate template;
+ //添加让jwt令牌失效的方法
+ public boolean invalidateJwt(String headerToken){
+ String token =this.convertToken(headerToken);
+ if (token == null ) return false;
+ Algorithm algorithm = Algorithm.HMAC256(key);
+ JWTVerifier jwtVerifier = JWT.require(algorithm).build();
+ try {
+ DecodedJWT jwt = jwtVerifier.verify(token);
+ String id = jwt.getId();
+ return deleteToken(id ,jwt.getExpiresAt());
+ }catch (JWTVerificationException e){
+ return false;
+ }
+ }
+
+ private boolean deleteToken(String uuid , Date time){
+ if (this.isInvalidToken(uuid))
+ return false;
+ Date now = new Date();
+ long expire = Math.max(time.getTime() - now.getTime() , 0 );
+ template.opsForValue().set(Const.JWT_BLACK_LIST + uuid , "" ,expire , TimeUnit.MILLISECONDS);
+ return true;
+ }
+
+ private boolean isInvalidToken(String uuid){
+ return Boolean.TRUE.equals(template.hasKey(Const.JWT_BLACK_LIST + uuid));
+ }
+
+
//解析jwt
public DecodedJWT resolverJwt(String headerToken){
String token =this.convertToken(headerToken);
if (token == null ) return null;
Algorithm algorithm = Algorithm.HMAC256(key);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
- //验证jwt是否被用户篡改过,是的话抛出异常
try {
DecodedJWT verify = jwtVerifier.verify(token);
- Date expiresAt = verify.getExpiresAt(); //判断token是否过期
+ if (this.isInvalidToken(verify.getId()))
+ return null;
+ Date expiresAt = verify.getExpiresAt();
return new Date().after(expiresAt) ? null : verify;
} catch (JWTVerificationException e) {
@@ -70,6 +106,8 @@ public class JwtUtils {
Algorithm algorithm = Algorithm.HMAC256(key);
Date expire = this.expireTime();
return JWT.create()
+ //让每个令牌生成一个随机的uuid
+ .withJWTId(UUID.randomUUID().toString())
.withClaim("id",id)
.withClaim("name",username)
.withClaim("authorities",details.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())