diff --git a/src/main/java/sast/evento/controller/LoginController.java b/src/main/java/sast/evento/controller/LoginController.java index eba0ef0..13c643d 100644 --- a/src/main/java/sast/evento/controller/LoginController.java +++ b/src/main/java/sast/evento/controller/LoginController.java @@ -78,15 +78,13 @@ public Map bindStudentId(@RequestParam String studentId, /** * 获取授权给新设备登录的ticket - * @param studentId 学号 * @return Map */ @OperateLog("获取ticket") @PostMapping("/login/ticket/get") @DefaultActionState(ActionState.PUBLIC) - public Map getTicket(@RequestParam String studentId, - @RequestParam(required = false) String ticket){ - return loginService.getLoginTicket(studentId,ticket); + public Map getTicket(@RequestParam(required = false) String ticket){ + return loginService.getLoginTicket(ticket); } /** @@ -99,7 +97,7 @@ public Map getTicket(@RequestParam String studentId, @DefaultActionState(ActionState.LOGIN) public String loginByTicket(@RequestParam String ticket){ UserModel user = HttpInterceptor.userHolder.get(); - loginService.checkTicket(user.getStudentId(), ticket); + loginService.checkLoginTicket(ticket,user.getId()); return "ok"; } diff --git a/src/main/java/sast/evento/service/LoginService.java b/src/main/java/sast/evento/service/LoginService.java index 8a3726c..7091cd4 100644 --- a/src/main/java/sast/evento/service/LoginService.java +++ b/src/main/java/sast/evento/service/LoginService.java @@ -1,5 +1,6 @@ package sast.evento.service; +import jakarta.annotation.Nullable; import sast.sastlink.sdk.exception.SastLinkException; import java.util.Map; @@ -15,9 +16,9 @@ public interface LoginService { Map bindStudentOnWechat(String userId, String studentId, Boolean force); - Map getLoginTicket(String studentId, String ticket); + Map getLoginTicket(String ticket); - void checkTicket(String studentId, String ticket); + void checkLoginTicket(String ticket,String userId); void bindPassword(String studentId, String password); diff --git a/src/main/java/sast/evento/service/impl/LoginServiceImpl.java b/src/main/java/sast/evento/service/impl/LoginServiceImpl.java index bb8f7c1..c2d6e3f 100644 --- a/src/main/java/sast/evento/service/impl/LoginServiceImpl.java +++ b/src/main/java/sast/evento/service/impl/LoginServiceImpl.java @@ -1,6 +1,8 @@ package sast.evento.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -53,6 +55,7 @@ public class LoginServiceImpl implements LoginService { private static final String LOGIN_TICKET = "ticket:"; private static final String LOGIN_SUCCESS = "login:"; private static final long LOGIN_TICKET_EXPIRE = 600; + private static final long LOGIN_TIME_OUT = 30; /** * 这边逻辑和业务强耦合,建议先熟悉登陆流程再阅读代码 @@ -118,43 +121,40 @@ public Map wxLogin(String code) { return Map.of("unionid", jsCodeSessionResponse.getUnionid(), "userInfo", user, "token", token); } - //未登录展示保持连接并等待(检查Ticket更改状态) @Override - public Map getLoginTicket(String studentId, String ticket) { - studentId = studentId.toLowerCase(); - if (!userMapper.exists(Wrappers.lambdaQuery(User.class) - .eq(User::getStudentId, studentId))) { - throw new LocalRunTimeException(ErrorEnum.STUDENT_NOT_BIND); + public Map getLoginTicket(@Nullable String ticket) { + if (ticket == null || ticket.isEmpty()) { + String key = TicketUtil.generateKey(); + return Map.of("expireIn", LOGIN_TICKET_EXPIRE, "ticket", generateTicket(key)); } - String local = (String) redisUtil.get(LOGIN_TICKET + studentId); - if (ticket != null && !ticket.isEmpty()) { - String userJson = (String) redisUtil.get(LOGIN_SUCCESS + studentId); - if (ticket.equals(local) && userJson != null) { - User user = JsonUtil.fromJson(userJson, User.class); - String token = addTokenInCache(user, false); - redisUtil.del(LOGIN_TICKET + studentId, LOGIN_SUCCESS + studentId); - return Map.of("token", token, "userInfo", user); - } + String key = TicketUtil.getInfoFromTicket(ticket)[0]; + String localTicket = (String) redisUtil.get(LOGIN_TICKET + key); + if (!ticket.equals(localTicket)) { + redisUtil.del(LOGIN_TICKET + key); + key = TicketUtil.generateKey(); + return Map.of("expireIn", LOGIN_TICKET_EXPIRE, "ticket", generateTicket(key)); } - if (local != null) { - return Map.of("expireIn", redisUtil.getExpire(LOGIN_TICKET + studentId), "ticket", local); + String userJson = (String) redisUtil.get(LOGIN_SUCCESS + key); + if (userJson == null || userJson.isEmpty()) { + return Map.of("expireIn", redisUtil.getExpire(LOGIN_TICKET + key), "ticket", ticket); } - ticket = TicketUtil.generateTicket(); - redisUtil.set(LOGIN_TICKET + studentId, ticket, LOGIN_TICKET_EXPIRE); - return Map.of("expireIn", LOGIN_TICKET_EXPIRE, "ticket", ticket); + User user = JsonUtil.fromJson(userJson, User.class); + String token = addTokenInCache(user, false); + redisUtil.del(LOGIN_TICKET + key, LOGIN_SUCCESS + key); + return Map.of("token", token, "userInfo", user); } - //检查Ticket更改状态 @Override - public void checkTicket(String studentId, String ticket) { - studentId = studentId.toLowerCase(); - String localTicket = (String) redisUtil.get(LOGIN_TICKET + studentId); - if (localTicket != null && localTicket.equals(ticket)) { - redisUtil.del(LOGIN_TICKET + studentId); - } + public void checkLoginTicket(String ticket, String userId) { + if (ticket == null || ticket.isEmpty()) + throw new LocalRunTimeException(ErrorEnum.LOGIN_ERROR, "ticket should not be null"); + String key = TicketUtil.getInfoFromTicket(ticket)[0]; + String localTicket = (String) redisUtil.get(LOGIN_TICKET + key); + if (!ticket.equals(localTicket)) + throw new LocalRunTimeException(ErrorEnum.LOGIN_ERROR, "error ticket please try again"); User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class) - .eq(User::getStudentId, studentId)); - redisUtil.set(LOGIN_SUCCESS + studentId, JsonUtil.toJson(user)); + .eq(User::getId, userId)); + redisUtil.set(LOGIN_SUCCESS + key, JsonUtil.toJson(user)); } @@ -278,5 +278,29 @@ private String generateToken(UserModel user) { return jwtUtil.generateToken(payload); } + private String getTicket(@Nonnull String ticket) { + String key = TicketUtil.generateKey(); + return (!checkTicket(ticket)) ? generateTicket(key) : ticket; + } + + private String generateTicket(String key) { + int times = 0; + String ticket = TicketUtil.generateTicket(key); + while (!redisUtil.setnx(LOGIN_TICKET + key, ticket, LOGIN_TICKET_EXPIRE, TimeUnit.SECONDS) && times < 5) { + key = TicketUtil.generateKey(); + ticket = TicketUtil.generateTicket(key); + times++; + } + if (times == 5) throw new LocalRunTimeException(ErrorEnum.COMMON_ERROR, "please try again later"); + return ticket; + } + + private boolean checkTicket(String ticket) { + String[] info = TicketUtil.getInfoFromTicket(ticket); + String key = info[0]; + String localTicket = (String) redisUtil.get(LOGIN_TICKET + key); + return localTicket != null && localTicket.equals(ticket); + } + } diff --git a/src/main/java/sast/evento/utils/TicketUtil.java b/src/main/java/sast/evento/utils/TicketUtil.java index c93b20d..6788496 100644 --- a/src/main/java/sast/evento/utils/TicketUtil.java +++ b/src/main/java/sast/evento/utils/TicketUtil.java @@ -4,19 +4,42 @@ public class TicketUtil { - public static String generateTicket(){ - char[] chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + - "1234567890!@#$%^&*()_+").toCharArray(); - StringBuilder sb = new StringBuilder(); - int size = 50; - for(int i = 0; i < size; i++){ - //Random().nextInt()返回值为[0,n) - char aChar = chars[ThreadLocalRandom.current().nextInt(chars.length)]; - sb.append(aChar); - } - return sb.toString(); - } + public static String generateTicket(String key, String randomStr) { + return key + "::" + System.currentTimeMillis() + "::" + randomStr; + } + + public static String generateTicket() { + return generateKey() + "::" + System.currentTimeMillis() + "::" + generateRandomStr(); + } + + public static String generateTicket(String key) { + return key + "::" + System.currentTimeMillis() + "::" + generateRandomStr(); + } + + + public static String[] getInfoFromTicket(String ticket){ + int p1 = ticket.indexOf(':'); + int p2 = ticket.lastIndexOf(':'); + return new String[]{ticket.substring(0,p1),ticket.substring(p1+2,p2-1),ticket.substring(p1+1)}; + } + + public static String generateRandomStr() { + char[] chars = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + + "1234567890!@#$%^&*()_+").toCharArray(); + StringBuilder sb = new StringBuilder(); + int size = 50; + for (int i = 0; i < size; i++) { + //Random().nextInt()返回值为[0,n) + char aChar = chars[ThreadLocalRandom.current().nextInt(chars.length)]; + sb.append(aChar); + } + return sb.toString(); + } + + public static String generateKey() { + return String.valueOf(Math.abs(ThreadLocalRandom.current().nextLong())); + } }