添加认证接口.
This commit is contained in:
@@ -44,7 +44,7 @@ public class SystemUserDTO implements Serializable {
|
||||
private String email;
|
||||
|
||||
@Schema(description = "用户状态 0正常 1停用 2锁定")
|
||||
private Byte status;
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "最后登录时间")
|
||||
private Date lastLoginTime;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.orion.ops.module.infra.controller;
|
||||
|
||||
import com.orion.lang.define.wrapper.HttpWrapper;
|
||||
import com.orion.ops.framework.common.annotation.IgnoreLog;
|
||||
import com.orion.ops.framework.common.annotation.RestWrapper;
|
||||
import com.orion.ops.module.infra.entity.request.UserLoginRequest;
|
||||
import com.orion.ops.module.infra.entity.vo.UserLoginVO;
|
||||
import com.orion.ops.module.infra.service.AuthenticationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 认证服务
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 11:20
|
||||
*/
|
||||
@Tag(name = "infra - 认证服务")
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RestWrapper
|
||||
@RestController
|
||||
@RequestMapping("/infra/auth")
|
||||
@SuppressWarnings({"ELValidationInJSP", "SpringElInspection"})
|
||||
public class AuthenticationController {
|
||||
|
||||
@Resource
|
||||
private AuthenticationService authenticationService;
|
||||
|
||||
@PermitAll
|
||||
@Operation(summary = "登陆")
|
||||
@PostMapping("/login")
|
||||
public UserLoginVO login(@Validated @RequestBody UserLoginRequest request,
|
||||
HttpServletRequest servletRequest) {
|
||||
// 验证登陆
|
||||
String token = authenticationService.login(request, servletRequest);
|
||||
return UserLoginVO.builder().token(token).build();
|
||||
}
|
||||
|
||||
@IgnoreLog
|
||||
@PermitAll
|
||||
@Operation(summary = "登出")
|
||||
@GetMapping("/logout")
|
||||
public HttpWrapper<?> logout(HttpServletRequest servletRequest) {
|
||||
// 登出
|
||||
authenticationService.logout(servletRequest);
|
||||
return HttpWrapper.ok();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
package com.orion.ops.module.infra.convert;
|
||||
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.ops.module.infra.entity.request.SystemUserCreateRequest;
|
||||
import com.orion.ops.module.infra.entity.request.SystemUserQueryRequest;
|
||||
import com.orion.ops.module.infra.entity.request.SystemUserUpdateRequest;
|
||||
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import com.orion.ops.module.infra.entity.domain.*;
|
||||
import com.orion.ops.module.infra.entity.vo.*;
|
||||
import com.orion.ops.module.infra.entity.dto.*;
|
||||
import com.orion.ops.module.infra.entity.request.*;
|
||||
import com.orion.ops.module.infra.convert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -17,9 +19,8 @@ import java.util.List;
|
||||
* @since 2023-7-13 18:42
|
||||
*/
|
||||
@Mapper
|
||||
@SuppressWarnings("ALL")
|
||||
public interface SystemUserConvert {
|
||||
|
||||
|
||||
SystemUserConvert MAPPER = Mappers.getMapper(SystemUserConvert.class);
|
||||
|
||||
SystemUserDO to(SystemUserCreateRequest request);
|
||||
@@ -28,8 +29,11 @@ public interface SystemUserConvert {
|
||||
|
||||
SystemUserDO to(SystemUserQueryRequest request);
|
||||
|
||||
SystemUserVO to(SystemUserDO request);
|
||||
SystemUserVO to(SystemUserDO domain);
|
||||
|
||||
List<SystemUserVO> to(List<SystemUserDO> list);
|
||||
|
||||
LoginUser toLoginUser(SystemUserDO domain);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,14 +17,13 @@ import java.util.List;
|
||||
* @since 2023-7-13 18:42
|
||||
*/
|
||||
@Mapper
|
||||
@SuppressWarnings("ALL")
|
||||
public interface SystemUserProviderConvert {
|
||||
|
||||
SystemUserProviderConvert MAPPER = Mappers.getMapper(SystemUserProviderConvert.class);
|
||||
|
||||
SystemUserDO to(SystemUserDTO dto);
|
||||
|
||||
SystemUserDTO to(SystemUserDO dto);
|
||||
SystemUserDTO to(SystemUserDO domain);
|
||||
|
||||
List<SystemUserDO> toDO(List<SystemUserDTO> list);
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.orion.ops.module.infra.define;
|
||||
|
||||
import com.orion.lang.define.cache.CacheKeyDefine;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 基建模块缓存 key
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/13 21:54
|
||||
*/
|
||||
public interface InfraCacheKeyDefine {
|
||||
|
||||
CacheKeyDefine USER_INFO = new CacheKeyDefine("user:info:{}", "用户信息", 30, TimeUnit.DAYS);
|
||||
|
||||
CacheKeyDefine USER_TOKEN = new CacheKeyDefine("user:token:{}", "用户认证 authenticationToken", 48, TimeUnit.HOURS);
|
||||
|
||||
CacheKeyDefine USER_REFRESH = new CacheKeyDefine("user:refresh:{}", "用户认证 refreshToken", 54, TimeUnit.HOURS);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.orion.ops.module.infra.define;
|
||||
|
||||
import com.orion.lang.define.cache.CacheKeyDefine;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 用户模块缓存 key
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/13 21:54
|
||||
*/
|
||||
public interface UserCacheKeyDefine {
|
||||
|
||||
CacheKeyDefine USER_INFO = new CacheKeyDefine("user:info:{}", "用户信息 ${id}", 30, TimeUnit.DAYS);
|
||||
|
||||
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyDefine("user:failed:{}", "用户登陆失败次数 ${username}", 3, TimeUnit.DAYS);
|
||||
|
||||
CacheKeyDefine LOGIN_TOKEN = new CacheKeyDefine("user:token:{}:{}", "用户登陆 token ${id} ${time}", 24, TimeUnit.HOURS);
|
||||
|
||||
CacheKeyDefine LOGIN_REFRESH = new CacheKeyDefine("user:refresh:{}:{}", "用户刷新 token ${id} ${time}", 28, TimeUnit.HOURS);
|
||||
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class SystemUserDO extends BaseDO {
|
||||
|
||||
@Schema(description = "用户状态 0正常 1停用 2锁定")
|
||||
@TableField("status")
|
||||
private Byte status;
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "最后登录时间")
|
||||
@TableField("last_login_time")
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.orion.ops.module.infra.entity.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 登陆 token 缓存
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 16:11
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class LoginTokenDTO {
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 登陆时间
|
||||
*/
|
||||
private Long loginTime;
|
||||
|
||||
/**
|
||||
* 登陆 ip
|
||||
*/
|
||||
private String ip;
|
||||
|
||||
/**
|
||||
* 登陆地址
|
||||
*/
|
||||
private String location;
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class SystemUserCreateRequest implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户状态 0正常 1停用 2锁定")
|
||||
private Byte status;
|
||||
private Integer status;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "最后登录时间")
|
||||
|
||||
@@ -43,7 +43,7 @@ public class SystemUserQueryRequest extends PageRequest {
|
||||
private String email;
|
||||
|
||||
@Schema(description = "用户状态 0正常 1停用 2锁定")
|
||||
private Byte status;
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "最后登录时间")
|
||||
private Date lastLoginTime;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class SystemUserUpdateRequest implements Serializable {
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "用户状态 0正常 1停用 2锁定")
|
||||
private Byte status;
|
||||
private Integer status;
|
||||
|
||||
@NotNull
|
||||
@Schema(description = "最后登录时间")
|
||||
|
||||
@@ -44,7 +44,7 @@ public class SystemUserVO implements Serializable {
|
||||
private String email;
|
||||
|
||||
@Schema(description = "用户状态 0正常 1停用 2锁定")
|
||||
private Byte status;
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "最后登录时间")
|
||||
private Date lastLoginTime;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.orion.ops.module.infra.entity.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 用户登陆响应
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 11:23
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(name = "SystemUserVO", description = "用户 视图响应对象")
|
||||
public class UserLoginVO {
|
||||
|
||||
@Schema(description = "登陆 token")
|
||||
private String token;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.orion.ops.module.infra.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 登陆 token 状态
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 16:15
|
||||
*/
|
||||
@Getter
|
||||
public enum LoginTokenStatusEnum {
|
||||
|
||||
/**
|
||||
* 正常
|
||||
*/
|
||||
OK(0),
|
||||
|
||||
/**
|
||||
* 已在其他设备登陆
|
||||
*/
|
||||
OTHER_DEVICE(1, "已在其他设备登陆"),
|
||||
|
||||
;
|
||||
|
||||
LoginTokenStatusEnum(Integer status) {
|
||||
this(status, null);
|
||||
}
|
||||
|
||||
LoginTokenStatusEnum(Integer status, String message) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private final Integer status;
|
||||
|
||||
private final String message;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.orion.ops.module.infra.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 用户状态枚举
|
||||
*
|
||||
* @author Jiahang Li
|
||||
* @version 1.0.0
|
||||
* @since 2023/7/14 11:35
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum UserStatusEnum {
|
||||
|
||||
/**
|
||||
* 0 正常
|
||||
*/
|
||||
NORMAL(0),
|
||||
|
||||
/**
|
||||
* 1 停用
|
||||
*/
|
||||
DISABLED(1),
|
||||
|
||||
/**
|
||||
* 2 锁定
|
||||
*/
|
||||
LOCKED(2),
|
||||
|
||||
;
|
||||
|
||||
private final Integer status;
|
||||
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package com.orion.ops.module.infra.service;
|
||||
|
||||
import com.orion.lang.define.wrapper.Pair;
|
||||
import com.orion.ops.module.infra.entity.request.UserLoginRequest;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 认证服务
|
||||
*
|
||||
@@ -14,16 +17,25 @@ public interface AuthenticationService {
|
||||
/**
|
||||
* 登陆
|
||||
*
|
||||
* @param request request
|
||||
* @param request request
|
||||
* @param servletRequest servletRequest
|
||||
* @return token
|
||||
*/
|
||||
String login(UserLoginRequest request);
|
||||
String login(UserLoginRequest request, HttpServletRequest servletRequest);
|
||||
|
||||
/**
|
||||
* 登出
|
||||
*
|
||||
* @param token token
|
||||
* @param servletRequest servletRequest
|
||||
*/
|
||||
void logout(String token);
|
||||
void logout(HttpServletRequest servletRequest);
|
||||
|
||||
/**
|
||||
* 获取 token pair
|
||||
*
|
||||
* @param loginToken loginToken
|
||||
* @return pair
|
||||
*/
|
||||
Pair<Long, Long> getLoginTokenPair(String loginToken);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
package com.orion.ops.module.infra.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.orion.lang.constant.StandardHttpHeader;
|
||||
import com.orion.lang.define.wrapper.Pair;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Valid;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.crypto.Signatures;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
import com.orion.ops.framework.common.constant.ErrorMessage;
|
||||
import com.orion.ops.framework.common.crypto.ValueCrypto;
|
||||
import com.orion.ops.framework.common.security.LoginUser;
|
||||
import com.orion.ops.framework.common.utils.CryptoUtils;
|
||||
import com.orion.ops.framework.common.utils.IpUtils;
|
||||
import com.orion.ops.framework.common.utils.Kits;
|
||||
import com.orion.ops.framework.redis.core.utils.RedisUtils;
|
||||
import com.orion.ops.module.infra.convert.SystemUserConvert;
|
||||
import com.orion.ops.module.infra.dao.SystemUserDAO;
|
||||
import com.orion.ops.module.infra.define.UserCacheKeyDefine;
|
||||
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
|
||||
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
|
||||
import com.orion.ops.module.infra.entity.request.UserLoginRequest;
|
||||
import com.orion.ops.module.infra.enums.LoginTokenStatusEnum;
|
||||
import com.orion.ops.module.infra.enums.UserStatusEnum;
|
||||
import com.orion.ops.module.infra.service.AuthenticationService;
|
||||
import com.orion.web.servlet.web.Servlets;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 认证服务实现
|
||||
@@ -24,10 +47,18 @@ import java.util.Optional;
|
||||
@Service
|
||||
public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
|
||||
/**
|
||||
* 允许多端登陆
|
||||
*/
|
||||
private final boolean allowMultiPlatform = false;
|
||||
// TODO 想想看 如何配置化
|
||||
// 允许多端登陆
|
||||
private final boolean allowMultiDevice = true;
|
||||
// 允许凭证续签
|
||||
private final boolean allowRefresh = true;
|
||||
// 凭证续签最大次数
|
||||
private final int maxRefreshCount = 5;
|
||||
// 失败锁定次数
|
||||
private final int maxFailedLoginCount = 5;
|
||||
|
||||
@Resource
|
||||
private ValueCrypto valueCrypto;
|
||||
|
||||
@Resource
|
||||
private SystemUserDAO systemUserDAO;
|
||||
@@ -36,32 +67,253 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
@Override
|
||||
public String login(UserLoginRequest request) {
|
||||
// 检查登陆
|
||||
LambdaQueryWrapper<SystemUserDO> wrapper = systemUserDAO.wrapper()
|
||||
.eq(SystemUserDO::getUsername, request.getUsername())
|
||||
.eq(SystemUserDO::getPassword, Signatures.md5(request.getPassword()));
|
||||
public String login(UserLoginRequest request, HttpServletRequest servletRequest) {
|
||||
// 登陆前检查
|
||||
this.preCheckLogin(request);
|
||||
// 获取登陆用户
|
||||
Optional<SystemUserDO> systemUserDO = systemUserDAO.of(wrapper).only().get();
|
||||
Valid.isTrue(systemUserDO.isPresent(), ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
LambdaQueryWrapper<SystemUserDO> wrapper = systemUserDAO.wrapper()
|
||||
.eq(SystemUserDO::getUsername, request.getUsername());
|
||||
SystemUserDO user = systemUserDAO.of(wrapper).only().get();
|
||||
// 检查密码
|
||||
boolean passwordCorrect = this.checkPassword(request, user);
|
||||
Valid.isTrue(passwordCorrect, ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
// 检查用户状态
|
||||
|
||||
this.checkUserStatus(user.getStatus());
|
||||
// 设置上次登录时间
|
||||
this.setLastLoginTime(user.getId());
|
||||
// 设置缓存
|
||||
|
||||
// 不允许多端登陆删除缓存
|
||||
|
||||
// 生成 authenticationToken
|
||||
|
||||
// 生成 refreshToken
|
||||
|
||||
//
|
||||
|
||||
return null;
|
||||
this.setUserCache(user);
|
||||
// 删除登陆失败次数缓存
|
||||
redisTemplate.delete(UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername()));
|
||||
// 获取登陆 ip
|
||||
String remoteAddr = Servlets.getRemoteAddr(servletRequest);
|
||||
String location = IpUtils.getLocation(remoteAddr);
|
||||
long current = System.currentTimeMillis();
|
||||
// 不允许多端登陆
|
||||
if (!false) {
|
||||
// 无效化其他缓存
|
||||
this.invalidOtherDeviceToken(user.getId(), current, remoteAddr, location);
|
||||
}
|
||||
// 生成 loginToken
|
||||
return this.generatorLoginToken(user, current, remoteAddr, location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logout(String token) {
|
||||
public void logout(HttpServletRequest request) {
|
||||
// 获取登陆 token
|
||||
String loginToken = Kits.getAuthorization(request.getHeader(StandardHttpHeader.AUTHORIZATION));
|
||||
if (loginToken == null) {
|
||||
return;
|
||||
}
|
||||
Pair<Long, Long> pair = this.getLoginTokenPair(loginToken);
|
||||
if (pair == null) {
|
||||
return;
|
||||
}
|
||||
Long id = pair.getKey();
|
||||
Long current = pair.getValue();
|
||||
// 删除 loginToken & refreshToken
|
||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, current);
|
||||
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, current);
|
||||
redisTemplate.delete(Lists.of(loginKey, refreshKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Long, Long> getLoginTokenPair(String loginToken) {
|
||||
if (loginToken == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
String value = CryptoUtils.decryptBase62(loginToken);
|
||||
String[] pair = value.split(":");
|
||||
return Pair.of(Long.valueOf(pair[0]), Long.valueOf(pair[1]));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登陆预检查
|
||||
*
|
||||
* @param request request
|
||||
*/
|
||||
private void preCheckLogin(UserLoginRequest request) {
|
||||
// 检查密码长度是否正确 MD5 长度为 32
|
||||
if (request.getPassword().length() != Const.MD5_LEN) {
|
||||
throw Exceptions.argument(ErrorMessage.USERNAME_PASSWORD_ERROR);
|
||||
}
|
||||
// 检查登陆失败次数
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(request.getUsername());
|
||||
String failedCount = redisTemplate.opsForValue().get(failedCountKey);
|
||||
if (failedCount != null && Integer.parseInt(failedCount) >= maxFailedLoginCount) {
|
||||
throw Exceptions.argument(ErrorMessage.MAX_LOGIN_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查密码
|
||||
*
|
||||
* @param request request
|
||||
* @param user user
|
||||
* @return 是否正确
|
||||
*/
|
||||
private boolean checkPassword(UserLoginRequest request, SystemUserDO user) {
|
||||
// 密码正确
|
||||
if (user != null && user.getPassword().equals(Signatures.md5(request.getPassword()))) {
|
||||
return true;
|
||||
}
|
||||
// 刷新登陆失败缓存
|
||||
String failedCountKey = UserCacheKeyDefine.LOGIN_FAILED_COUNT.format(user.getUsername());
|
||||
Long failedLoginCount = redisTemplate.opsForValue().increment(failedCountKey);
|
||||
// 用户不存在
|
||||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
// 锁定用户
|
||||
if (failedLoginCount >= maxFailedLoginCount) {
|
||||
// 更新用户表
|
||||
SystemUserDO updateUser = new SystemUserDO();
|
||||
updateUser.setId(user.getId());
|
||||
updateUser.setStatus(UserStatusEnum.LOCKED.getStatus());
|
||||
systemUserDAO.updateById(updateUser);
|
||||
// 更新缓存
|
||||
String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
|
||||
String userInfoCache = redisTemplate.opsForValue().get(userInfoKey);
|
||||
// 缓存不存在
|
||||
if (userInfoCache == null) {
|
||||
return false;
|
||||
}
|
||||
// 修改缓存状态
|
||||
LoginUser loginUser = JSON.parseObject(userInfoCache, LoginUser.class);
|
||||
loginUser.setStatus(UserStatusEnum.LOCKED.getStatus());
|
||||
redisTemplate.opsForValue().set(userInfoKey, JSON.toJSONString(loginUser),
|
||||
UserCacheKeyDefine.USER_INFO.getTimeout(),
|
||||
UserCacheKeyDefine.USER_INFO.getUnit());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查用户状态
|
||||
*
|
||||
* @param status status
|
||||
*/
|
||||
private void checkUserStatus(Integer status) {
|
||||
if (UserStatusEnum.DISABLED.getStatus().equals(status)) {
|
||||
// 禁用状态
|
||||
throw Exceptions.argument(ErrorMessage.USER_DISABLED);
|
||||
} else if (UserStatusEnum.LOCKED.getStatus().equals(status)) {
|
||||
// 锁定状态
|
||||
throw Exceptions.argument(ErrorMessage.USER_LOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最后登录时间
|
||||
*
|
||||
* @param id id
|
||||
*/
|
||||
private void setLastLoginTime(Long id) {
|
||||
SystemUserDO update = new SystemUserDO();
|
||||
update.setId(id);
|
||||
update.setLastLoginTime(new Date());
|
||||
systemUserDAO.updateById(update);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户缓存
|
||||
*
|
||||
* @param user user
|
||||
*/
|
||||
private void setUserCache(SystemUserDO user) {
|
||||
String userInfoKey = UserCacheKeyDefine.USER_INFO.format(user.getId());
|
||||
String userInfoCache = redisTemplate.opsForValue().get(userInfoKey);
|
||||
// 缓存存在
|
||||
if (userInfoCache != null) {
|
||||
return;
|
||||
}
|
||||
// 设置缓存
|
||||
LoginUser loginUser = SystemUserConvert.MAPPER.toLoginUser(user);
|
||||
// TODO 查询角色
|
||||
redisTemplate.opsForValue().set(userInfoKey, JSON.toJSONString(loginUser),
|
||||
UserCacheKeyDefine.USER_INFO.getTimeout(),
|
||||
UserCacheKeyDefine.USER_INFO.getUnit());
|
||||
}
|
||||
|
||||
/**
|
||||
* 无效化其他登陆信息
|
||||
*
|
||||
* @param id id
|
||||
* @param loginTime loginTime
|
||||
* @param remoteAddr remoteAddr
|
||||
* @param location location
|
||||
*/
|
||||
private void invalidOtherDeviceToken(Long id, long loginTime, String remoteAddr, String location) {
|
||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, "*");
|
||||
// 获取登陆信息
|
||||
Set<String> loginKeyList = RedisUtils.scanKeys(redisTemplate, loginKey, 100);
|
||||
if (!loginKeyList.isEmpty()) {
|
||||
// 获取有效登陆信息
|
||||
List<LoginTokenDTO> loginTokenInfoList = redisTemplate.opsForValue()
|
||||
.multiGet(loginKeyList)
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(s -> JSON.parseObject(s, LoginTokenDTO.class))
|
||||
.filter(s -> LoginTokenStatusEnum.OK.getStatus().equals(s.getStatus()))
|
||||
.collect(Collectors.toList());
|
||||
// 修改登陆信息
|
||||
for (LoginTokenDTO loginTokenInfo : loginTokenInfoList) {
|
||||
String deviceLoginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTokenInfo.getLoginTime());
|
||||
loginTokenInfo.setStatus(LoginTokenStatusEnum.OTHER_DEVICE.getStatus());
|
||||
loginTokenInfo.setLoginTime(loginTime);
|
||||
loginTokenInfo.setIp(remoteAddr);
|
||||
loginTokenInfo.setLocation(location);
|
||||
redisTemplate.opsForValue().set(deviceLoginKey, JSON.toJSONString(loginTokenInfo),
|
||||
UserCacheKeyDefine.LOGIN_TOKEN.getTimeout(),
|
||||
UserCacheKeyDefine.LOGIN_TOKEN.getUnit());
|
||||
}
|
||||
}
|
||||
// 删除续签信息
|
||||
if (allowRefresh) {
|
||||
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, "*");
|
||||
Set<String> refreshKeyList = RedisUtils.scanKeys(redisTemplate, refreshKey, 100);
|
||||
if (!refreshKeyList.isEmpty()) {
|
||||
redisTemplate.delete(refreshKeyList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 loginToken
|
||||
*
|
||||
* @param user user
|
||||
* @param loginTime loginTime
|
||||
* @param remoteAddr remoteAddr
|
||||
* @param location location
|
||||
* @return loginToken
|
||||
*/
|
||||
private String generatorLoginToken(SystemUserDO user, long loginTime,
|
||||
String remoteAddr, String location) {
|
||||
Long id = user.getId();
|
||||
// 生成 loginToken
|
||||
String loginKey = UserCacheKeyDefine.LOGIN_TOKEN.format(id, loginTime);
|
||||
LoginTokenDTO loginValue = LoginTokenDTO.builder()
|
||||
.status(LoginTokenStatusEnum.OK.getStatus())
|
||||
.ip(remoteAddr)
|
||||
.loginTime(loginTime)
|
||||
.location(location)
|
||||
.build();
|
||||
redisTemplate.opsForValue().set(loginKey, JSON.toJSONString(loginValue),
|
||||
UserCacheKeyDefine.LOGIN_TOKEN.getTimeout(),
|
||||
UserCacheKeyDefine.LOGIN_TOKEN.getUnit());
|
||||
// 生成 refreshToken
|
||||
if (allowRefresh) {
|
||||
String refreshKey = UserCacheKeyDefine.LOGIN_REFRESH.format(id, loginTime);
|
||||
redisTemplate.opsForValue().set(refreshKey, "1",
|
||||
UserCacheKeyDefine.LOGIN_REFRESH.getTimeout(),
|
||||
UserCacheKeyDefine.LOGIN_REFRESH.getUnit());
|
||||
}
|
||||
// 返回token
|
||||
return CryptoUtils.encryptBase62(id + ":" + loginTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user