diff --git a/src/main/java/com/dispose/common/Helper.java b/src/main/java/com/dispose/common/Helper.java new file mode 100644 index 00000000..823ec670 --- /dev/null +++ b/src/main/java/com/dispose/common/Helper.java @@ -0,0 +1,64 @@ +package com.dispose.common; + +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * The type Helper. + * + * @author + */ +public class Helper { + + public static String getCurrentDatetime() { + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } + + /** + * Gets timestamp milli second. + * + * @param dateTime the date time + * @return the timestamp milli second + */ + public static int getTimestampSecond(String dateTime) { + return (int)(Timestamp.valueOf(dateTime).toInstant().toEpochMilli() / 1000); + } + + /** + * Gets timestamp milli second. + * + * @param dateTime the date time + * @return the timestamp milli second + */ + public static int getTimestampMilliSecond(String dateTime) { + return (int)(Timestamp.valueOf(dateTime).toInstant().toEpochMilli()); + } + + /** + * Gets timestamp diff. + * + * @param begin the begin + * @param end the end + * @return the timestamp diff + */ + public static int getTimestampDiff(String begin, String end) { + int starTime = getTimestampSecond(begin); + int endTm = getTimestampSecond(end); + + return endTm - starTime; + } + + /** + * Gets timestamp diff now. + * + * @param begin the begin + * @return the timestamp diff now + */ + public static int getTimestampDiffNow(String begin) { + int starTime = getTimestampSecond(begin); + int endTm = (int)(System.currentTimeMillis() / 1000); + + return endTm - starTime; + } +} diff --git a/src/main/java/com/dispose/manager/UserAccountManager.java b/src/main/java/com/dispose/manager/UserAccountManager.java index 86582ef6..b224dc80 100644 --- a/src/main/java/com/dispose/manager/UserAccountManager.java +++ b/src/main/java/com/dispose/manager/UserAccountManager.java @@ -1,6 +1,7 @@ package com.dispose.manager; import com.dispose.common.ErrorCode; +import com.dispose.pojo.po.MulReturnType; import java.security.NoSuchAlgorithmException; @@ -17,7 +18,7 @@ public interface UserAccountManager { * @return the user token * @throws NoSuchAlgorithmException the no such algorithm exception */ - String getUserToken(String username) throws NoSuchAlgorithmException; + MulReturnType getUserToken(String username) throws NoSuchAlgorithmException; /** * Gets usr pwd err times. diff --git a/src/main/java/com/dispose/manager/impl/UserAccountManagerImpl.java b/src/main/java/com/dispose/manager/impl/UserAccountManagerImpl.java index d7be3913..8a9beb5b 100644 --- a/src/main/java/com/dispose/manager/impl/UserAccountManagerImpl.java +++ b/src/main/java/com/dispose/manager/impl/UserAccountManagerImpl.java @@ -1,57 +1,272 @@ package com.dispose.manager.impl; +import cn.hutool.core.convert.Convert; +import com.dispose.common.ConstValue; import com.dispose.common.ErrorCode; +import com.dispose.common.Helper; import com.dispose.manager.UserAccountManager; import com.dispose.mapper.UserAccountMapper; +import com.dispose.pojo.entity.UserAccount; +import com.dispose.pojo.po.MulReturnType; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Hex; import org.springframework.stereotype.Component; import javax.annotation.Resource; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Optional; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +/** + * The type User account manager. + * + * @author + */ @Component @Slf4j public class UserAccountManagerImpl implements UserAccountManager { + /** + * The User account cache. + */ + private final ConcurrentHashMap userAccountCache = new ConcurrentHashMap<>(); + + /** + * The User account mapper. + */ @Resource private UserAccountMapper userAccountMapper; + /** + * Gets user token. + * + * @param username the username + * @return the user token + * @throws NoSuchAlgorithmException the no such algorithm exception + */ @Override - public String getUserToken(String username) throws NoSuchAlgorithmException { - return null; + public MulReturnType getUserToken(String username) throws NoSuchAlgorithmException { + String token; + UserAccount user; + + // 根据用户名在缓存中查找用户 + Optional findRet = userAccountCache.values().stream() + .filter(userAccountCache -> username.equals(userAccountCache.getUsername())) + .findFirst(); + + // 缓存中存在 + if (findRet.isPresent()) { + user = findRet.get(); + + // token过期获取以前没有token,创建一个新token + if (tokenTimeout(user.getLastAccess()) || user.getToken().length() == 0) { + token = createUserToken(username); + // 移除过期的项 + userAccountCache.remove(user.getToken()); + // 更新token + user.setToken(userAccountMapper.upgradeToken(username, token)); + + // 重新添加到缓存中 + userAccountCache.put(token, user); + + log.info("Refresh {} token {} at {}", username, user.getToken(), user.getLastAccess()); + } else { + token = user.getToken(); + log.info("Get {} token:{}", username, token); + } + + // 更新访问时间 + user.setLastAccess(userAccountMapper.upgradeLastAccessTime(username)); + + return MulReturnType.builder() + .firstParam(ErrorCode.ERR_OK) + .secondParam(token) + .build(); + } else { + user = userAccountMapper.getUserByName(username); + + // 用户不存在 + if (user == null) { + log.error("User {} not exists", username); + return MulReturnType.builder() + .firstParam(ErrorCode.ERR_USERNOTFOUND) + .build(); + } + + // 超时或者以前未登录 + if (tokenTimeout(user.getToken()) || user.getToken().length() == 0) { + token = createUserToken(username); + userAccountMapper.upgradeToken(username, token); + log.info("Refresh {} token {} at {}", username, user.getToken(), user.getLastAccess()); + } else { + token = user.getToken(); + log.info("Get {} token:{}", username, token); + } + + // 更新访问时间 + userAccountMapper.upgradeLastAccessTime(username); + + // 添加用户到缓存 + userAccountCache.put(token, user); + + return MulReturnType.builder() + .firstParam(ErrorCode.ERR_OK) + .secondParam(token) + .build(); + } } + /** + * Gets usr pwd err times. + * + * @param username the username + * @return the usr pwd err times + */ @Override public int getUsrPwdErrTimes(String username) { return 0; } + /** + * Sets user pwd err times. + * + * @param username the username + * @param errTimes the err times + */ @Override public void setUserPwdErrTimes(String username, Integer errTimes) { } + /** + * Clean user token. + * + * @param username the username + */ @Override public void cleanUserToken(String username) { } + /** + * Verify user login error code. + * + * @param username the username + * @param token the token + * @return the error code + */ @Override public ErrorCode verifyUserLogin(String username, String token) { return null; } + /** + * Verify token error code. + * + * @param token the token + * @return the error code + */ @Override public ErrorCode verifyToken(String token) { - return null; + + // 该用户不在缓存中 + if (!userAccountCache.containsKey(token)) { + // 从数据库中获取用户并添加到缓存中 + UserAccount user = userAccountMapper.getUserByToken(token); + + // 数据库中不存在该用户 + if (user == null) { + log.error("Token {} not found, user maybe logout", token); + return ErrorCode.ERR_LOGOUT; + } else if (user.getToken() != null && user.getToken().length() > 0) { + + // token 错误 + if (!user.getToken().equals(token)) { + log.error("Token {} not equal {}, user maybe logout", token, user.getToken()); + return ErrorCode.ERR_LOGOUT; + } + + // 超时 + if (tokenTimeout(user.getLastAccess())) { + log.error("User {} token timeout before {}", user.getUsername(), user.getLastAccess()); + return ErrorCode.ERR_TOKENTIMEOUT; + } else { + // 更新用户最后一次访问时间戳 + user.setLastAccess(userAccountMapper.upgradeLastAccessTime(user.getUsername())); + // 缓存当前用户token + userAccountCache.put(token, user); + + return ErrorCode.ERR_OK; + } + } + } else { + UserAccount user = userAccountCache.get(token); + + // 根据token提取用户信息,判断当前token是否超时 + if (tokenTimeout(user.getLastAccess())) { + log.error("User {} token timeout before {}", user.getUsername(), user.getLastAccess()); + return ErrorCode.ERR_TOKENTIMEOUT; + } else { + // 更新用户最后一次访问时间戳 + user.setLastAccess(userAccountMapper.upgradeLastAccessTime(user.getUsername())); + + return ErrorCode.ERR_OK; + } + } + + return ErrorCode.ERR_LOGOUT; } + /** + * Gets username by token. + * + * @param token the token + * @return the username by token + */ @Override public String getUsernameByToken(String token) { return null; } + /** + * Verify permission error code. + * + * @param token the token + * @return the error code + */ @Override public ErrorCode verifyPermission(String token) { return null; } + + /** + * Create user token string. + * + * @param username the username + * @return the string + * @throws NoSuchAlgorithmException the no such algorithm exception + */ + private String createUserToken(String username) throws NoSuchAlgorithmException { + // 获取指定摘要算法的messageDigest对象 + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + + String tokenKey = username + + Convert.toStr(new Random(System.currentTimeMillis())) + + Convert.toStr(System.currentTimeMillis()); + + // 调用digest方法,进行加密操作 + byte[] cipherBytes = messageDigest.digest(tokenKey.getBytes()); + + return Hex.encodeHexString(cipherBytes); + } + + private boolean tokenTimeout(String lastAccess) { + try { + return (System.currentTimeMillis() - Helper.getTimestampMilliSecond(lastAccess)) + >= ConstValue.GlobalConfigure.TOKEN_TIMEOUT_MS; + } catch (Exception ex) { + return false; + } + } } diff --git a/src/main/java/com/dispose/mapper/UserAccountMapper.java b/src/main/java/com/dispose/mapper/UserAccountMapper.java index 8818814e..35d96413 100644 --- a/src/main/java/com/dispose/mapper/UserAccountMapper.java +++ b/src/main/java/com/dispose/mapper/UserAccountMapper.java @@ -19,7 +19,15 @@ public interface UserAccountMapper extends Mapper, * @param username the username * @return the user by name */ - UserAccount getUserByName(String username); + UserAccount getUserByName(@Param("username") String username); + + /** + * Gets user by token. + * + * @param token the token + * @return the user by token + */ + UserAccount getUserByToken(@Param("token") String token); /** * Lock user account. @@ -36,26 +44,29 @@ public interface UserAccountMapper extends Mapper, void unlockUserAccount(@Param("username") String username); /** - * Upgrade login time. + * Upgrade login time string. * * @param username the username + * @return the string */ - void upgradeLoginTime(@Param("username") String username); + String upgradeLoginTime(@Param("username") String username); /** - * Upgrade last access time. + * Upgrade last access time string. * * @param username the username + * @return the string */ - void upgradeLastAccessTime(@Param("username") String username); + String upgradeLastAccessTime(@Param("username") String username); /** - * Upgrade token. + * Upgrade token string. * * @param username the username * @param token the token + * @return the string */ - void upgradeToken(@Param("username") String username, + String upgradeToken(@Param("username") String username, @Param("token") String token); /** @@ -63,7 +74,8 @@ public interface UserAccountMapper extends Mapper, * * @param username the username * @param pwdErrTimes the pwd err times + * @return the pwd err times */ - void setPwdErrTimes(@Param("username") String username, + Integer setPwdErrTimes(@Param("username") String username, @Param("pwdErrTimes") Integer pwdErrTimes); } diff --git a/src/main/java/com/dispose/service/impl/UserAccountServiceImpl.java b/src/main/java/com/dispose/service/impl/UserAccountServiceImpl.java index 2686f011..e59dc22c 100644 --- a/src/main/java/com/dispose/service/impl/UserAccountServiceImpl.java +++ b/src/main/java/com/dispose/service/impl/UserAccountServiceImpl.java @@ -24,7 +24,7 @@ import java.security.NoSuchAlgorithmException; public class UserAccountServiceImpl implements UserAccountService { @Resource - private UserAccountManager userAccountCacheManager; + private UserAccountManager userAccountManager; @Resource private UserAccountMapper userAccountMapper; @@ -45,7 +45,7 @@ public class UserAccountServiceImpl implements UserAccountService { return ErrorCode.ERR_OK; } - return userAccountCacheManager.verifyToken(token); + return userAccountManager.verifyToken(token); } /** @@ -77,10 +77,10 @@ public class UserAccountServiceImpl implements UserAccountService { if (!loginUser.getPassword().equals(password)) { log.error("User {} password [{}] error", username, password); // 密码错误 - int errTimes = userAccountCacheManager.getUsrPwdErrTimes(username) + 1; + int errTimes = userAccountManager.getUsrPwdErrTimes(username) + 1; // 更新密码错误次数 - userAccountCacheManager.setUserPwdErrTimes(username, errTimes); + userAccountManager.setUserPwdErrTimes(username, errTimes); if (errTimes == ConstValue.GlobalConfigure.ALLOW_PWD_ERR_TIMES - 1) { // 提示用户即将锁定账户 @@ -98,8 +98,8 @@ public class UserAccountServiceImpl implements UserAccountService { } return MulReturnType.builder() - .firstParam(ErrorCode.ERR_OK) - .secondParam(userAccountCacheManager.getUserToken(username)).build(); + .firstParam(userAccountManager.getUserToken(username).getFirstParam()) + .secondParam(userAccountManager.getUserToken(username).getSecondParam()).build(); } /** @@ -119,9 +119,9 @@ public class UserAccountServiceImpl implements UserAccountService { } // 注销 - ErrorCode err = userAccountCacheManager.verifyUserLogin(username, token); + ErrorCode err = userAccountManager.verifyUserLogin(username, token); - userAccountCacheManager.cleanUserToken(username); + userAccountManager.cleanUserToken(username); return err; } @@ -134,7 +134,7 @@ public class UserAccountServiceImpl implements UserAccountService { */ @Override public UserAccount getUserByToken(String token) { - String username = userAccountCacheManager.getUsernameByToken(token); + String username = userAccountManager.getUsernameByToken(token); if (username != null && username.length() > 0) { return userAccountMapper.getUserByName(username); diff --git a/src/main/resources/mappers/UserAccount.xml b/src/main/resources/mappers/UserAccount.xml index 627c3558..5b92e5af 100644 --- a/src/main/resources/mappers/UserAccount.xml +++ b/src/main/resources/mappers/UserAccount.xml @@ -8,6 +8,57 @@ WHERE username = #{username} + + + + + + + + + + UPDATE user_account @@ -23,32 +74,4 @@ lockTime = 0 WHERE username = #{username, jdbcType=VARCHAR} - - - UPDATE - user_account - SET lastLoginTime = CURRENT_TIMESTAMP - WHERE username = #{username, jdbcType=VARCHAR} - - - - UPDATE - user_account - SET lastAccess = CURRENT_TIMESTAMP - WHERE username = #{username, jdbcType=VARCHAR} - - - - UPDATE - user_account - SET token = #{token, jdbcType=VARCHAR} - WHERE username = #{username, jdbcType=VARCHAR} - - - - UPDATE - user_account - SET pwdErrTimes = #{pwdErrTimes, jdbcType=INTEGER} - WHERE username = #{username, jdbcType=VARCHAR} - \ No newline at end of file diff --git a/src/test/java/com/dispose/test/debug/demo.java b/src/test/java/com/dispose/test/debug/demo.java new file mode 100644 index 00000000..4f2b96cb --- /dev/null +++ b/src/test/java/com/dispose/test/debug/demo.java @@ -0,0 +1,61 @@ +package com.dispose.test.debug; + +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; + +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * The type Demo. + * + * @author + */ +@Slf4j +public class demo { +// /** +// * A 1 idid array req test. +// * +// * @throws JsonProcessingException the json processing exception +// */ +// @Test +// public void a1_IDIDArrayReqTest() throws JsonProcessingException { +// String json = "{\"id\":[\"1\", \"123\", \"1234\", \"1234\"]}"; +// String json2 = "{\"id\":[\"1\", \"123\", \"1234\"]}"; +// String json3 = "{\"id\":[\"\", \"1\", \"123\", \"1234\"]}"; +// //String json4 = "{\"id\":[\"1\", \"123\", \"1234\", null]}"; +// +// +// IDArrayReq id = new ObjectMapper().readValue(json3, IDArrayReq.class); +// Assert.assertEquals(id.getId().length, 1); +// +// id = new ObjectMapper().readValue(json2, IDArrayReq.class); +// Assert.assertEquals(id.getId().length, 3); +// +// id = new ObjectMapper().readValue(json, IDArrayReq.class); +// Assert.assertEquals(id.getId().length, 3); +// +// //id = new ObjectMapper().readValue(json4, IDArrayReq.class); +// } +// +// /** +// * A 2 ip range test. +// * +// * @throws AddressStringException the address string exception +// */ +// @Test +// public void a2_ipRangeTest() throws AddressStringException { +// Assert.assertTrue(IPAddrType.ipInRange("192.168.0.1-192.168.0.100", "192.168.0.30")); +// Assert.assertTrue(IPAddrType.ipInRange("192.168.0.30", "192.168.0.30")); +// Assert.assertTrue(IPAddrType.ipInRange("192.168.0.40-192.168.0.40", "192.168.0.40")); +// Assert.assertTrue(IPAddrType.ipInRange("0.0.0.0-255.255.255.255", "192.168.0.30")); +// Assert.assertTrue(IPAddrType.ipInRange("", "192.168.0.30")); +// } + + @Test + public void dateTimeDebug() { + log.info("Current Datetime: {}", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + //log.info("Current Datetime: {}", new SimpleDateFormat("yyyy-MM-dd :hh:mm:ss").format(LocalDateTime.now())); + } +} diff --git a/src/test/java/com/dispose/test/mapper/UserAccountMapperTest.java b/src/test/java/com/dispose/test/mapper/UserAccountMapperTest.java index 3e4d7da5..270a1835 100644 --- a/src/test/java/com/dispose/test/mapper/UserAccountMapperTest.java +++ b/src/test/java/com/dispose/test/mapper/UserAccountMapperTest.java @@ -1,6 +1,7 @@ package com.dispose.test.mapper; import com.dispose.common.ConstValue; +import com.dispose.common.Helper; import com.dispose.mapper.UserAccountMapper; import com.dispose.pojo.entity.UserAccount; import com.fasterxml.jackson.core.JsonProcessingException; @@ -18,9 +19,12 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; +import java.util.List; /** * The type User account mapper test. + * + * @author */ @RunWith(SpringRunner.class) @SpringBootTest @@ -42,12 +46,12 @@ public class UserAccountMapperTest { private UserAccountMapper userAccountMapper; /** - * T 1 get user by name. + * A 1 get user by name. * * @throws JsonProcessingException the json processing exception */ @Test - public void t1_getUserByName() throws JsonProcessingException { + public void a1_getUserByName() throws JsonProcessingException { UserAccount user = userAccountMapper.getUserByName("admin"); log.info(objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user)); @@ -57,12 +61,12 @@ public class UserAccountMapperTest { } /** - * T 2 lock user. + * A 2 lock user. * * @throws JsonProcessingException the json processing exception */ @Test - public void t2_lockUser() throws JsonProcessingException { + public void a2_lockUser() throws JsonProcessingException { userAccountMapper.lockUserAccount("admin"); UserAccount user = userAccountMapper.getUserByName("admin"); @@ -77,16 +81,46 @@ public class UserAccountMapperTest { /** - * T 3 upgrade login time. + * A 3 upgrade login time. * * @throws JsonProcessingException the json processing exception */ @Test - public void t3_upgradeLoginTime() throws JsonProcessingException { + public void a3_upgradeLoginTime() throws JsonProcessingException { userAccountMapper.upgradeLoginTime("admin"); UserAccount user = userAccountMapper.getUserByName("admin"); Assert.assertNotNull(user); log.info(objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user)); } + + /** + * A 4 get user by token. + */ + @Test + public void a4_getUserByToken() { + List userList = userAccountMapper.selectAll(); + + userList.stream() + .filter(v -> v.getToken().length() > 0) + .forEach(v -> { + UserAccount user = userAccountMapper.getUserByToken(v.getToken()); + try { + log.info(objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(v)); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + Assert.assertNotNull(user); + Assert.assertEquals(user.getUsername(), v.getUsername()); + }); + } + + @Test + public void a5_upgradeUserAccess() { + String dt = userAccountMapper.upgradeLastAccessTime("admin"); + + log.info("Upgrade datetime: {}", dt); + + Assert.assertTrue(Helper.getTimestampDiffNow(dt) < 2); + } }