token登录
1.用户登录
/**
* 用户登录
* @param user
* @return
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public Result userLogin(@RequestBody User user, HttpServletResponse response,HttpServletRequest request) {
log.info("用户登录[start]");
String authToken = request.getHeader(Constants.RESPONSE_TOKEN);
if(!StringUtils.isBlank(authToken)){
TokenAuthBO tokenBo = tokenService.verifyToken(authToken);
if(tokenBo.isAuthState()){
return new Result(Result.SUCCESS_CODE, "你已经登录,无需重复登录!");
}
}
if (StringUtils.isBlank(user.getUserName()) || StringUtils.isBlank(user.getUserPass())) {
log.info("无效参数");
return new Result(Result.ERROR_CODE, "无效参数!");
}
int count = loginService.checkUserName(user.getUserName());
//是否存在用户
if (count == 0) {
log.info("没有该用户");
return new Result(Result.ERROR_CODE, "没有该用户!");
}
User u = loginService.userLogin(user);
if (u == null) {
log.info("用户登失败,密码错误");
return new Result(Result.ERROR_CODE, "密码错误!");
}
//用户状态为启用
if (u.getUserStartusing() == 1) {
log.info("用户登失败,账号禁止使用");
return new Result(Result.ERROR_CODE, "账号禁止使用!");
}
// 生成Token
Integer userId = u.getUserId();
Long loginTime = System.currentTimeMillis() / 1000;
long changePwdTime = loginTime;
String token = tokenService.createToken(userId, loginTime, changePwdTime);
//设置响应token
Cookie cookie = new Cookie(Constants.RESPONSE_TOKEN,token);
cookie.setHttpOnly(false);
cookie.setPath("/");
cookie.setMaxAge(60 * 60 * 24 ); //有效期一天
response.addCookie(cookie);
//存入缓存
template.opsForValue().set(token,user,1, TimeUnit.DAYS);
log.info("用户登录[end]");
return new Result(Result.SUCCESS_CODE, "登陆成功!",template.opsForValue().get(token));
}
2.生成token
/**
* @author sxw 创建Token
* @param userId 用户Id, loginTime登录时间, changePwdTime密码修改时间
* @return TokenAuthEnum 认证result枚举
**/
public String createToken(Integer userId, Long loginTime, Long changePwdTime) {
log.info("创建Token[start], userId: " + userId + ", loginTime: " + loginTime + ", changePwdTime: "
+ changePwdTime);
JSONObject tokenJson = new JSONObject();
tokenJson.put(TOKEN_USERID, userId);
tokenJson.put(TOKEN_LOGIN_TIME, loginTime);
tokenJson.put(TOKEN_CHANGE_PWD_TIME, changePwdTime);
long endTime = loginTime + 72*60*60;
tokenJson.put(TOKEN_END_TIME, endTime);
String encryptToken = DESUtils.encrypt(tokenJson.toJSONString());
String token = Base64.encodeBase64URLSafeString(encryptToken.getBytes());
log.info("创建Token[end], token: " + token);
return token;
}
3.校验token
/**
* @author sxw 验证Token
* @param token 认证字符
* @return TokenAuthEnum 认证result枚举
**/
public TokenAuthBO verifyToken(String token) {
log.info("token认证开始[start], token: " + token);
if (StringUtils.isBlank(token)) {
log.warn("token认证开始[end], token is null~");
return new TokenAuthBO(TokenAuthEnum.FAIL_ISNULL, Collections.emptyMap());
}
JSONObject tokenJson = null;
try {
String base64Token = new String(Base64.decodeBase64(token));
String decrypt = DESUtils.decrypt(base64Token);
tokenJson = JSONObject.parseObject(decrypt);
log.info("token解析后的数据,tokenJson: " + tokenJson);
if (MapUtils.isBlank(tokenJson) || tokenJson.isEmpty()) {
throw new SecurityException("tokenJson is NULL");
}
tokenJson.put(TOKEN, token);
} catch (SecurityException | JSONException ex) {
log.error("token 解析解密异常,message: " + ex.getMessage());
return new TokenAuthBO(TokenAuthEnum.FAIL, Collections.emptyMap());
}
if (MapUtils.isEmptyMap(tokenJson,
new String[] { TOKEN_USERID, TOKEN_LOGIN_TIME, TOKEN_END_TIME, TOKEN_CHANGE_PWD_TIME })) {
log.warn("token缺失数据~");
return new TokenAuthBO(TokenAuthEnum.FAIL_DATA_ERROR, tokenJson);
}
if (!StringUtils.isNumeric(tokenJson.getString(TOKEN_USERID))
|| !StringUtils.isNumeric(tokenJson.getString(TOKEN_LOGIN_TIME))
|| !StringUtils.isNumeric(tokenJson.getString(TOKEN_END_TIME))
|| !StringUtils.isNumeric(tokenJson.getString(TOKEN_CHANGE_PWD_TIME))) {
log.warn("token数据格式有误~");
return new TokenAuthBO(TokenAuthEnum.FAIL_DATA_ERROR, tokenJson);
}
Integer loginTime = Integer.parseInt(tokenJson.getString(TOKEN_LOGIN_TIME));
Integer endTime = Integer.parseInt(tokenJson.getString(TOKEN_END_TIME));
Integer change_pwd_time = Integer.parseInt(tokenJson.getString(TOKEN_CHANGE_PWD_TIME));
// 判断token时间有效性
long thisTime = System.currentTimeMillis() / 1000;
if (!(thisTime >= loginTime && thisTime <= endTime)) {
log.warn("token timeOut, token解析后的数据,tokenJson: " + tokenJson + ", thisTime: " + thisTime);
return new TokenAuthBO(TokenAuthEnum.FAIL_TIMEOUT, tokenJson);
}
// 判断userId有效性
String userId = tokenJson.getString(TOKEN_USERID);
Map<String,Object> data = tokenDao.getObjectByUserId(userId);
String newChangeTime = data.get("pwdChangeTime").toString();
Integer num = (Integer) data.get("num");
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = simpleDateFormat.parse(newChangeTime);
} catch (ParseException e) {
e.printStackTrace();
}
long newChangeTimeNow = date.getTime()/1000;
if (num == 0) {
log.warn("认证失败,非有效用户~");
return new TokenAuthBO(TokenAuthEnum.FAIL, tokenJson);
}
// 判断密码最新修改时间是否大于token中历史修改时间
if (newChangeTimeNow > change_pwd_time.intValue()) {
log.warn("认证成功,密码已过期[密码修改时间: " + newChangeTimeNow + "], token密码时间: " + change_pwd_time);
return new TokenAuthBO(TokenAuthEnum.FAIL_PWDCHANGE, tokenJson);
}
Object user = template.opsForValue().get(token);
if(user == null){
log.warn("redis已过期,请更新redis");
return new TokenAuthBO(TokenAuthEnum.FAIL_PWDCHANGE, tokenJson);
}
log.info("token认证成功 \n"+"token="+token+"\n"+"tokenInfo="+tokenJson);
return new TokenAuthBO(TokenAuthEnum.SUCCESS, tokenJson);
}
4.yml配置redis
redis:
database: 0
host: localhost
jedis:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
shutdown-timeout: 100ms
password: 123456
port: 6379
5.pom.xml配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
6.redis序列化
@Configuration
public class RedisConfig {
//还是使用springboot默认配置的RedisConnectionFactory
@Autowired
private RedisConnectionFactory redisConnectionFactory;
// 默认用的是用JdkSerializationRedisSerializer进行序列化的
@Bean
@SuppressWarnings({ "rawtypes", "unchecked" })
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
// 注入数据源
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key-value结构序列化数据结构
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash数据结构序列化方式,必须这样否则存hash 就是基于jdk序列化的
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
// 启用默认序列化方式
redisTemplate.setEnableDefaultSerializer(true);
redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
/// redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}