diff --git a/src/main/java/xyz/playedu/api/PlayEduFCtx.java b/src/main/java/xyz/playedu/api/PlayEduFCtx.java index f17ade5..5361b8e 100644 --- a/src/main/java/xyz/playedu/api/PlayEduFCtx.java +++ b/src/main/java/xyz/playedu/api/PlayEduFCtx.java @@ -13,6 +13,8 @@ public class PlayEduFCtx { private static final String KEY_USER_ID = "user_id"; private static final String KEY_USER = "user"; + private static final String KEY_JWT_JTI = "jwt_jti"; + private static final String KEY_TOKEN = "token"; public PlayEduFCtx() { } @@ -49,4 +51,20 @@ public class PlayEduFCtx { public static User getUser() { return (User) get(KEY_USER); } + + public static void setJWtJti(String jti) { + put(KEY_JWT_JTI, jti); + } + + public static String getJwtJti() { + return (String) get(KEY_JWT_JTI); + } + + public static void setToken(String token) { + put(KEY_TOKEN, token); + } + + public static String getToken() { + return (String) get(KEY_TOKEN); + } } diff --git a/src/main/java/xyz/playedu/api/caches/UserLoginCache.java b/src/main/java/xyz/playedu/api/caches/UserLoginCache.java deleted file mode 100644 index 0ff081f..0000000 --- a/src/main/java/xyz/playedu/api/caches/UserLoginCache.java +++ /dev/null @@ -1,32 +0,0 @@ -package xyz.playedu.api.caches; - -import org.springframework.stereotype.Component; -import xyz.playedu.api.exception.LimitException; -import xyz.playedu.api.util.RedisUtil; - -/** - * @Author 杭州白书科技有限公司 - * @create 2023/3/10 14:13 - */ -@Component -public class UserLoginCache { - - private final static String keyTemplate = "user-login:%s"; - - private final static int expire = 10;//10s - - public void check(String email) throws LimitException { - if (RedisUtil.exists(key(email))) { - throw new LimitException(); - } - } - - public void put(String email) { - RedisUtil.set(key(email), "1", expire); - } - - private String key(String email) { - return String.format(keyTemplate, email); - } - -} diff --git a/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java b/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java index 6682dcc..e9c769a 100644 --- a/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java +++ b/src/main/java/xyz/playedu/api/controller/frontend/LoginController.java @@ -7,10 +7,12 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import xyz.playedu.api.caches.UserLoginCache; +import xyz.playedu.api.PlayEduFCtx; import xyz.playedu.api.constant.SystemConstant; import xyz.playedu.api.domain.User; import xyz.playedu.api.event.UserLoginEvent; +import xyz.playedu.api.event.UserLogoutEvent; +import xyz.playedu.api.exception.JwtLogoutException; import xyz.playedu.api.exception.LimitException; import xyz.playedu.api.request.frontend.LoginPasswordRequest; import xyz.playedu.api.service.JWTService; @@ -40,16 +42,10 @@ public class LoginController { @Autowired private ApplicationContext ctx; - @Autowired - private UserLoginCache userLoginCache; - @PostMapping("/password") public JsonResponse password(@RequestBody @Validated LoginPasswordRequest req) throws LimitException { String email = req.getEmail(); - // 限流-限制学员10s内登录成功一次 - userLoginCache.check(email); - User user = userService.find(email); if (user == null) { return JsonResponse.error("邮箱或密码错误"); @@ -72,4 +68,11 @@ public class LoginController { return JsonResponse.data(data); } + @PostMapping("/logout") + public JsonResponse logout() throws JwtLogoutException { + jwtService.userLogout(PlayEduFCtx.getToken()); + ctx.publishEvent(new UserLogoutEvent(this, PlayEduFCtx.getUserId(), PlayEduFCtx.getJwtJti())); + return JsonResponse.success(); + } + } diff --git a/src/main/java/xyz/playedu/api/event/UserLogoutEvent.java b/src/main/java/xyz/playedu/api/event/UserLogoutEvent.java new file mode 100644 index 0000000..13aea25 --- /dev/null +++ b/src/main/java/xyz/playedu/api/event/UserLogoutEvent.java @@ -0,0 +1,26 @@ +package xyz.playedu.api.event; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +import java.util.Date; + +/** + * @Author 杭州白书科技有限公司 + * @create 2023/3/21 14:31 + */ +@Setter +@Getter +public class UserLogoutEvent extends ApplicationEvent { + private Integer userId; + private String jti; + private Date createdAt; + + public UserLogoutEvent(Object source, Integer userId, String jti) { + super(source); + this.userId = userId; + this.jti = jti; + this.createdAt = new Date(); + } +} diff --git a/src/main/java/xyz/playedu/api/listener/UserLoginListener.java b/src/main/java/xyz/playedu/api/listener/UserLoginListener.java index 96e0525..d83a6c1 100644 --- a/src/main/java/xyz/playedu/api/listener/UserLoginListener.java +++ b/src/main/java/xyz/playedu/api/listener/UserLoginListener.java @@ -5,7 +5,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; -import xyz.playedu.api.caches.UserLoginCache; import xyz.playedu.api.constant.SystemConstant; import xyz.playedu.api.event.UserLoginEvent; import xyz.playedu.api.exception.JwtLogoutException; @@ -28,9 +27,6 @@ public class UserLoginListener { @Autowired private JWTService jwtService; - @Autowired - private UserLoginCache userLoginCache; - @EventListener @Async public void updateLoginInfo(UserLoginEvent event) throws JwtLogoutException { @@ -47,10 +43,4 @@ public class UserLoginListener { event.getUserAgent().getOs().toString() ); } - - @EventListener - public void writeCache(UserLoginEvent event) { - userLoginCache.put(event.getEmail()); - } - } diff --git a/src/main/java/xyz/playedu/api/listener/UserLogoutListener.java b/src/main/java/xyz/playedu/api/listener/UserLogoutListener.java new file mode 100644 index 0000000..01ebd4d --- /dev/null +++ b/src/main/java/xyz/playedu/api/listener/UserLogoutListener.java @@ -0,0 +1,27 @@ +package xyz.playedu.api.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import xyz.playedu.api.event.UserLogoutEvent; +import xyz.playedu.api.service.UserLoginRecordService; + +/** + * @Author 杭州白书科技有限公司 + * @create 2023/3/21 14:51 + */ +@Component +@Slf4j +public class UserLogoutListener { + + @Autowired + private UserLoginRecordService userLoginRecordService; + + @EventListener + @Async + public void updateLoginRecord(UserLogoutEvent event) { + userLoginRecordService.logout(event.getUserId(), event.getJti()); + } +} diff --git a/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java b/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java index b507534..6ff6327 100644 --- a/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java +++ b/src/main/java/xyz/playedu/api/middleware/FrontMiddleware.java @@ -61,6 +61,8 @@ public class FrontMiddleware implements HandlerInterceptor { PlayEduFCtx.setUserId(user.getId()); PlayEduFCtx.setUser(user); + PlayEduFCtx.setJWtJti(token); + PlayEduFCtx.setJWtJti(payload.getJti()); return HandlerInterceptor.super.preHandle(request, response, handler); } catch (Exception e) { diff --git a/src/main/java/xyz/playedu/api/service/JWTService.java b/src/main/java/xyz/playedu/api/service/JWTService.java index a70eef0..359eef9 100644 --- a/src/main/java/xyz/playedu/api/service/JWTService.java +++ b/src/main/java/xyz/playedu/api/service/JWTService.java @@ -11,5 +11,9 @@ public interface JWTService { void logout(String token, String prv) throws JwtLogoutException; + void userLogout(String token) throws JwtLogoutException; + + void adminUserLogout(String token) throws JwtLogoutException; + JWTPayload parse(String token, String prv) throws JwtLogoutException; } diff --git a/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java b/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java index 0db83a4..cc47daa 100644 --- a/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java +++ b/src/main/java/xyz/playedu/api/service/UserLoginRecordService.java @@ -12,4 +12,6 @@ public interface UserLoginRecordService extends IService { UserLoginRecord store(Integer userId, String jti, Long expired, String ip, String ipArea, String browser, String browserVersion, String os); void saveIpArea(Integer id, String area); + + void logout(Integer userid, String jti); } diff --git a/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java index 950aeb7..85c766c 100644 --- a/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/JwtServiceImpl.java @@ -7,6 +7,8 @@ import io.jsonwebtoken.security.Keys; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import xyz.playedu.api.constant.FrontendConstant; +import xyz.playedu.api.constant.SystemConstant; import xyz.playedu.api.exception.JwtLogoutException; import xyz.playedu.api.service.JWTService; import xyz.playedu.api.types.JWTPayload; @@ -82,6 +84,16 @@ public class JwtServiceImpl implements JWTService { RedisUtil.set(cacheKey, 1, expire); } + @Override + public void userLogout(String token) throws JwtLogoutException { + logout(token, SystemConstant.JWT_PRV_USER); + } + + @Override + public void adminUserLogout(String token) throws JwtLogoutException { + logout(token, SystemConstant.JWT_PRV_ADMIN_USER); + } + private Claims parseToken(String token, String prv) throws JwtLogoutException { Claims claims = (Claims) Jwts.parserBuilder().setSigningKey(getSecretKey()).require("prv", prv).build().parse(token).getBody(); if (isInBlack(claims.getId())) { diff --git a/src/main/java/xyz/playedu/api/service/impl/UserLoginRecordServiceImpl.java b/src/main/java/xyz/playedu/api/service/impl/UserLoginRecordServiceImpl.java index 7ca3489..8f2c52a 100644 --- a/src/main/java/xyz/playedu/api/service/impl/UserLoginRecordServiceImpl.java +++ b/src/main/java/xyz/playedu/api/service/impl/UserLoginRecordServiceImpl.java @@ -36,6 +36,19 @@ public class UserLoginRecordServiceImpl extends ServiceImpl