reghao 8 місяців тому
батько
коміт
77092458b1
31 змінених файлів з 410 додано та 382 видалено
  1. 1 1
      account/account-service/src/main/java/cn/reghao/tnb/account/app/config/web/AccountInterceptor.java
  2. 0 4
      account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountAdminController.java
  3. 1 1
      account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountRecordController.java
  4. 6 3
      account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountResourceController.java
  5. 4 1
      account/account-service/src/main/java/cn/reghao/tnb/account/app/db/mapper/LoginAttemptsMapper.java
  6. 1 0
      account/account-service/src/main/java/cn/reghao/tnb/account/app/model/constant/AccountType.java
  7. 6 5
      account/account-service/src/main/java/cn/reghao/tnb/account/app/model/po/LoginAttempts.java
  8. 7 0
      account/account-service/src/main/java/cn/reghao/tnb/account/app/redis/RedisConfig.java
  9. 22 31
      account/account-service/src/main/java/cn/reghao/tnb/account/app/redis/RedisKeys.java
  10. 2 3
      account/account-service/src/main/java/cn/reghao/tnb/account/app/rpc/AccountQueryImpl.java
  11. 11 4
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/MyAuthenticationEntryPoint.java
  12. 1 1
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/OAuth2AuthorizationConfig.java
  13. 1 1
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/OAuth2ResourceConfig.java
  14. 6 6
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/WebSecurityConfig.java
  15. 5 5
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/exception/AccountLoginException.java
  16. 2 0
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/form/AccountAuthProvider.java
  17. 2 2
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/handler/AuthFailureHandlerImpl.java
  18. 1 35
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/handler/AuthSuccessHandlerImpl.java
  19. 3 3
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/session/MySecurityContextRepository.java
  20. 8 8
      account/account-service/src/main/java/cn/reghao/tnb/account/app/security/session/RedisHttpSessionListener.java
  21. 4 4
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AccountProfileService.java
  22. 56 12
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AccountTokenService.java
  23. 3 4
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/LoginAttemptService.java
  24. 2 2
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/OAuthAppService.java
  25. 9 6
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountLoginServiceImpl.java
  26. 153 159
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountTokenServiceImpl.java
  27. 62 40
      account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/LoginAttemptServiceImpl.java
  28. 5 24
      account/account-service/src/main/java/cn/reghao/tnb/account/app/util/JwtUtil.java
  29. 8 8
      account/account-service/src/main/resources/application.yml
  30. 16 6
      account/account-service/src/main/resources/mapper/LoginAttemptsMapper.xml
  31. 2 3
      gateway/src/main/java/cn/reghao/tnb/gateway/token/GlobalTokenFilter.java

+ 1 - 1
account/account-service/src/main/java/cn/reghao/tnb/account/app/config/web/AccountInterceptor.java

