mirror of
https://github.com/PlayEdu/PlayEdu
synced 2025-07-23 09:39:33 +08:00
学员安全登出
This commit is contained in:
parent
ee248b483d
commit
ad02bcad59
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
26
src/main/java/xyz/playedu/api/event/UserLogoutEvent.java
Normal file
26
src/main/java/xyz/playedu/api/event/UserLogoutEvent.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -12,4 +12,6 @@ public interface UserLoginRecordService extends IService<UserLoginRecord> {
|
||||
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);
|
||||
}
|
||||
|
@ -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())) {
|
||||
|
@ -36,6 +36,19 @@ public class UserLoginRecordServiceImpl extends ServiceImpl<UserLoginRecordMappe
|
||||
record.setIpArea(area);
|
||||
updateById(record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(Integer userid, String jti) {
|
||||
UserLoginRecord record = getOne(query().getWrapper().eq("user_id", userid).eq("jti", jti).eq("is_logout", 0));
|
||||
if (record == null) {
|
||||
return;
|
||||
}
|
||||
UserLoginRecord newRecord = new UserLoginRecord();
|
||||
newRecord.setId(record.getId());
|
||||
newRecord.setIsLogout(1);
|
||||
|
||||
updateById(newRecord);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user