REM:
1. 用户管理功能开发过程保存
2. 增加调试代码测试用例
This commit is contained in:
HuangXin 2020-07-20 16:52:41 +08:00
parent 0c02f3d741
commit d1296b4d1a
8 changed files with 465 additions and 55 deletions

View File

@ -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 <huangxin@cmhi.chinamoblie.com>
*/
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;
}
}

View File

@ -1,6 +1,7 @@
package com.dispose.manager; package com.dispose.manager;
import com.dispose.common.ErrorCode; import com.dispose.common.ErrorCode;
import com.dispose.pojo.po.MulReturnType;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -17,7 +18,7 @@ public interface UserAccountManager {
* @return the user token * @return the user token
* @throws NoSuchAlgorithmException the no such algorithm exception * @throws NoSuchAlgorithmException the no such algorithm exception
*/ */
String getUserToken(String username) throws NoSuchAlgorithmException; MulReturnType<ErrorCode, String> getUserToken(String username) throws NoSuchAlgorithmException;
/** /**
* Gets usr pwd err times. * Gets usr pwd err times.

View File

@ -1,57 +1,272 @@
package com.dispose.manager.impl; 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.ErrorCode;
import com.dispose.common.Helper;
import com.dispose.manager.UserAccountManager; import com.dispose.manager.UserAccountManager;
import com.dispose.mapper.UserAccountMapper; import com.dispose.mapper.UserAccountMapper;
import com.dispose.pojo.entity.UserAccount;
import com.dispose.pojo.po.MulReturnType;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
/**
* The type User account manager.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/
@Component @Component
@Slf4j @Slf4j
public class UserAccountManagerImpl implements UserAccountManager { public class UserAccountManagerImpl implements UserAccountManager {
/**
* The User account cache.
*/
private final ConcurrentHashMap<String, UserAccount> userAccountCache = new ConcurrentHashMap<>();
/**
* The User account mapper.
*/
@Resource @Resource
private UserAccountMapper userAccountMapper; private UserAccountMapper userAccountMapper;
/**
* Gets user token.
*
* @param username the username
* @return the user token
* @throws NoSuchAlgorithmException the no such algorithm exception
*/
@Override @Override
public String getUserToken(String username) throws NoSuchAlgorithmException { public MulReturnType<ErrorCode, String> getUserToken(String username) throws NoSuchAlgorithmException {
return null; String token;
UserAccount user;
// 根据用户名在缓存中查找用户
Optional<UserAccount> 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.<ErrorCode, String>builder()
.firstParam(ErrorCode.ERR_OK)
.secondParam(token)
.build();
} else {
user = userAccountMapper.getUserByName(username);
// 用户不存在
if (user == null) {
log.error("User {} not exists", username);
return MulReturnType.<ErrorCode, String>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.<ErrorCode, String>builder()
.firstParam(ErrorCode.ERR_OK)
.secondParam(token)
.build();
}
} }
/**
* Gets usr pwd err times.
*
* @param username the username
* @return the usr pwd err times
*/
@Override @Override
public int getUsrPwdErrTimes(String username) { public int getUsrPwdErrTimes(String username) {
return 0; return 0;
} }
/**
* Sets user pwd err times.
*
* @param username the username
* @param errTimes the err times
*/
@Override @Override
public void setUserPwdErrTimes(String username, Integer errTimes) { public void setUserPwdErrTimes(String username, Integer errTimes) {
} }
/**
* Clean user token.
*
* @param username the username
*/
@Override @Override
public void cleanUserToken(String username) { public void cleanUserToken(String username) {
} }
/**
* Verify user login error code.
*
* @param username the username
* @param token the token
* @return the error code
*/
@Override @Override
public ErrorCode verifyUserLogin(String username, String token) { public ErrorCode verifyUserLogin(String username, String token) {
return null; return null;
} }
/**
* Verify token error code.
*
* @param token the token
* @return the error code
*/
@Override @Override
public ErrorCode verifyToken(String token) { 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 @Override
public String getUsernameByToken(String token) { public String getUsernameByToken(String token) {
return null; return null;
} }
/**
* Verify permission error code.
*
* @param token the token
* @return the error code
*/
@Override @Override
public ErrorCode verifyPermission(String token) { public ErrorCode verifyPermission(String token) {
return null; 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;
}
}
} }