@@ -37,7 +37,7 @@ public class AccountInterceptor implements HandlerInterceptor {
     public void postHandle(HttpServletRequest request, HttpServletResponse response,
                            Object handler, @Nullable ModelAndView modelAndView) throws Exception {
 
-        AccountAuthToken authToken = accountTokenService.getAuthedToken();
+        AccountAuthToken authToken = accountTokenService.getAuthToken();
         if (authToken != null) {
             long userId = authToken.getUserId();
             ServletUtil.getSession().setAttribute("userId", userId);

+ 0 - 4
account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountAdminController.java

@@ -28,10 +28,6 @@ public class AccountAdminController {
     @Operation(summary = "获取当前在线用户", description = "N")
     @GetMapping("/online_users")
     public String getOnlineUsers() {
-        long userId = 1L;
-        String keyPattern = RedisKeys.getLoginSuccessKeyPattern(userId);
-        Set<String> keys = redisOps.keys(keyPattern);
-
         return WebResult.success();
     }
 }

+ 1 - 1
account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountRecordController.java

@@ -42,7 +42,7 @@ public class AccountRecordController {
 	@Operation(summary = "注销某次登入", description = "N")
 	@PostMapping(value = "/deactivate/{loginId}", produces = MediaType.APPLICATION_JSON_VALUE)
 	public String deactivateLogin(@PathVariable("loginId") String loginId) {
-		AccountAuthToken authToken = accountTokenService.getAuthedToken();
+		AccountAuthToken authToken = accountTokenService.getAuthToken();
 		String currentLoginId = authToken.getLoginId();
 		if (loginId.equals(currentLoginId)) {
 			// 注销当前登入的设备

+ 6 - 3
account/account-service/src/main/java/cn/reghao/tnb/account/app/controller/AccountResourceController.java

@@ -6,11 +6,11 @@ import cn.reghao.tnb.account.api.dto.AccountInfo;
 import cn.reghao.tnb.account.api.dto.OAuthAccountInfo;
 import cn.reghao.tnb.account.api.iface.AccountQuery;
 import cn.reghao.tnb.account.app.db.mapper.UserAccountMapper;
+import cn.reghao.tnb.account.app.service.AccountTokenService;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.oauth2.provider.OAuth2Authentication;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,16 +25,19 @@ import org.springframework.web.bind.annotation.*;
 public class AccountResourceController {
     private final UserAccountMapper userAccountMapper;
     private final AccountQuery accountQuery;
+    private AccountTokenService accountTokenService;
 
-    public AccountResourceController(UserAccountMapper userAccountMapper, AccountQuery accountQuery) {
+    public AccountResourceController(UserAccountMapper userAccountMapper, AccountQuery accountQuery,
+                                     AccountTokenService accountTokenService) {
         this.userAccountMapper = userAccountMapper;
         this.accountQuery = accountQuery;
+        this.accountTokenService = accountTokenService;
     }
 
     @Operation(summary = "OAuth2 认证访问", description = "N")
     @GetMapping("/my/1")
     public String getUserInfo() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        Authentication authentication = accountTokenService.getAuthentication();
         if (authentication instanceof OAuth2Authentication) {
             OAuth2Authentication oauth = (OAuth2Authentication) authentication;
             String principal = (String) oauth.getUserAuthentication().getPrincipal();

+ 4 - 1
account/account-service/src/main/java/cn/reghao/tnb/account/app/db/mapper/LoginAttemptsMapper.java

@@ -3,6 +3,7 @@ package cn.reghao.tnb.account.app.db.mapper;
 import cn.reghao.jutil.jdk.db.BaseMapper;
 import cn.reghao.tnb.account.app.model.po.LoginAttempts;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -12,6 +13,8 @@ import java.util.List;
  */
 @Mapper
 public interface LoginAttemptsMapper extends BaseMapper<LoginAttempts> {
+    void updateSetFailed(String loginId);
+
     List<LoginAttempts> findByLoginIds(List<String> loginIds);
-    LoginAttempts findByLoginId(String loginId);
+    List<LoginAttempts> findByUserId(@Param("userId") long userId, @Param("pageSize") int pageSize);
 }

+ 1 - 0
account/account-service/src/main/java/cn/reghao/tnb/account/app/model/constant/AccountType.java

@@ -6,6 +6,7 @@ package cn.reghao.tnb.account.app.model.constant;
  * @author reghao
  * @date 2021-11-17 16:54:00
  */
+@Deprecated
 public enum AccountType {
     // 本系统注册用户
     tnb(1),

+ 6 - 5
account/account-service/src/main/java/cn/reghao/tnb/account/app/model/po/LoginAttempts.java

@@ -3,8 +3,6 @@ package cn.reghao.tnb.account.app.model.po;
 import cn.reghao.jutil.jdk.db.BaseObject;
 import lombok.*;
 
-import java.time.LocalDateTime;
-
 /**
  * 用户登入日志
  *
@@ -20,19 +18,22 @@ public class LoginAttempts extends BaseObject<Integer> {
     private Long userId;
     private Integer loginType;
     private Integer plat;
-    private LocalDateTime loginAt;
+    private Long loginAt;
     private String loginIp;
     private String userAgent;
     private Boolean rememberMe;
+    private Boolean success;
 
-    public LoginAttempts(String loginId, Long userId, int plat, String loginIp, int loginType, String userAgent, Boolean rememberMe) {
+    public LoginAttempts(String loginId, long userId, int plat, String loginIp,
+                         int loginType, String userAgent, boolean rememberMe) {
         this.loginId = loginId;
         this.userId = userId;
         this.loginType = loginType;
         this.plat = plat;
-        this.loginAt = LocalDateTime.now();
+        this.loginAt = System.currentTimeMillis();
         this.loginIp = loginIp;
         this.userAgent = userAgent;
         this.rememberMe = rememberMe;
+        this.success = true;
     }
 }

+ 7 - 0
account/account-service/src/main/java/cn/reghao/tnb/account/app/redis/RedisConfig.java

@@ -5,6 +5,7 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.RedisSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
 /**
@@ -19,7 +20,13 @@ public class RedisConfig extends CachingConfigurerSupport {
     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
         RedisTemplate<String, Object> template = new RedisTemplate<>();
         template.setConnectionFactory(factory);
+
         template.setKeySerializer(new StringRedisSerializer());
+        //template.setValueSerializer(RedisSerializer.string());
+        //template.setValueSerializer(RedisSerializer.java());
+
+        template.setHashKeySerializer(RedisSerializer.string());
+        template.setValueSerializer(RedisSerializer.java());
         return template;
     }
 }

+ 22 - 31
account/account-service/src/main/java/cn/reghao/tnb/account/app/redis/RedisKeys.java

@@ -11,10 +11,6 @@ public class RedisKeys {
         return String.format("tnb:auth:jwt:rsa:%s", key);
     }
 
-    public static String getJwtBlacklistKey(String key) {
-        return String.format("tnb:auth:jwt:blacklist:%s", key);
-    }
-
     public static String getCaptchaKey() {
         String sessionId = ServletUtil.getSessionId();
         return String.format("tnb:account:code:%s:captcha", sessionId);
@@ -38,51 +34,46 @@ public class RedisKeys {
         return String.format("tnb:account:rsa:prikey");
     }
 
-    public static String getLoginSuccessKey(long userId, int plat, String loginId) {
-        return String.format("tnb:account:login:success:%s:%s:%s", userId, plat, loginId);
+    public static String getLoginFailedCountKey(long userId) {
+        return String.format("tnb:auth:login:failed:%s:count", userId);
     }
 
-    public static String getLoginSuccessKey(String sessId) {
-        return String.format("tnb:account:login:success:%s", sessId);
+    public static String getLoginFailedLockKey(long userId) {
+        return String.format("tnb:auth:login:failed:%s:lock", userId);
     }
 
-    public static String getLoginSuccessKeyPattern(long userId, Integer plat) {
-        return String.format("tnb:account:login:success:%s:%s:*", userId, plat);
+    public static String getLoginSuccessKeyPattern(long userId) {
+        return String.format("tnb:auth:login:success:%s:*", userId);
     }
 
-    public static String getLoginSuccessKeyPattern(long userId) {
-        return String.format("tnb:account:login:success:%s:*", userId);
+    public static String getAuthTokenKey(long userId, String loginId) {
+        return String.format("tnb:auth:login:success:%s:%s:auth_token", userId, loginId);
     }
 
-    public static String getLoginFailedCountKey(String userId, int plat) {
-        return String.format("tnb:account:login:failed:count:%s:%s", userId, plat);
+    public static String getAccessTokenKey(long userId, String loginId) {
+        return String.format("tnb:auth:login:success:%s:%s:access_token", userId, loginId);
     }
 
-    public static String getLoginFailedLockKey(String userId, int plat) {
-        return String.format("tnb:account:login:failed:lock:%s:%s", userId, plat);
+    public static String getRefreshTokenKey(long userId, String loginId) {
+        return String.format("tnb:auth:login:success:%s:%s:refresh_token", userId, loginId);
     }
 
     public static String getAccessSignKeyKey(String accessToken) {
-        return String.format("tnb:account:login:signkey:access:%s", accessToken);
+        return String.format("tnb:auth:jwt:sign_key:access:%s", accessToken);
     }
 
     public static String getRefreshSignKeyKey(String refreshToken) {
-        return String.format("tnb:account:login:signkey:refresh:%s", refreshToken);
-    }
-
-    public static String getAccessTokenKey(String userId, String loginId) {
-        return String.format("tnb:account:login:%s:accesstoken:%s", userId, loginId);
-    }
-
-    public static String getRefreshTokenKey(String userId, String loginId) {
-        return String.format("tnb:account:login:%s:refreshtoken:%s", userId, loginId);
-    }
-
-    public static String getAuthTokenKey(String userId, Integer plat, String loginId) {
-        return String.format("tnb:account:login:%s:authtoken:%s:%s", userId, plat, loginId);
+        return String.format("tnb:auth:jwt:sign_key:refresh:%s", refreshToken);
     }
 
+    /**
+     * application.yml 配置文件中
+     *
+     * @param
+     * @return
+     * @date 2025-08-28 10:30:42
+     */
     public static String getRedisSessionKey(String loginId) {
-        return String.format("tnb:account:session:sessions:%s:*", loginId);
+        return String.format("tnb:auth:session:sessions:%s:*", loginId);
     }
 }

+ 2 - 3
account/account-service/src/main/java/cn/reghao/tnb/account/app/rpc/AccountQueryImpl.java

@@ -40,9 +40,8 @@ public class AccountQueryImpl implements AccountQuery {
 
     @Override
     public AuthedAccount getAuthedAccount(int type, String sessId) {
-        AccountInfo accountInfo = accountTokenService.getAccountInfo(type, sessId);
-        if (accountInfo != null) {
-            long userId = accountInfo.getUserId();
+        long userId = accountTokenService.getUserId(type, sessId);
+        if (userId != -1L) {
             UserAccount userAccount = accountRepository.getUserAccountByUserId(userId);
             String userIdStr = userIdObfuscation.obfuscate(userId);
             Set<String> roles = userAccount.getAuthorities().stream()

+ 11 - 4
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/ExceptionAuthenticationEntryPoint.java → account/account-service/src/main/java/cn/reghao/tnb/account/app/security/MyAuthenticationEntryPoint.java

@@ -19,16 +19,16 @@ import java.io.PrintWriter;
  * @date 2023-07-28 16:43:59
  */
 @Slf4j
-public class ExceptionAuthenticationEntryPoint implements AuthenticationEntryPoint {
+public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
     @Override
     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
             throws IOException, ServletException {
         String uri = request.getRequestURI();
         log.error("请求 {} 接口认证失败...", uri);
-        String body = String.format("%s, %s need authenticate", exception.getMessage(), uri);
 
-        String xForwardedHost = request.getHeader("x-forwarded-host");
+        /*String xForwardedHost = request.getHeader("x-forwarded-host");
         if (xForwardedHost != null && xForwardedHost.startsWith("api")) {
+            String body = String.format("%s, %s need authenticate", exception.getMessage(), uri);
             String retJson = WebResult.failWithMsg(body);
             response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
             response.setContentType("application/json; charset=utf-8");
@@ -36,6 +36,13 @@ public class ExceptionAuthenticationEntryPoint implements AuthenticationEntryPoi
             printWriter.write(retJson);
         } else {
             response.sendRedirect("/login");
-        }
+        }*/
+
+        String body = String.format("%s, %s need authenticate", exception.getMessage(), uri);
+        String retJson = WebResult.failWithMsg(body);
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        response.setContentType("application/json; charset=utf-8");
+        PrintWriter printWriter = response.getWriter();
+        printWriter.write(retJson);
     }
 }

+ 1 - 1
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/OAuth2AuthorizationConfig.java

@@ -94,7 +94,7 @@ public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdap
                 .tokenKeyAccess("permitAll()")
                 .checkTokenAccess("isAuthenticated()");
 
-        security.authenticationEntryPoint(new ExceptionAuthenticationEntryPoint());
+        security.authenticationEntryPoint(new MyAuthenticationEntryPoint());
         //security.realm("qq");
         // 允许客户端进行表单认证
         security.allowFormAuthenticationForClients();

+ 1 - 1
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/OAuth2ResourceConfig.java

@@ -33,6 +33,6 @@ public class OAuth2ResourceConfig extends ResourceServerConfigurerAdapter {
                 .antMatchers("/api/account/resource/user/**").access("#oauth2.hasScope('get_user_info')");
 
         http.exceptionHandling()
-                .authenticationEntryPoint(new ExceptionAuthenticationEntryPoint());
+                .authenticationEntryPoint(new MyAuthenticationEntryPoint());
     }
 }

+ 6 - 6
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/WebSecurityConfig.java

@@ -4,7 +4,7 @@ import cn.reghao.tnb.account.app.security.filter.LoginRedirectFilter;
 import cn.reghao.tnb.account.app.security.form.ThirdPartyAuthFilter;
 import cn.reghao.tnb.account.app.security.form.AccountAuthFilter;
 import cn.reghao.tnb.account.app.security.form.AccountAuthProvider;
-import cn.reghao.tnb.account.app.security.session.CookieSecurityContextRepository;
+import cn.reghao.tnb.account.app.security.session.MySecurityContextRepository;
 import cn.reghao.tnb.account.app.security.session.MySessionAuthenticationStrategy;
 import cn.reghao.tnb.account.app.service.AccountLoginService;
 import org.springframework.context.annotation.Bean;
@@ -38,7 +38,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     private final String loginApi = "/api/auth/signin";
     private final String logoutApi = "/api/auth/signout";
 
-    private final CookieSecurityContextRepository cookieSecurityContextRepository;
+    private final MySecurityContextRepository mySecurityContextRepository;
     private final AccountAuthProvider userAuthProvider;
     private final AccountLoginService accountLoginService;
     private final AuthenticationSuccessHandler successHandler;
@@ -46,12 +46,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     private final LogoutHandler logoutHandler;
     private final LogoutSuccessHandler logoutSuccessHandler;
 
-    public WebSecurityConfig(CookieSecurityContextRepository cookieSecurityContextRepository,
+    public WebSecurityConfig(MySecurityContextRepository mySecurityContextRepository,
                              AccountAuthProvider userAuthProvider,
                              AccountLoginService accountLoginService, AuthenticationSuccessHandler successHandler,
                              AuthenticationFailureHandler failureHandler, LogoutHandler logoutHandler,
                              LogoutSuccessHandler logoutSuccessHandler) {
-        this.cookieSecurityContextRepository = cookieSecurityContextRepository;
+        this.mySecurityContextRepository = mySecurityContextRepository;
         this.userAuthProvider = userAuthProvider;
         this.accountLoginService = accountLoginService;
         this.successHandler = successHandler;
@@ -90,7 +90,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
         http.addFilterBefore(thirdPartyAuthFilter(), UsernamePasswordAuthenticationFilter.class);
 
         // 自定义 SecurityContextPersistenceFilter 中使用的 SecurityContextRepository
-        http.securityContext().securityContextRepository(cookieSecurityContextRepository);
+        http.securityContext().securityContextRepository(mySecurityContextRepository);
 
         // 配置 UsernamePasswordAuthenticationFilter, 禁用 UsernamePasswordAuthenticationFilter
         http.formLogin().disable();
@@ -106,7 +106,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
         // 配置 ExceptionTranslationFilter, 登入认证接口失败时的处理, 不会重定向到 loginPage
         http.exceptionHandling()
-                .authenticationEntryPoint(new ExceptionAuthenticationEntryPoint());
+                .authenticationEntryPoint(new MyAuthenticationEntryPoint());
 
         // 配置 SessionManagementFilter 和 ConcurrentSessionFilter
         http.sessionManagement()

+ 5 - 5
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/exception/AccountLoginException.java

@@ -8,19 +8,19 @@ import org.springframework.security.core.AuthenticationException;
  */
 public class AccountLoginException extends AuthenticationException {
     private final long userId;
-    private final int plat;
+    private final String loginId;
 
-    public AccountLoginException(String msg, long userId, int plat) {
+    public AccountLoginException(String msg, long userId, String loginId) {
         super(msg);
         this.userId = userId;
-        this.plat = plat;
+        this.loginId = loginId;
     }
 
     public long getUserId() {
         return userId;
     }
 
-    public int getPlat() {
-        return plat;
+    public String getLoginId() {
+        return loginId;
     }
 }

+ 2 - 0
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/form/AccountAuthProvider.java

@@ -9,6 +9,8 @@ import org.springframework.security.core.AuthenticationException;
 import org.springframework.stereotype.Component;
 
 /**
+ * 对用户登入进行认证
+ *
  * @author reghao
  * @date 2022-07-08 14:57:40
  */

+ 2 - 2
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/handler/AuthFailureHandlerImpl.java

@@ -35,8 +35,8 @@ public class AuthFailureHandlerImpl implements AuthenticationFailureHandler {
         if (exception instanceof AccountLoginException) {
             AccountLoginException ex = (AccountLoginException) exception;
             long userId = ex.getUserId();
-            int plat = ex.getPlat();
-            Result result = loginAttemptService.loginFailedCount(userId, plat);
+            String loginId = ex.getLoginId();
+            Result result = loginAttemptService.loginFailedCount(userId, loginId);
             if (result.getCode() != 0) {
                 errMsg = result.getMsg();
             }

+ 1 - 35
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/handler/AuthSuccessHandlerImpl.java

@@ -1,23 +1,15 @@
 package cn.reghao.tnb.account.app.security.handler;
 
-import cn.reghao.jutil.jdk.converter.DateTimeConverter;
 import cn.reghao.jutil.web.WebResult;
 import cn.reghao.jutil.web.ServletUtil;
 import cn.reghao.tnb.account.api.iface.AccountQuery;
 import cn.reghao.tnb.account.app.db.repository.AccountRepository;
-import cn.reghao.tnb.account.app.util.RabbitProducer;
 import cn.reghao.tnb.account.app.model.constant.LoginPlat;
 import cn.reghao.tnb.account.api.dto.AccountInfo;
 import cn.reghao.tnb.account.app.model.dto.AccountLoginRet;
 import cn.reghao.tnb.account.app.model.dto.AccountToken;
-import cn.reghao.tnb.account.app.db.mapper.UserAccountMapper;
-import cn.reghao.tnb.account.app.model.po.LoginAttempts;
-import cn.reghao.tnb.account.app.model.po.UserAccount;
 import cn.reghao.tnb.account.app.security.form.AccountAuthToken;
 import cn.reghao.tnb.account.app.service.AccountTokenService;
-import cn.reghao.tnb.account.app.service.LoginAttemptService;
-import cn.reghao.tnb.message.api.constant.NotifyType;
-import cn.reghao.tnb.message.api.dto.msg.LoginMessage;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
 import org.springframework.security.web.savedrequest.SavedRequest;
@@ -28,7 +20,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.time.LocalDateTime;
 
 /**
  * 认证成功后的处理
@@ -38,17 +29,12 @@ import java.time.LocalDateTime;
  */
 @Component
 public class AuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
-    private final RabbitProducer rabbitProducer;
-    private final LoginAttemptService loginAttemptService;
     private final AccountRepository accountRepository;
     private final AccountTokenService accountTokenService;
     private final AccountQuery accountQuery;
 
-    public AuthSuccessHandlerImpl(RabbitProducer rabbitProducer, LoginAttemptService loginAttemptService,
-                                  AccountRepository accountRepository, AccountTokenService accountTokenService,
+    public AuthSuccessHandlerImpl(AccountRepository accountRepository, AccountTokenService accountTokenService,
                                   AccountQuery accountQuery) {
-        this.rabbitProducer = rabbitProducer;
-        this.loginAttemptService = loginAttemptService;
         this.accountRepository = accountRepository;
         this.accountTokenService = accountTokenService;
         this.accountQuery = accountQuery;
@@ -58,7 +44,6 @@ public class AuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth)
             throws IOException, ServletException {
         AccountAuthToken authToken = (AccountAuthToken) auth;
-        loginAttemptService.saveSuccessLogin(authToken);
         loginSuccess(authToken, response);
     }
 
@@ -69,25 +54,6 @@ public class AuthSuccessHandlerImpl implements AuthenticationSuccessHandler {
         int plat = authToken.getPlat();
         boolean rememberMe = authToken.isRememberMe();
         String redirectPath = getRedirectPath();
-
-        /*UserAccount userAccount = accountRepository.getUserAccountByUserId(userId);
-        if (userAccount.getNotify()) {
-            int notifyType = NotifyType.email.getValue();
-            String receiver = userAccount.getEmail();
-            if (receiver == null) {
-                notifyType = NotifyType.mobile.getValue();
-                receiver = userAccount.getMobile();
-            }
-
-            LoginAttempts loginAttempts = loginAttemptService.getLoginAttempts(loginId);
-            String userAgent = loginAttempts.getUserAgent();
-            String publicIp = loginAttempts.getLoginIp();
-            LocalDateTime localDateTime = loginAttempts.getLoginAt();
-            String loginAt = DateTimeConverter.format(localDateTime);
-            LoginMessage loginMessage = new LoginMessage(notifyType, receiver, publicIp, userAgent, loginAt);
-            rabbitProducer.sendLoginMessage(loginMessage);
-        }*/
-
         long timeout = rememberMe ? 3600*24*30 : 0;
         String uri = ServletUtil.getRequest().getRequestURI();
         if (uri.startsWith("/oauth/redirect")) {

+ 3 - 3
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/session/CookieSecurityContextRepository.java → account/account-service/src/main/java/cn/reghao/tnb/account/app/security/session/MySecurityContextRepository.java

@@ -21,11 +21,11 @@ import javax.servlet.http.HttpServletResponse;
  * @date 2023-08-11 10:20:17
  */
 @Component
-public class CookieSecurityContextRepository implements SecurityContextRepository {
+public class MySecurityContextRepository implements SecurityContextRepository {
     private final String cookieName = "USERDATA";
     private final AccountTokenService accountTokenService;
 
-    public CookieSecurityContextRepository(AccountTokenService accountTokenService) {
+    public MySecurityContextRepository(AccountTokenService accountTokenService) {
         this.accountTokenService = accountTokenService;
     }
 
@@ -34,7 +34,6 @@ public class CookieSecurityContextRepository implements SecurityContextRepositor
         HttpServletRequest request = requestResponseHolder.getRequest();
         String token = ServletUtil.getBearerToken(request);
         SecurityContext context = null;
-
         if (token != null) {
             context = readSecurityContext(TokenType.token.getValue(), token);
         } else {
@@ -60,6 +59,7 @@ public class CookieSecurityContextRepository implements SecurityContextRepositor
             return context;
         } else {
             if (tokenType == TokenType.cookie.getValue()) {
+                // 没有认证信息时重置 response 中返回的 cookie
                 accountTokenService.clearCookie();
             }
             return null;

+ 8 - 8
account/account-service/src/main/java/cn/reghao/tnb/account/app/security/session/RedisHttpSessionListener.java

@@ -25,21 +25,21 @@ public class RedisHttpSessionListener {
 
     @EventListener
     public void onCreated(SessionCreatedEvent event) {
-        String loginId = event.getSessionId();
-        log.info("创建 loginId {}", loginId);
+        String sessionId = event.getSessionId();
+        log.info("创建 sessionId {}", sessionId);
     }
 
     @EventListener
     public void onDeleted(SessionDeletedEvent event) {
-        String loginId = event.getSessionId();
-        accountTokenService.deleteOnSessionExpired(loginId);
-        log.info("删除 loginId {}", loginId);
+        String sessionId = event.getSessionId();
+        accountTokenService.deleteOnSessionExpired(sessionId);
+        log.info("删除 sessionId {}", sessionId);
     }
 
     @EventListener
     public void onExpired(SessionExpiredEvent event) {
-        String loginId = event.getSessionId();
-        accountTokenService.deleteOnSessionExpired(loginId);
-        log.info("过期 loginId {}", loginId);
+        String sessionId = event.getSessionId();
+        accountTokenService.deleteOnSessionExpired(sessionId);
+        log.info("过期 sessionId {}", sessionId);
     }
 }

+ 4 - 4
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AccountProfileService.java

@@ -33,7 +33,7 @@ public class AccountProfileService {
     }
 
     public Result updateUserScreenName(String screenName) {
-        AccountAuthToken authToken = accountTokenService.getAuthedToken();
+        AccountAuthToken authToken = accountTokenService.getAuthToken();
         long userId = authToken.getUserId();
         userAccountMapper.updateUserScreenName(userId, screenName);
         return Result.success();
@@ -47,7 +47,7 @@ public class AccountProfileService {
             return Result.fail("error verify code");
         }
 
-        long loginUser = accountTokenService.getAuthedToken().getUserId();
+        long loginUser = accountTokenService.getAuthToken().getUserId();
         userAccountMapper.updateUserMobile(loginUser, mobile);
         return Result.success();
     }
@@ -61,7 +61,7 @@ public class AccountProfileService {
         }
 
         String password = emailUpdate.getPassword();
-        long loginUser = accountTokenService.getAuthedToken().getUserId();
+        long loginUser = accountTokenService.getAuthToken().getUserId();
         userAccountMapper.updateUserEmail(loginUser, email);
         return Result.success();
     }
@@ -74,7 +74,7 @@ public class AccountProfileService {
      * @date 2023-02-20 11:48:44
      */
     public Result updatePassword(PasswordUpdateDto passwordUpdateDto) {
-        AccountAuthToken authToken = accountTokenService.getAuthedToken();
+        AccountAuthToken authToken = accountTokenService.getAuthToken();
         long userId = authToken.getUserId();
 
         UserAccount userAccount = userAccountMapper.findByUserId(userId);

+ 56 - 12
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/AccountTokenService.java

@@ -1,24 +1,30 @@
 package cn.reghao.tnb.account.app.service;
 
-import cn.reghao.tnb.account.api.dto.AccountInfo;
 import cn.reghao.tnb.account.app.model.dto.AccountToken;
 import cn.reghao.tnb.account.app.security.form.AccountAuthToken;
+import org.springframework.security.core.Authentication;
 
 /**
  * @author reghao
  * @date 2023-02-16 14:57:49
  */
 public interface AccountTokenService {
-    AccountInfo getAccountInfo(int tokenType, String userdata);
-    AccountAuthToken getAuthedToken();
     /**
-     * 从 cookie 或 token 中读取已认证用户的 AccountAuthToken
+     * 颁发用户令牌
      *
      * @param
      * @return
-     * @date 2023-10-19 18:38:43
+     * @date 2023-02-17 14:29:32
      */
-    AccountAuthToken getAuthToken(int tokenType, String userdata);
+    AccountToken grantUserToken(AccountAuthToken authToken);
+    /**
+     * 设置用户 cookie
+     *
+     * @param
+     * @return
+     * @date 2025-08-28 14:54:02
+     */
+    void setCookie(AccountAuthToken authToken, long timeout);
     /**
      * 使用刷新令牌颁发新令牌
      *
@@ -29,25 +35,63 @@ public interface AccountTokenService {
     AccountToken getUserToken(String refreshToken) throws Exception;
 
     /**
-     * 颁发用户令牌
+     * 根据 token 获取已认证用户的 ID
      *
      * @param
      * @return
-     * @date 2023-02-17 14:29:32
+     * @date 2025-08-28 15:16:58
      */
-    AccountToken grantUserToken(AccountAuthToken authToken);
-    void setCookie(AccountAuthToken authToken, long timeout);
-    void clearCookie();
+    long getUserId(int tokenType, String userdata);
 
     /**
-     * 删除与与用户关联的所有设备的登入数据
+     * 从 SecurityContextHolder 中获取 Authentication
+     *
+     * @param
+     * @return
+     * @date 2025-08-28 15:09:20
+     */
+    Authentication getAuthentication();
+    /**
+     * 从 SecurityContextHolder 中获取 AccountAuthToken
+     *
+     * @param
+     * @return
+     * @date 2025-08-28 14:52:39
+     */
+    AccountAuthToken getAuthToken();
+    /**
+     * 从 cookie 或 token 中读取已认证用户的 AccountAuthToken
+     *
+     * @param
+     * @return
+     * @date 2023-10-19 18:38:43
+     */
+    AccountAuthToken getAuthToken(int tokenType, String userdata);
+
+    void clearCookie();
+    /**
+     * 重置密码后, 删除所有与用户关联认证数据
      *
      * @param
      * @return
      * @date 2023-02-20 14:30:44
      */
     void logout(long userId);
+    /**
+     * 登出其他设备
+     *
+     * @param
+     * @return
+     * @date 2025-08-28 16:29:25
+     */
     void logout(String loginId);
+    /**
+     * 登出当前设备
+     *
+     * @param
+     * @return
+     * @date 2025-08-28 16:29:15
+     */
     void logout();
     void deleteOnSessionExpired(String loginId);
 }

+ 3 - 4
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/LoginAttemptService.java

@@ -12,9 +12,8 @@ import java.util.List;
  * @date 2022-07-08 10:37:49
  */
 public interface LoginAttemptService {
-    void check(long userId, int plat);
-    Result loginFailedCount(long userId, int plat);
-    void saveSuccessLogin(AccountAuthToken authToken);
+    void loginCheck(long userId);
+    Result loginFailedCount(long userId, String loginId);
+    void saveLoginAttempts(AccountAuthToken authToken);
     List<LoginRecordVo> getActiveLogin();
-    LoginAttempts getLoginAttempts(String loginId);
 }

+ 2 - 2
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/OAuthAppService.java

@@ -32,7 +32,7 @@ public class OAuthAppService {
 
     @Transactional(rollbackFor = Exception.class)
     public void create(OAuthAppDto oAuthAppDto) {
-        AccountAuthToken authToken = accountTokenService.getAuthedToken();
+        AccountAuthToken authToken = accountTokenService.getAuthToken();
         long userId = authToken.getUserId();
 
         String appName = oAuthAppDto.getName();
@@ -63,7 +63,7 @@ public class OAuthAppService {
     }
 
     public List<ClientDetails> getAll() {
-        long loginUser = accountTokenService.getAuthedToken().getUserId();
+        long loginUser = accountTokenService.getAuthToken().getUserId();
         return clientDetailsMapper.findByOwner(loginUser);
     }
 

+ 9 - 6
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountLoginServiceImpl.java

@@ -70,7 +70,9 @@ public class AccountLoginServiceImpl implements AccountLoginService {
         }
 
         String loginId = UUID.randomUUID().toString().replace("-", "");
-        return new AccountAuthToken(plat, loginId, loginType, rememberMe, principal, decryptCredential);
+        AccountAuthToken accountAuthToken = new AccountAuthToken(plat, loginId, loginType, rememberMe, principal, decryptCredential);
+        loginAttemptService.saveLoginAttempts(accountAuthToken);
+        return accountAuthToken;
     }
 
     @Override
@@ -96,14 +98,14 @@ public class AccountLoginServiceImpl implements AccountLoginService {
         String username = (String) authToken.getPrincipal();
         UserAccount userDetail = (UserAccount) loadUserByUsername(username);
         long userId = userDetail.getUserId();
-        int plat = authToken.getPlat();
-        loginAttemptService.check(userId, plat);
+        String loginId = authToken.getLoginId();
+        loginAttemptService.loginCheck(userId);
 
         String password = (String) authToken.getCredentials();
         String encodedPassword = passwordEncoder.encode(password + userDetail.getSalt());
         if (!userDetail.getPassword().equals(encodedPassword)) {
             String errMsg = "账号或密码不正确";
-            throw new AccountLoginException(errMsg, userId, plat);
+            throw new AccountLoginException(errMsg, userId, loginId);
         }
 
         userDetail.setLoginType(LoginType.usernamePassword.getValue());
@@ -134,8 +136,9 @@ public class AccountLoginServiceImpl implements AccountLoginService {
                 String errMsg = "短信验证码不正确";
                 if (userDetail != null) {
                     long userId = userDetail.getUserId();
-                    loginAttemptService.check(userId, plat);
-                    throw new AccountLoginException(errMsg, userId, plat);
+                    String loginId = authToken.getLoginId();
+                    loginAttemptService.loginCheck(userId);
+                    throw new AccountLoginException(errMsg, userId, loginId);
                 }
 
                 throw new DisabledException(errMsg);

+ 153 - 159
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/AccountTokenServiceImpl.java

@@ -2,14 +2,11 @@ package cn.reghao.tnb.account.app.service.impl;
 
 import cn.reghao.jutil.jdk.security.RandomString;
 import cn.reghao.jutil.jdk.security.RsaCryptor;
-import cn.reghao.jutil.jdk.string.IDObfuscation;
 import cn.reghao.tnb.account.api.constant.TokenType;
 import cn.reghao.tnb.account.app.db.mapper.LoginAttemptsMapper;
 import cn.reghao.tnb.account.app.model.constant.LoginPlat;
-import cn.reghao.tnb.account.api.dto.AccountInfo;
 import cn.reghao.tnb.account.app.model.po.LoginAttempts;
 import cn.reghao.tnb.account.app.service.AccountTokenService;
-import cn.reghao.tnb.account.app.db.mapper.UserAccountMapper;
 import cn.reghao.tnb.account.app.redis.ds.RedisOps;
 import cn.reghao.jutil.web.ServletUtil;
 import cn.reghao.tnb.account.app.model.dto.AccountToken;
@@ -20,6 +17,7 @@ import cn.reghao.tnb.account.app.redis.RedisKeys;
 import cn.reghao.tnb.account.app.model.vo.RefreshPayload;
 import cn.reghao.tnb.account.app.redis.ds.RedisString;
 import cn.reghao.tnb.account.app.redis.ds.RedisStringObject;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.autoconfigure.web.ServerProperties;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContext;
@@ -33,12 +31,14 @@ import javax.servlet.http.HttpServletRequest;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.util.List;
-import java.util.Set;
+import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * @author reghao
  * @date 2023-02-16 14:57:49
  */
+@Slf4j
 @Service
 public class AccountTokenServiceImpl implements AccountTokenService {
     private final String cookieName = "USERDATA";
@@ -47,125 +47,36 @@ public class AccountTokenServiceImpl implements AccountTokenService {
     private final RedisOps redisOps;
     private final RedisString redisString;
     private final RedisStringObject redisStringObject;
-    private final UserAccountMapper userAccountMapper;
     private final LoginAttemptsMapper loginAttemptsMapper;
     private final PubkeyService pubkeyService;
-    private final IDObfuscation idObfuscation;
 
     public AccountTokenServiceImpl(RedisOps redisOps, RedisString redisString, RedisStringObject redisStringObject,
-                                   UserAccountMapper userAccountMapper, LoginAttemptsMapper loginAttemptsMapper,
-                                   ServerProperties serverProperties, PubkeyService pubkeyService,
-                                   IDObfuscation idObfuscation) {
+                                   LoginAttemptsMapper loginAttemptsMapper, ServerProperties serverProperties,
+                                   PubkeyService pubkeyService) {
         long sessionTimeout = serverProperties.getServlet().getSession().getTimeout().getSeconds();
         this.redisOps = redisOps;
         this.redisString = redisString;
         this.redisStringObject = redisStringObject;
-        this.userAccountMapper = userAccountMapper;
         this.loginAttemptsMapper = loginAttemptsMapper;
         this.pubkeyService = pubkeyService;
-        this.idObfuscation = idObfuscation;
-    }
-
-    @Override
-    public AccountInfo getAccountInfo(int type, String userdata) {
-        AccountAuthToken userAuthToken = getAuthToken(type, userdata);
-        if (userAuthToken != null) {
-            long userId = userAuthToken.getUserId();
-            return userAccountMapper.findAccountInfo(userId);
-        }
-
-        return null;
-    }
-
-    @Override
-    public AccountAuthToken getAuthedToken() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        if (authentication instanceof AccountAuthToken) {
-            return (AccountAuthToken) SecurityContextHolder.getContext().getAuthentication();
-        }
-
-        return null;
-    }
-
-    @Override
-    public AccountAuthToken getAuthToken(int type, String userdata) {
-        AccountAuthToken authToken = null;
-        if (type == TokenType.cookie.getValue()) {
-            authToken = getFromCookie(userdata);
-        } else if (type == TokenType.token.getValue()) {
-            authToken = getFromToken(userdata);
-        }
-
-        return authToken;
-    }
-
-    private AccountAuthToken getFromCookie(String userdata) {
-        String redisKey = RedisKeys.getLoginSuccessKey(userdata);
-        Object object = redisStringObject.get(redisKey);
-        if (object != null) {
-            return (AccountAuthToken) object;
-        }
-
-        return null;
-    }
-
-    private AccountAuthToken getFromToken(String token) {
-        String savedSignKey = redisString.get(RedisKeys.getJwtSignKey("pubkey"));
-        RSAPublicKey rsaPublicKey = RsaCryptor.getRSAPublicKey(savedSignKey);
-        try {
-            AccountAuthToken userAuthToken = JwtUtil.getAuthentication1(token, rsaPublicKey, idObfuscation);
-            if (userAuthToken != null) {
-                return userAuthToken;
-            }
-            redisOps.del(savedSignKey);
-        } catch (Exception ignore) {
-            redisOps.del(savedSignKey);
-        }
-
-        return null;
-    }
-
-    @Override
-    public AccountToken getUserToken(String refreshToken) throws Exception {
-        String signKey = redisString.get(RedisKeys.getRefreshSignKeyKey(refreshToken));
-        if (signKey == null) {
-            throw new Exception("refresh-token 已过期");
-        }
-
-        RefreshPayload refreshPayload = JwtUtil.parseRefreshToken(refreshToken, signKey);
-        int plat = refreshPayload.getPlat();
-        String loginId = refreshPayload.getLoginId();
-        long userId = refreshPayload.getUserId();
-        String userIdStr = idObfuscation.obfuscate(userId);
-        Object object = redisStringObject.get(RedisKeys.getAuthTokenKey(userIdStr, plat, loginId));
-        AccountAuthToken authToken = (AccountAuthToken) object;
-
-        AccountToken accountToken = grantUserToken(authToken);
-        String accessToken = ServletUtil.getBearerToken();
-        if (accessToken != null) {
-            redisOps.del(RedisKeys.getAccessSignKeyKey(accessToken));
-        }
-        redisOps.del(RedisKeys.getRefreshSignKeyKey(refreshToken));
-        return accountToken;
     }
 
     @Override
     public AccountToken grantUserToken(AccountAuthToken authToken) {
         long userId = authToken.getUserId();
-        String userIdStr = idObfuscation.obfuscate(userId);
-        int plat = authToken.getPlat();
         long accessExpireIn = 1000L*3600*24*7;
         long accessExpireAt = System.currentTimeMillis() + accessExpireIn;
-        String accessSignKey = RandomString.getSalt(64);
         String accessToken = "";
         try {
             RSAPrivateKey privateKey = pubkeyService.getPrivateKey();
-            accessToken = JwtUtil.createAccessToken(authToken, userIdStr, accessExpireAt, privateKey);
-        } catch (Exception ignore) {
+            accessToken = JwtUtil.createAccessToken(authToken, userId, accessExpireAt, privateKey);
+        } catch (Exception e) {
+            log.error("error -> {}", e.getMessage());
         }
 
+        String accessSignKey = RandomString.getSalt(64);
         if (accessToken.isBlank()) {
-            accessToken = JwtUtil.createAccessToken(authToken, userIdStr, accessExpireAt, accessSignKey);
+            accessToken = JwtUtil.createAccessToken(authToken, userId, accessExpireAt, accessSignKey);
         }
 
         long refreshExpireIn = accessExpireIn*4;
@@ -173,16 +84,20 @@ public class AccountTokenServiceImpl implements AccountTokenService {
         String refreshSignKey = RandomString.getSalt(64);
         String refreshToken = JwtUtil.createRefreshToken(authToken, refreshExpireAt, refreshSignKey);
 
-        redisString.setWithTimeout(RedisKeys.getAccessSignKeyKey(accessToken), accessSignKey, accessExpireIn);
-        redisString.setWithTimeout(RedisKeys.getRefreshSignKeyKey(refreshToken), refreshSignKey, refreshExpireIn);
+        redisStringObject.setWithTimeout(RedisKeys.getAccessSignKeyKey(accessToken), accessSignKey, accessExpireIn);
+        redisStringObject.setWithTimeout(RedisKeys.getRefreshSignKeyKey(refreshToken), refreshSignKey, refreshExpireIn);
 
         String loginId = authToken.getLoginId();
-        redisStringObject.setWithTimeout(RedisKeys.getAuthTokenKey(userIdStr, plat, loginId), authToken, refreshExpireIn);
-        redisString.setWithTimeout(RedisKeys.getAccessTokenKey(userIdStr, loginId), accessToken, accessExpireIn);
-        redisString.setWithTimeout(RedisKeys.getRefreshTokenKey(userIdStr, loginId), refreshToken, refreshExpireIn);
+        redisStringObject.setWithTimeout(RedisKeys.getAuthTokenKey(userId, loginId), authToken, accessExpireIn);
+        redisStringObject.setWithTimeout(RedisKeys.getAccessTokenKey(userId, loginId), accessToken, accessExpireIn);
+        redisStringObject.setWithTimeout(RedisKeys.getRefreshTokenKey(userId, loginId), refreshToken, refreshExpireIn);
         return new AccountToken(accessToken, accessExpireAt, refreshToken, refreshExpireAt);
     }
 
+    public void refreshToken(long userId, String loginId) {
+
+    }
+
     @Override
     public void setCookie(AccountAuthToken authToken, long timeout) {
         long userId = authToken.getUserId();
@@ -192,12 +107,6 @@ public class AccountTokenServiceImpl implements AccountTokenService {
         String userdata = String.format("%s:%s:%s", userId, plat, loginId);
         Cookie cookie3 = generateCookie(cookieName, userdata, timeout);
         ServletUtil.getResponse().addCookie(cookie3);
-
-        String loginSuccessKey = RedisKeys.getLoginSuccessKey(userId, plat, loginId);
-        if (timeout == 0) {
-            timeout = 3600*24*30;
-        }
-        redisStringObject.setWithTimeout(loginSuccessKey, authToken, timeout);
     }
 
     private Cookie generateCookie(String name, String value, long timeout) {
@@ -214,41 +123,25 @@ public class AccountTokenServiceImpl implements AccountTokenService {
         return cookie;
     }
 
-    private void deleteSession(long userId, int plat, String loginId, boolean deleteSession) {
-        if (LoginPlat.rest.getValue() == plat) {
-            revokeUserToken(userId, plat, loginId);
-        }
-
-        String userdata = String.format("%s:%s:%s", userId, plat, loginId);
-        deleteLoginData(userdata);
-        if (deleteSession) {
-            deleteRedisSession(loginId);
-        }
-    }
-
     @Override
     public void logout() {
         // 删除 cookie 中的数据(或者是撤销 token)
         // 删除 redis 中的 account login 数据
         // 删除 redis 中的 session 数据
         // 删除 SecurityContextHolder 中的数据
-
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
         if (authentication instanceof AccountAuthToken) {
             AccountAuthToken authToken = (AccountAuthToken) authentication;
             long userId = authToken.getUserId();
             int plat = authToken.getPlat();
             String loginId = authToken.getLoginId();
-            if (LoginPlat.web.getValue() == plat) {
+            if (LoginPlat.rest.getValue() == plat) {
+                revokeUserToken(userId, loginId);
+            } else {
                 clearCookie();
-            } else if (LoginPlat.rest.getValue() == plat) {
-                revokeUserToken(userId, plat, loginId);
+                deleteSession(loginId);
             }
 
-            String userdata = String.format("%s:%s:%s", userId, plat, loginId);
-            deleteLoginData(userdata);
-            deleteRedisSession(loginId);
-
             SecurityContext context = SecurityContextHolder.getContext();
             context.setAuthentication(null);
             SecurityContextHolder.clearContext();
@@ -266,25 +159,36 @@ public class AccountTokenServiceImpl implements AccountTokenService {
         ServletUtil.getResponse().addCookie(cookie2);
     }
 
-    private void revokeUserToken(long userId, int plat, String loginId) {
-        String userIdStr = idObfuscation.obfuscate(userId);
-        String refreshToken = redisString.get(RedisKeys.getRefreshTokenKey(userIdStr, loginId));
-        String accessToken = redisString.get(RedisKeys.getAccessTokenKey(userIdStr, loginId));
-        String[] keys = List.of(RedisKeys.getRefreshTokenKey(userIdStr, loginId),
-                        RedisKeys.getAccessTokenKey(userIdStr, loginId),
-                        RedisKeys.getAuthTokenKey(userIdStr ,plat, loginId),
-                        RedisKeys.getRefreshSignKeyKey(refreshToken),
-                        RedisKeys.getAccessSignKeyKey(accessToken))
-                .toArray(new String[0]);
-        redisOps.del(keys);
+    @Override
+    public void logout(String loginId) {
+        List<LoginAttempts> loginAttemptsList = loginAttemptsMapper.findByLoginIds(List.of(loginId));
+        if (!loginAttemptsList.isEmpty() && !loginAttemptsList.get(0).getRememberMe()) {
+            LoginAttempts loginAttempts = loginAttemptsList.get(0);
+            long userId = loginAttempts.getUserId();
+            int plat = loginAttempts.getPlat();
+            if (LoginPlat.rest.getValue() == plat) {
+                revokeUserToken(userId, loginId);
+            } else {
+                String userdata = String.format("%s:%s:%s", userId, plat, loginId);
+                deleteSession(loginId);
+            }
+        }
     }
 
-    private void deleteLoginData(String userdata) {
-        String redisKey = RedisKeys.getLoginSuccessKey(userdata);
-        redisOps.del(redisKey);
+    private void revokeUserToken(long userId, String loginId) {
+        String refreshToken = redisString.get(RedisKeys.getRefreshTokenKey(userId, loginId));
+        String accessToken = redisString.get(RedisKeys.getAccessTokenKey(userId, loginId));
+        String[] keys = List.of(
+                        RedisKeys.getAuthTokenKey(userId, loginId),
+                        RedisKeys.getAccessTokenKey(userId, loginId),
+                        RedisKeys.getRefreshTokenKey(userId, loginId),
+                        RedisKeys.getAccessSignKeyKey(accessToken),
+                        RedisKeys.getRefreshSignKeyKey(refreshToken))
+                .toArray(new String[0]);
+        redisOps.del(keys);
     }
 
-    private void deleteRedisSession(String loginId) {
+    private void deleteSession(String loginId) {
         HttpServletRequest request = ServletUtil.getRequest();
         if (request != null) {
             Object object = request.getAttribute(SessionRepositoryFilter.SESSION_REPOSITORY_ATTR);
@@ -295,21 +199,25 @@ public class AccountTokenServiceImpl implements AccountTokenService {
         }
     }
 
-    @Override
-    public void logout(String loginId) {
-        LoginAttempts loginAttempts = loginAttemptsMapper.findByLoginId(loginId);
-        if (loginAttempts != null && !loginAttempts.getRememberMe()) {
-            long userId = loginAttempts.getUserId();
-            int plat = loginAttempts.getPlat();
-            deleteSession(userId, plat, loginId, true);
-        }
-    }
-
     @Override
     public void logout(long userId) {
-        String keyPattern = "login:" + userId;
-        Set<String> loginKeys = redisOps.keys(keyPattern);
-        redisOps.del(loginKeys.toArray(new String[0]));
+        String keyPattern = RedisKeys.getLoginSuccessKeyPattern(userId);
+        List<String> loginIds = redisOps.keys(keyPattern).stream()
+                .filter(key -> key.endsWith("auth_token"))
+                .map(key -> {
+                    String[] arr = key.split(":");
+                    if (arr.length != 7) {
+                        return null;
+                    }
+
+                    return arr[arr.length-2];
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (!loginIds.isEmpty()) {
+            loginIds.forEach(this::logout);
+        }
     }
 
     @Override
@@ -321,4 +229,90 @@ public class AccountTokenServiceImpl implements AccountTokenService {
             deleteSession(userId, plat, loginId, false);
         }*/
     }
+
+    @Override
+    public long getUserId(int type, String userdata) {
+        AccountAuthToken userAuthToken = getAuthToken(type, userdata);
+        return userAuthToken != null ? userAuthToken.getUserId() : -1L;
+    }
+
+    @Override
+    public Authentication getAuthentication() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        return authentication;
+    }
+
+    @Override
+    public AccountAuthToken getAuthToken() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication instanceof AccountAuthToken) {
+            return (AccountAuthToken) SecurityContextHolder.getContext().getAuthentication();
+        }
+
+        return null;
+    }
+
+    @Override
+    public AccountAuthToken getAuthToken(int type, String userdata) {
+        AccountAuthToken authToken = null;
+        if (type == TokenType.cookie.getValue()) {
+            authToken = getFromCookie(userdata);
+        } else if (type == TokenType.token.getValue()) {
+            authToken = getFromToken(userdata);
+        }
+
+        return authToken;
+    }
+
+    private AccountAuthToken getFromCookie(String loginId) {
+        long userId = 0;
+        String redisKey = RedisKeys.getAuthTokenKey(userId, loginId);
+        Object object = redisStringObject.get(redisKey);
+        if (object != null) {
+            return (AccountAuthToken) object;
+        }
+
+        return null;
+    }
+
+    private AccountAuthToken getFromToken(String token) {
+        String savedSignKey = redisString.get(RedisKeys.getJwtSignKey("pubkey"));
+        RSAPublicKey rsaPublicKey = RsaCryptor.getRSAPublicKey(savedSignKey);
+        try {
+            AccountAuthToken userAuthToken = JwtUtil.getAuthentication(token, rsaPublicKey);
+            if (userAuthToken != null) {
+                long userId = userAuthToken.getUserId();
+                String loginId = userAuthToken.getLoginId();
+                String redisKey = RedisKeys.getAuthTokenKey(userId, loginId);
+                boolean online = redisOps.exists(redisKey);
+                return online ? userAuthToken : null;
+            }
+        } catch (Exception ignore) {
+        }
+
+        return null;
+    }
+
+    @Override
+    public AccountToken getUserToken(String refreshToken) throws Exception {
+        String signKey = redisString.get(RedisKeys.getRefreshSignKeyKey(refreshToken));
+        if (signKey == null) {
+            throw new Exception("refresh-token 已过期");
+        }
+
+        RefreshPayload refreshPayload = JwtUtil.parseRefreshToken(refreshToken, signKey);
+        int plat = refreshPayload.getPlat();
+        String loginId = refreshPayload.getLoginId();
+        long userId = refreshPayload.getUserId();
+        Object object = redisStringObject.get(RedisKeys.getAuthTokenKey(userId, loginId));
+        AccountAuthToken authToken = (AccountAuthToken) object;
+
+        AccountToken accountToken = grantUserToken(authToken);
+        String accessToken = ServletUtil.getBearerToken();
+        if (accessToken != null) {
+            redisOps.del(RedisKeys.getAccessSignKeyKey(accessToken));
+        }
+        redisOps.del(RedisKeys.getRefreshSignKeyKey(refreshToken));
+        return accountToken;
+    }
 }

+ 62 - 40
account/account-service/src/main/java/cn/reghao/tnb/account/app/service/impl/LoginAttemptServiceImpl.java

@@ -2,6 +2,7 @@ package cn.reghao.tnb.account.app.service.impl;
 
 import cn.reghao.jutil.jdk.converter.DateTimeConverter;
 import cn.reghao.jutil.jdk.result.Result;
+import cn.reghao.tnb.account.app.service.AccountTokenService;
 import cn.reghao.tnb.account.app.service.LoginAttemptService;
 import cn.reghao.tnb.account.app.model.vo.LoginRecordVo;
 import cn.reghao.tnb.account.app.redis.ds.RedisOps;
@@ -12,7 +13,6 @@ import cn.reghao.tnb.account.app.db.mapper.LoginAttemptsMapper;
 import cn.reghao.tnb.account.app.model.po.LoginAttempts;
 import cn.reghao.tnb.account.app.redis.ds.RedisString;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException;
 import org.springframework.stereotype.Service;
 
@@ -27,24 +27,29 @@ import java.util.stream.Collectors;
 @Slf4j
 @Service
 public class LoginAttemptServiceImpl implements LoginAttemptService {
+    // 帐号可同时登入的设备数量
+    private final int maxDevice = 3;
     private final LoginAttemptsMapper attemptsMapper;
     private final RedisString redisString;
     private final RedisOps redisOps;
+    private final AccountTokenService accountTokenService;
 
-    public LoginAttemptServiceImpl(LoginAttemptsMapper attemptsMapper, RedisString redisString, RedisOps redisOps) {
+    public LoginAttemptServiceImpl(LoginAttemptsMapper attemptsMapper, RedisString redisString, RedisOps redisOps,
+                                   AccountTokenService accountTokenService) {
         this.attemptsMapper = attemptsMapper;
         this.redisString = redisString;
         this.redisOps = redisOps;
+        this.accountTokenService = accountTokenService;
     }
 
     @Override
-    public void check(long userId, int plat) {
-        checkFailedCount(userId, plat);
-        checkMaxSession(userId, plat);
+    public void loginCheck(long userId) {
+        checkFailedCount(userId);
+        checkMaxSession(userId);
     }
 
-    private void checkFailedCount(long userId, int plat) {
-        String redisKey = RedisKeys.getLoginFailedLockKey(String.valueOf(userId), plat);
+    private void checkFailedCount(long userId) {
+        String redisKey = RedisKeys.getLoginFailedLockKey(userId);
         boolean exist = redisOps.exists(redisKey);
         if (exist) {
             String errMsg = "登入错误次数超过限制,请一个小时后再尝试登入";
@@ -52,39 +57,47 @@ public class LoginAttemptServiceImpl implements LoginAttemptService {
         }
     }
 
-    private void checkMaxSession(long userId, int plat) {
-        String keyPattern = RedisKeys.getLoginSuccessKeyPattern(userId, plat);
-        Set<String> keys = redisOps.keys(keyPattern);
-        int maxSession = 10;
-        if (keys.size() >= maxSession) {
-            SortedMap<Long, String> sortedMap = new TreeMap<>();
-            keys.forEach(redisKey -> {
-                long ttl = redisOps.ttl(redisKey);
-                sortedMap.putIfAbsent(ttl, redisKey);
-            });
-
-            String oldestSession = sortedMap.get(sortedMap.firstKey());
-            redisOps.del(oldestSession);
-            //String errMsg = "您在本平台登入的设备数量已超过最大限制, 请注销一些设备后再尝试登入";
-            //throw new PreAuthenticatedCredentialsNotFoundException(errMsg);
+    private void checkMaxSession(long userId) {
+        String keyPattern = RedisKeys.getLoginSuccessKeyPattern(userId);
+        List<String> loginIds = redisOps.keys(keyPattern).stream()
+                .filter(key -> key.endsWith("auth_token"))
+                .map(key -> {
+                    String[] arr = key.split(":");
+                    if (arr.length != 7) {
+                        return null;
+                    }
+
+                    return arr[arr.length-2];
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+
+        if (loginIds.size() >= maxDevice) {
+            // 登出最早登入的设备
+            List<LoginAttempts> list = attemptsMapper.findByLoginIds(loginIds).stream()
+                    .sorted((o1, o2) -> (int) (o1.getLoginAt()-o2.getLoginAt()))
+                    .collect(Collectors.toList());
+            if (!list.isEmpty()) {
+                LoginAttempts oldest = list.get(0);
+                accountTokenService.logout(oldest.getLoginId());
+            }
         }
     }
 
     @Override
-    public Result loginFailedCount(long userId, int plat) {
-        String userIdStr = String.valueOf(userId);
-        String loginFailedCountKey = RedisKeys.getLoginFailedCountKey(userIdStr, plat);
+    public Result loginFailedCount(long userId, String loginId) {
+        attemptsMapper.updateSetFailed(loginId);
+
+        String loginFailedCountKey = RedisKeys.getLoginFailedCountKey(userId);
         String failedCountStr = redisString.get(loginFailedCountKey);
+        long timeout = 600;
         if (failedCountStr == null) {
-            long timeout = 600;
             redisString.setWithTimeout(loginFailedCountKey, "1", timeout);
         } else {
             long failedCount = redisString.incr(loginFailedCountKey);
             if (failedCount >= 3) {
-                long timeout = 600;
-                redisString.setWithTimeout(RedisKeys.getLoginFailedLockKey(userIdStr, plat), "failed-lock", timeout);
-                String errMsg = "登入错误次数超过限制,请一个小时后再尝试登入";
-                return Result.fail(errMsg);
+                redisString.setWithTimeout(RedisKeys.getLoginFailedLockKey(userId), "failed-lock", timeout);
+                return Result.fail("登入错误次数超过限制,请一个小时后再尝试登入");
             }
         }
 
@@ -92,7 +105,7 @@ public class LoginAttemptServiceImpl implements LoginAttemptService {
     }
 
     @Override
-    public void saveSuccessLogin(AccountAuthToken authToken) {
+    public void saveLoginAttempts(AccountAuthToken authToken) {
         long userId = authToken.getUserId();
         String loginId = authToken.getLoginId();
         int plat = authToken.getPlat();
@@ -109,18 +122,32 @@ public class LoginAttemptServiceImpl implements LoginAttemptService {
 
     @Override
     public List<LoginRecordVo> getActiveLogin() {
-        AccountAuthToken authToken = (AccountAuthToken) SecurityContextHolder.getContext().getAuthentication();
+        AccountAuthToken authToken = accountTokenService.getAuthToken();
         long userId = authToken.getUserId();
         String loginId = authToken.getLoginId();
         String keyPattern = RedisKeys.getLoginSuccessKeyPattern(userId);
-        List<String> onlineLogins = redisOps.keys(keyPattern).stream()
+        List<String> loginIds = redisOps.keys(keyPattern).stream()
+                .filter(key -> key.endsWith("auth_token"))
                 .map(key -> {
                     String[] arr = key.split(":");
-                    return arr[5];
+                    if (arr.length != 7) {
+                        return null;
+                    }
+
+                    return arr[arr.length-2];
                 })
+                .filter(Objects::nonNull)
                 .collect(Collectors.toList());
 
-        return attemptsMapper.findByLoginIds(onlineLogins).stream()
+        List<LoginAttempts> loginAttemptsList = attemptsMapper.findByUserId(userId, 10);
+        loginAttemptsList.forEach(loginAttempts -> {
+            long userId0 = loginAttempts.getUserId();
+            String loginId0 = loginAttempts.getLoginId();
+            String redisKey = RedisKeys.getAuthTokenKey(userId0, loginId0);
+            boolean online = redisOps.exists(redisKey);
+        });
+
+        return attemptsMapper.findByLoginIds(loginIds).stream()
                 .map(loginAttempts -> {
                     String status = "在线";
                     if (loginId.equals(loginAttempts.getLoginId())) {
@@ -137,9 +164,4 @@ public class LoginAttemptServiceImpl implements LoginAttemptService {
     private String getLocationByIp(String ip) {
         return "中国-成都";
     }
-
-    @Override
-    public LoginAttempts getLoginAttempts(String loginId) {
-        return attemptsMapper.findByLoginId(loginId);
-    }
 }

+ 5 - 24
account/account-service/src/main/java/cn/reghao/tnb/account/app/util/JwtUtil.java

@@ -1,6 +1,5 @@
 package cn.reghao.tnb.account.app.util;
 
-import cn.reghao.jutil.jdk.string.IDObfuscation;
 import cn.reghao.tnb.account.app.model.vo.RefreshPayload;
 import cn.reghao.tnb.account.app.security.form.AccountAuthToken;
 import io.jsonwebtoken.Claims;
@@ -33,7 +32,7 @@ public class JwtUtil {
      * @return
      * @date 2019-11-21 下午4:39
      */
-    public static String createAccessToken(AccountAuthToken authToken, String userIdStr, long expireAt, String signKey) {
+    public static String createAccessToken(AccountAuthToken authToken, long userId, long expireAt, String signKey) {
         String jti = UUID.randomUUID().toString().replace("-", "");
         return Jwts.builder()
                 .claim("plat", authToken.getPlat())
@@ -43,14 +42,14 @@ public class JwtUtil {
                         .map(GrantedAuthority::getAuthority)
                         .collect(Collectors.toList())
                         .toString())
-                .setSubject(userIdStr)
+                .setSubject(userId+"")
                 .setExpiration(new Date(expireAt))
                 .signWith(SignatureAlgorithm.HS256, signKey)
                 .setId(jti)
                 .compact();
     }
 
-    public static String createAccessToken(AccountAuthToken authToken, String userIdStr, long expireAt, RSAPrivateKey privateKey) {
+    public static String createAccessToken(AccountAuthToken authToken, long userId, long expireAt, RSAPrivateKey privateKey) {
         // 根据 org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter 中的 WELL_KNOWN_AUTHORITIES_CLAIM_NAMES 字段
         // 将用户的 authorities 设置到 scope claim
         // TODO authorities claim 待删除
@@ -66,7 +65,7 @@ public class JwtUtil {
                         .map(GrantedAuthority::getAuthority)
                         .collect(Collectors.toList())
                         .toString())
-                .setSubject(userIdStr)
+                .setSubject(userId+"")
                 .setExpiration(new Date(expireAt))
                 .signWith(SignatureAlgorithm.RS256, privateKey)
                 .setId(jti)
@@ -81,7 +80,7 @@ public class JwtUtil {
      * @return
      * @date 2023-02-17 17:36:34
      */
-    public static AccountAuthToken getAuthentication(String token, String signKey) {
+    public static AccountAuthToken getAuthentication(String token, RSAPublicKey signKey) {
         Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
         Integer plat = (Integer) claims.get("plat");
         String loginId = (String) claims.get("loginId");
@@ -98,24 +97,6 @@ public class JwtUtil {
         return new AccountAuthToken(plat, loginId, loginType, userId, authorities);
     }
 
-    public static AccountAuthToken getAuthentication1(String token, RSAPublicKey signKey, IDObfuscation userIdObfuscation) {
-        Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
-        Integer plat = (Integer) claims.get("plat");
-        String loginId = (String) claims.get("loginId");
-        int loginType = (int) claims.get("loginType");
-        String userIdStr = claims.getSubject();
-        // TODO userId 是系统分配且固定的,但需要检查用户的 roles 是否发生变化
-        String roles = (String) claims.get("authorities");
-        long expireAt = claims.getExpiration().getTime();
-        if (plat == null || loginId == null || userIdStr == null || roles == null) {
-            return null;
-        }
-
-        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
-        long userId = userIdObfuscation.restore(userIdStr);
-        return new AccountAuthToken(plat, loginId, loginType, userId+"", authorities);
-    }
-
     /**
      * 创建刷新令牌
      *

+ 8 - 8
account/account-service/src/main/resources/application.yml

@@ -22,10 +22,6 @@ server:
         # 两个请求间隔的最大时间, 超过此时间则会话过期
       timeout: 10m
 spring:
-  servlet:
-    multipart:
-      max-request-size: 1GB
-      max-file-size: 1GB
   application:
     name: account-service
   profiles:
@@ -33,6 +29,14 @@ spring:
   mvc:
     pathmatch:
       matching-strategy: ant_path_matcher
+  servlet:
+    multipart:
+      max-request-size: 5MB
+      max-file-size: 5MB
+  session:
+    store-type: redis
+    redis:
+      namespace: tnb:auth:session
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
     type: com.zaxxer.hikari.HikariDataSource
@@ -45,10 +49,6 @@ spring:
       max-lifetime: 1800000
       connection-timeout: 30000
       connection-test-query: SELECT 1
-  session:
-    store-type: redis
-    redis:
-      namespace: tnb:account:session
   freemarker:
     template-loader-path:
       - classpath:/templates

+ 16 - 6
account/account-service/src/main/resources/mapper/LoginAttemptsMapper.xml

@@ -9,18 +9,28 @@
         (#{id},#{deleted},#{createTime},#{updateTime},#{loginId},#{userId},#{loginType},#{userAgent},#{loginIp},#{loginAt},#{plat},#{rememberMe})
     </insert>
 
-    <select id="findAll" resultType="cn.reghao.tnb.account.app.model.po.LoginAttempts">
-        select * from msg_login_attempts
-    </select>
-    <select id="findByLoginId" resultType="cn.reghao.tnb.account.app.model.po.LoginAttempts">
-        select * from msg_login_attempts
+    <update id="updateSetFailed">
+        update msg_login_attempts
+        set update_time=now(),`status`=0
         where login_id=#{loginId}
+    </update>
+
+    <select id="findAll" resultType="cn.reghao.tnb.account.app.model.po.LoginAttempts">
+        select *
+        from msg_login_attempts
     </select>
     <select id="findByLoginIds" resultType="cn.reghao.tnb.account.app.model.po.LoginAttempts">
-        select * from msg_login_attempts
+        select *
+        from msg_login_attempts
         where login_id in
         <foreach collection="list" item="id" index="index" open="(" close=")" separator=",">
             #{id}
         </foreach>
     </select>
+    <select id="findByUserId" resultType="cn.reghao.tnb.account.app.model.po.LoginAttempts">
+        select *
+        from msg_login_attempts
+        where user_id=#{userId}
+        limit #{pageSize}
+    </select>
 </mapper>

+ 2 - 3
gateway/src/main/java/cn/reghao/tnb/gateway/token/GlobalTokenFilter.java

@@ -130,12 +130,11 @@ public class GlobalTokenFilter implements GlobalFilter, Ordered {
         Integer plat = (Integer) claims.get("plat");
         String loginId = (String) claims.get("loginId");
         int loginType = (int) claims.get("loginType");
-        String userIdStr = claims.getSubject();
-        if (plat == null || loginId == null || userIdStr == null) {
+        String userId = claims.getSubject();
+        if (plat == null || loginId == null || userId == null) {
             return new UserLogin();
         }
 
-        long userId = userIdObfuscation.restore(userIdStr);
         return new UserLogin(""+userId, loginId);
     }