View File

@ -19,7 +19,15 @@ public interface UserAccountMapper extends Mapper<UserAccount>,
* @param username the username * @param username the username
* @return the user by name * @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. * Lock user account.
@ -36,26 +44,29 @@ public interface UserAccountMapper extends Mapper<UserAccount>,
void unlockUserAccount(@Param("username") String username); void unlockUserAccount(@Param("username") String username);
/** /**
* Upgrade login time. * Upgrade login time string.
* *
* @param username the username * @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 * @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 username the username
* @param token the token * @param token the token
* @return the string
*/ */
void upgradeToken(@Param("username") String username, String upgradeToken(@Param("username") String username,
@Param("token") String token); @Param("token") String token);
/** /**
@ -63,7 +74,8 @@ public interface UserAccountMapper extends Mapper<UserAccount>,
* *
* @param username the username * @param username the username
* @param pwdErrTimes the pwd err times * @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); @Param("pwdErrTimes") Integer pwdErrTimes);
} }

View File

@ -24,7 +24,7 @@ import java.security.NoSuchAlgorithmException;
public class UserAccountServiceImpl implements UserAccountService { public class UserAccountServiceImpl implements UserAccountService {
@Resource @Resource
private UserAccountManager userAccountCacheManager; private UserAccountManager userAccountManager;
@Resource @Resource
private UserAccountMapper userAccountMapper; private UserAccountMapper userAccountMapper;
@ -45,7 +45,7 @@ public class UserAccountServiceImpl implements UserAccountService {
return ErrorCode.ERR_OK; 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)) { if (!loginUser.getPassword().equals(password)) {
log.error("User {} password [{}] error", username, 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) { if (errTimes == ConstValue.GlobalConfigure.ALLOW_PWD_ERR_TIMES - 1) {
// 提示用户即将锁定账户 // 提示用户即将锁定账户
@ -98,8 +98,8 @@ public class UserAccountServiceImpl implements UserAccountService {
} }
return MulReturnType.<ErrorCode, String>builder() return MulReturnType.<ErrorCode, String>builder()
.firstParam(ErrorCode.ERR_OK) .firstParam(userAccountManager.getUserToken(username).getFirstParam())
.secondParam(userAccountCacheManager.getUserToken(username)).build(); .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; return err;
} }
@ -134,7 +134,7 @@ public class UserAccountServiceImpl implements UserAccountService {
*/ */
@Override @Override
public UserAccount getUserByToken(String token) { public UserAccount getUserByToken(String token) {
String username = userAccountCacheManager.getUsernameByToken(token); String username = userAccountManager.getUsernameByToken(token);
if (username != null && username.length() > 0) { if (username != null && username.length() > 0) {
return userAccountMapper.getUserByName(username); return userAccountMapper.getUserByName(username);

View File

@ -8,6 +8,57 @@
WHERE username = #{username} WHERE username = #{username}
</select> </select>
<select id="getUserByToken" resultType="com.dispose.pojo.entity.UserAccount">
SELECT *
FROM user_account
WHERE token = #{token}
AND LENGTH(#{token}) > 0
</select>
<select id="upgradeLastAccessTime" resultType="java.lang.String">
UPDATE
user_account
SET lastAccess = CURRENT_TIMESTAMP
WHERE username = #{username, jdbcType=VARCHAR};
SELECT lastAccess
FROM user_account
WHERE username = #{username}
</select>
<select id="upgradeLoginTime" resultType="java.lang.String">
UPDATE
user_account
SET lastLoginTime = CURRENT_TIMESTAMP
WHERE username = #{username, jdbcType=VARCHAR};
SELECT lastLoginTime
FROM user_account
WHERE username = #{username}
</select>
<select id="upgradeToken" resultType="java.lang.String">
UPDATE
user_account
SET token = #{token, jdbcType=VARCHAR}
WHERE username = #{username, jdbcType=VARCHAR};
SELECT token
FROM user_account
WHERE username = #{username}
</select>
<select id="setPwdErrTimes" resultType="java.lang.Integer">
UPDATE
user_account
SET pwdErrTimes = #{pwdErrTimes, jdbcType=INTEGER}
WHERE username = #{username, jdbcType=VARCHAR};
SELECT pwdErrTimes
FROM user_account
WHERE username = #{username}
</select>
<update id="lockUserAccount"> <update id="lockUserAccount">
UPDATE UPDATE
user_account user_account
@ -23,32 +74,4 @@
lockTime = 0 lockTime = 0
WHERE username = #{username, jdbcType=VARCHAR} WHERE username = #{username, jdbcType=VARCHAR}
</update> </update>
<update id="upgradeLoginTime">
UPDATE
user_account
SET lastLoginTime = CURRENT_TIMESTAMP
WHERE username = #{username, jdbcType=VARCHAR}
</update>
<update id="upgradeLastAccessTime">
UPDATE
user_account
SET lastAccess = CURRENT_TIMESTAMP
WHERE username = #{username, jdbcType=VARCHAR}
</update>
<update id="upgradeToken">
UPDATE
user_account
SET token = #{token, jdbcType=VARCHAR}
WHERE username = #{username, jdbcType=VARCHAR}
</update>
<update id="setPwdErrTimes">
UPDATE
user_account
SET pwdErrTimes = #{pwdErrTimes, jdbcType=INTEGER}
WHERE username = #{username, jdbcType=VARCHAR}
</update>
</mapper> </mapper>

View File

@ -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 <huangxin@cmhi.chinamoblie.com>
*/
@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()));
}
}

View File

@ -1,6 +1,7 @@
package com.dispose.test.mapper; package com.dispose.test.mapper;
import com.dispose.common.ConstValue; import com.dispose.common.ConstValue;
import com.dispose.common.Helper;
import com.dispose.mapper.UserAccountMapper; import com.dispose.mapper.UserAccountMapper;
import com.dispose.pojo.entity.UserAccount; import com.dispose.pojo.entity.UserAccount;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
@ -18,9 +19,12 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
/** /**
* The type User account mapper test. * The type User account mapper test.
*
* @author <huangxin@cmhi.chinamoblie.com>
*/ */
@RunWith(SpringRunner.class) @RunWith(SpringRunner.class)
@SpringBootTest @SpringBootTest
@ -42,12 +46,12 @@ public class UserAccountMapperTest {
private UserAccountMapper userAccountMapper; private UserAccountMapper userAccountMapper;
/** /**
* T 1 get user by name. * A 1 get user by name.
* *
* @throws JsonProcessingException the json processing exception * @throws JsonProcessingException the json processing exception
*/ */
@Test @Test
public void t1_getUserByName() throws JsonProcessingException { public void a1_getUserByName() throws JsonProcessingException {
UserAccount user = userAccountMapper.getUserByName("admin"); UserAccount user = userAccountMapper.getUserByName("admin");
log.info(objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user)); 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 * @throws JsonProcessingException the json processing exception
*/ */
@Test @Test
public void t2_lockUser() throws JsonProcessingException { public void a2_lockUser() throws JsonProcessingException {
userAccountMapper.lockUserAccount("admin"); userAccountMapper.lockUserAccount("admin");
UserAccount user = userAccountMapper.getUserByName("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 * @throws JsonProcessingException the json processing exception
*/ */
@Test @Test
public void t3_upgradeLoginTime() throws JsonProcessingException { public void a3_upgradeLoginTime() throws JsonProcessingException {
userAccountMapper.upgradeLoginTime("admin"); userAccountMapper.upgradeLoginTime("admin");
UserAccount user = userAccountMapper.getUserByName("admin"); UserAccount user = userAccountMapper.getUserByName("admin");
Assert.assertNotNull(user); Assert.assertNotNull(user);
log.info(objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user)); log.info(objMapper.writerWithDefaultPrettyPrinter().writeValueAsString(user));
} }
/**
* A 4 get user by token.
*/
@Test
public void a4_getUserByToken() {
List<UserAccount> 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);
}
} }