feat: 查询个人信息 后端.

This commit is contained in:
lijiahang
2023-11-02 17:19:27 +08:00
parent eafe69ebca
commit 0322729797
17 changed files with 211 additions and 57 deletions

View File

@@ -52,6 +52,8 @@ public enum ErrorCode implements CodeInfo {
OTHER_DEVICE_LOGIN(702, "该账号于 {} 已在其他设备登录 {}({})"),
SESSION_OFFLINE(703, "该账号于 {} 已被强制下线 {}({})"),
// -------------------- 自定义 - 通用 --------------------
NETWORK_FLUCTUATION(900, "当前环境网路波动"),

View File

@@ -0,0 +1,35 @@
package com.orion.ops.framework.common.entity;
import java.io.Serializable;
/**
* 请求身份
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/11/2 16:23
*/
public interface RequestIdentity extends Serializable {
/**
* 设置请求地址
*
* @param address address
*/
void setAddress(String address);
/**
* 设置请求位置
*
* @param location location
*/
void setLocation(String location);
/**
* 设置请求 userAgent
*
* @param userAgent userAgent
*/
void setUserAgent(String userAgent);
}

View File

@@ -0,0 +1,39 @@
package com.orion.ops.framework.common.utils;
import com.orion.ops.framework.common.entity.RequestIdentity;
import com.orion.web.servlet.web.Servlets;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Optional;
/**
* 请求工具类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/11/2 16:26
*/
public class Requests {
private Requests() {
}
/**
* 填充请求信息
*
* @param identity identity
*/
public static void fillIdentity(RequestIdentity identity) {
Optional.ofNullable(RequestContextHolder.getRequestAttributes())
.map(s -> (ServletRequestAttributes) s)
.map(ServletRequestAttributes::getRequest)
.ifPresent(request -> {
String address = Servlets.getRemoteAddr(request);
identity.setAddress(address);
identity.setLocation(IpUtils.getLocation(address));
identity.setUserAgent(Servlets.getUserAgent(request));
});
}
}

View File

@@ -20,8 +20,7 @@ import com.orion.ops.framework.common.enums.BooleanBit;
import com.orion.ops.framework.common.meta.TraceIdHolder;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.common.security.SecurityHolder;
import com.orion.ops.framework.common.utils.IpUtils;
import com.orion.web.servlet.web.Servlets;
import com.orion.ops.framework.common.utils.Requests;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@@ -31,8 +30,6 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
@@ -251,15 +248,11 @@ public class OperatorLogAspect {
*/
private void fillRequest(OperatorLogModel model) {
model.setTraceId(TraceIdHolder.get());
Optional.ofNullable(RequestContextHolder.getRequestAttributes())
.map(s -> (ServletRequestAttributes) s)
.map(ServletRequestAttributes::getRequest)
.ifPresent(request -> {
String address = Servlets.getRemoteAddr(request);
model.setAddress(address);
model.setLocation(IpUtils.getLocation(address));
model.setUserAgent(Strings.retain(Servlets.getUserAgent(request), operatorLogConfig.getUserAgentLength()));
});
// 填充请求信息
Requests.fillIdentity(model);
Optional.ofNullable(model.getUserAgent())
.map(s -> Strings.retain(s, operatorLogConfig.getUserAgentLength()))
.ifPresent(model::setUserAgent);
}
/**

View File

@@ -1,5 +1,6 @@
package com.orion.ops.framework.biz.operator.log.core.model;
import com.orion.ops.framework.common.entity.RequestIdentity;
import lombok.Data;
import java.util.Date;
@@ -12,7 +13,7 @@ import java.util.Date;
* @since 2023/10/9 18:44
*/
@Data
public class OperatorLogModel {
public class OperatorLogModel implements RequestIdentity {
/**
* userId

View File

@@ -1,15 +1,19 @@
package com.orion.ops.module.infra.controller;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.ops.framework.biz.operator.log.core.annotation.OperatorLog;
import com.orion.ops.framework.common.validator.group.Page;
import com.orion.ops.framework.log.core.annotation.IgnoreLog;
import com.orion.ops.framework.log.core.enums.IgnoreLogMode;
import com.orion.ops.framework.web.core.annotation.RestWrapper;
import com.orion.ops.module.infra.define.operator.AuthenticationOperatorType;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import com.orion.ops.module.infra.service.MineService;
@@ -76,7 +80,6 @@ public class MineController {
return mineService.getCurrentUserSessionList();
}
@IgnoreLog(IgnoreLogMode.RET)
@PutMapping("/offline-session")
@Operation(summary = "下线当前用户会话")
public HttpWrapper<?> offlineCurrentUserSession(@Validated @RequestBody UserSessionOfflineRequest request) {
@@ -84,6 +87,11 @@ public class MineController {
return HttpWrapper.ok();
}
// fixme 操作日志
@IgnoreLog(IgnoreLogMode.RET)
@PostMapping("/query-operator-log")
@Operation(summary = "查询当前用户操作日志")
public DataGrid<OperatorLogVO> getCurrentUserOperatorLog(@Validated(Page.class) @RequestBody OperatorLogQueryRequest request) {
return mineService.getCurrentUserOperatorLog(request);
}
}

View File

@@ -47,10 +47,10 @@ public class OperatorLogController {
return operatorLogService.getOperatorLogPage(request);
}
// fixme 权限配置
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/login-history")
@Operation(summary = "查询用户登录日志")
@PreAuthorize("@ss.hasPermission('infra:operator-log:query')")
public List<LoginHistoryVO> getLoginHistory(@RequestParam("username") String username) {
return operatorLogService.getLoginHistory(username);
}

View File

@@ -138,18 +138,18 @@ public class SystemUserController {
return systemUserService.deleteSystemUserById(id);
}
// fixme 权限配置
@IgnoreLog(IgnoreLogMode.RET)
@GetMapping("/user-session")
@Operation(summary = "获取用户会话列表")
@PreAuthorize("@ss.hasPermission('infra:system-user:query-session')")
public List<UserSessionVO> getUserSessionList(@RequestParam("id") Long id) {
return systemUserService.getUserSessionList(id);
}
// fixme 权限配置
@IgnoreLog(IgnoreLogMode.RET)
@OperatorLog(SystemUserOperatorType.OFFLINE)
@PutMapping("/offline-session")
@Operation(summary = "下线用户会话")
@PreAuthorize("@ss.hasPermission('infra:system-user:offline-session')")
public HttpWrapper<?> offlineUserSession(@Validated @RequestBody UserSessionOfflineRequest request) {
systemUserService.offlineUserSession(request);
return HttpWrapper.ok();

View File

@@ -24,14 +24,14 @@ public interface UserCacheKeyDefine {
.build();
CacheKeyDefine USER_LIST = new CacheKeyBuilder()
.key("user:list:{}")
.key("user:base:list")
.desc("用户列表")
.type(UserInfoDTO.class)
.timeout(1, TimeUnit.DAYS)
.build();
CacheKeyDefine LOGIN_FAILED_COUNT = new CacheKeyBuilder()
.key("user:failed:{}")
.key("user:login-failed:{}")
.desc("用户登录失败次数 ${username}")
.type(Integer.class)
.timeout(3, TimeUnit.DAYS)

View File

@@ -28,6 +28,8 @@ public class SystemUserOperatorType extends InitializingOperatorTypes {
public static final String DELETE = "system-user:delete";
public static final String OFFLINE = "system-user:offline";
@Override
public OperatorType[] types() {
return new OperatorType[]{
@@ -37,6 +39,7 @@ public class SystemUserOperatorType extends InitializingOperatorTypes {
new OperatorType(M, GRANT_ROLE, "分配用户角色 <sb>${username}</sb>"),
new OperatorType(H, RESET_PASSWORD, "重置用户密码 <sb>${username}</sb>"),
new OperatorType(H, DELETE, "删除用户 <sb>${username}</sb>"),
new OperatorType(M, OFFLINE, "下线用户会话 <sb>${username}</sb>"),
};
}

View File

@@ -1,5 +1,6 @@
package com.orion.ops.module.infra.entity.dto;
import com.orion.ops.framework.common.entity.RequestIdentity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -14,7 +15,7 @@ import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginTokenIdentityDTO {
public class LoginTokenIdentityDTO implements RequestIdentity {
/**
* 原始登录时间

View File

@@ -1,7 +1,14 @@
package com.orion.ops.module.infra.enums;
import com.orion.lang.utils.time.Dates;
import com.orion.ops.framework.common.constant.ErrorCode;
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
import com.orion.ops.module.infra.entity.dto.LoginTokenIdentityDTO;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Date;
/**
* 登录 token 状态
*
@@ -10,6 +17,7 @@ import lombok.Getter;
* @since 2023/7/14 16:15
*/
@Getter
@AllArgsConstructor
public enum LoginTokenStatusEnum {
/**
@@ -20,21 +28,57 @@ public enum LoginTokenStatusEnum {
/**
* 已在其他设备登录
*/
OTHER_DEVICE(1, "已在其他设备登录"),
OTHER_DEVICE(1) {
@Override
public RuntimeException toException(LoginTokenDTO token) {
LoginTokenIdentityDTO override = token.getOverride();
return ErrorCode.OTHER_DEVICE_LOGIN.exception(
Dates.format(new Date(override.getLoginTime()), Dates.MD_HM),
override.getAddress(),
override.getLocation());
}
},
/**
* 强制下线
*/
SESSION_OFFLINE(2) {
@Override
public RuntimeException toException(LoginTokenDTO token) {
LoginTokenIdentityDTO override = token.getOverride();
return ErrorCode.SESSION_OFFLINE.exception(
Dates.format(new Date(override.getLoginTime()), Dates.MD_HM),
override.getAddress(),
override.getLocation());
}
},
;
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;
/**
* 获取异常信息
*
* @param token token
* @return exception
*/
public RuntimeException toException(LoginTokenDTO token) {
return null;
}
public static LoginTokenStatusEnum of(Integer status) {
if (status == null) {
return OK;
}
for (LoginTokenStatusEnum value : values()) {
if (value.getStatus().equals(status)) {
return value;
}
}
return OK;
}
}

View File

@@ -1,13 +1,10 @@
package com.orion.ops.module.infra.framework.service.impl;
import com.orion.lang.utils.time.Dates;
import com.orion.ops.framework.common.constant.ErrorCode;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.redis.core.utils.RedisUtils;
import com.orion.ops.framework.security.core.service.SecurityFrameworkService;
import com.orion.ops.module.infra.define.cache.UserCacheKeyDefine;
import com.orion.ops.module.infra.entity.dto.LoginTokenDTO;
import com.orion.ops.module.infra.entity.dto.LoginTokenIdentityDTO;
import com.orion.ops.module.infra.enums.LoginTokenStatusEnum;
import com.orion.ops.module.infra.enums.UserStatusEnum;
import com.orion.ops.module.infra.service.AuthenticationService;
@@ -15,7 +12,6 @@ import com.orion.ops.module.infra.service.PermissionService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Date;
/**
* 安全包 实现类
@@ -63,7 +59,6 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
this.checkTokenStatus(tokenInfo);
} catch (Exception e) {
// token 失效则删除
// fixme test
RedisUtils.delete(UserCacheKeyDefine.LOGIN_TOKEN.format(tokenInfo.getId(), tokenInfo.getOrigin().getLoginTime()));
throw e;
}
@@ -82,18 +77,15 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
* @param loginToken loginToken
*/
private void checkTokenStatus(LoginTokenDTO loginToken) {
Integer tokenStatus = loginToken.getStatus();
LoginTokenStatusEnum status = LoginTokenStatusEnum.of(loginToken.getStatus());
// 正常状态
if (LoginTokenStatusEnum.OK.getStatus().equals(tokenStatus)) {
if (LoginTokenStatusEnum.OK.equals(status)) {
return;
}
// 其他设备登录
if (LoginTokenStatusEnum.OTHER_DEVICE.getStatus().equals(tokenStatus)) {
LoginTokenIdentityDTO override = loginToken.getOverride();
throw ErrorCode.OTHER_DEVICE_LOGIN.exception(
Dates.format(new Date(override.getLoginTime()), Dates.MD_HM),
override.getAddress(),
override.getLocation());
RuntimeException ex = status.toException(loginToken);
if (ex != null) {
throw ex;
}
}

View File

@@ -1,9 +1,12 @@
package com.orion.ops.module.infra.service;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
@@ -61,4 +64,12 @@ public interface MineService {
*/
void offlineCurrentUserSession(UserSessionOfflineRequest request);
/**
* 查询当前用户操作日志
*
* @param request request
* @return rows
*/
DataGrid<OperatorLogVO> getCurrentUserOperatorLog(OperatorLogQueryRequest request);
}

View File

@@ -372,7 +372,7 @@ public class DictValueServiceImpl implements DictValueService {
return dictValueDAO.wrapper()
.eq(DictValueDO::getKeyId, request.getKeyId())
.like(DictValueDO::getKeyName, request.getKeyName())
.eq(DictValueDO::getValue, request.getValue())
.like(DictValueDO::getValue, request.getValue())
.like(DictValueDO::getLabel, request.getLabel())
.orderByDesc(DictValueDO::getId);
}

View File

@@ -1,16 +1,19 @@
package com.orion.ops.module.infra.service.impl;
import com.orion.lang.define.wrapper.DataGrid;
import com.orion.lang.utils.crypto.Signatures;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.security.core.utils.SecurityUtils;
import com.orion.ops.module.infra.dao.SystemUserDAO;
import com.orion.ops.module.infra.entity.domain.SystemUserDO;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.request.operator.OperatorLogQueryRequest;
import com.orion.ops.module.infra.entity.request.user.SystemUserUpdateRequest;
import com.orion.ops.module.infra.entity.request.user.UserResetPasswordRequest;
import com.orion.ops.module.infra.entity.request.user.UserSessionOfflineRequest;
import com.orion.ops.module.infra.entity.request.user.UserUpdatePasswordRequest;
import com.orion.ops.module.infra.entity.vo.LoginHistoryVO;
import com.orion.ops.module.infra.entity.vo.OperatorLogVO;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
import com.orion.ops.module.infra.entity.vo.UserSessionVO;
import com.orion.ops.module.infra.service.MineService;
@@ -86,4 +89,10 @@ public class MineServiceImpl implements MineService {
systemUserService.offlineUserSession(request);
}
@Override
public DataGrid<OperatorLogVO> getCurrentUserOperatorLog(OperatorLogQueryRequest request) {
request.setUserId(SecurityUtils.getLoginUserId());
return operatorLogService.getOperatorLogPage(request);
}
}

View File

@@ -10,6 +10,7 @@ import com.orion.ops.framework.common.constant.Const;
import com.orion.ops.framework.common.constant.ErrorCode;
import com.orion.ops.framework.common.constant.ErrorMessage;
import com.orion.ops.framework.common.security.LoginUser;
import com.orion.ops.framework.common.utils.Requests;
import com.orion.ops.framework.common.utils.Valid;
import com.orion.ops.framework.redis.core.utils.RedisMaps;
import com.orion.ops.framework.redis.core.utils.RedisStrings;
@@ -23,6 +24,7 @@ import com.orion.ops.module.infra.define.cache.TipsCacheKeyDefine;
import com.orion.ops.module.infra.define.cache.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.dto.LoginTokenIdentityDTO;
import com.orion.ops.module.infra.entity.dto.UserInfoDTO;
import com.orion.ops.module.infra.entity.request.user.*;
import com.orion.ops.module.infra.entity.vo.SystemUserVO;
@@ -157,7 +159,6 @@ public class SystemUserServiceImpl implements SystemUserService {
@Override
public List<SystemUserVO> getSystemUserList() {
// fixme test
// 查询用户列表
List<UserInfoDTO> list = RedisMaps.valuesJson(UserCacheKeyDefine.USER_LIST);
if (list.isEmpty()) {
@@ -219,7 +220,6 @@ public class SystemUserServiceImpl implements SystemUserService {
public void deleteSystemUserRel(Long id, String username) {
log.info("SystemUserService-deleteSystemUserRel id: {}", id);
// 删除用户列表缓存
// FIXME test
RedisMaps.delete(UserCacheKeyDefine.USER_LIST, id);
// 删除用户缓存 需要扫描的 key 让其自动过期
RedisUtils.delete(
@@ -305,10 +305,26 @@ public class SystemUserServiceImpl implements SystemUserService {
public void offlineUserSession(UserSessionOfflineRequest request) {
Long userId = Valid.notNull(request.getUserId());
Long timestamp = request.getTimestamp();
RedisStrings.delete(
UserCacheKeyDefine.LOGIN_TOKEN.format(userId, timestamp),
UserCacheKeyDefine.LOGIN_REFRESH.format(userId, request.getTimestamp())
);
// 查询用户
SystemUserDO user = systemUserDAO.selectById(userId);
Valid.notNull(user, ErrorMessage.USER_ABSENT);
// 添加日志参数
OperatorLogs.add(OperatorLogs.USERNAME, user.getUsername());
// 删除刷新缓存
RedisStrings.delete(UserCacheKeyDefine.LOGIN_REFRESH.format(userId, request.getTimestamp()));
// 查询并且覆盖 token
String tokenKey = UserCacheKeyDefine.LOGIN_TOKEN.format(userId, timestamp);
LoginTokenDTO tokenInfo = RedisStrings.getJson(tokenKey, UserCacheKeyDefine.LOGIN_TOKEN);
if (tokenInfo != null) {
tokenInfo.setStatus(LoginTokenStatusEnum.SESSION_OFFLINE.getStatus());
LoginTokenIdentityDTO override = new LoginTokenIdentityDTO();
override.setLoginTime(System.currentTimeMillis());
// 设置请求信息
Requests.fillIdentity(override);
tokenInfo.setOverride(override);
// 更新 token
RedisStrings.setJson(tokenKey, UserCacheKeyDefine.LOGIN_TOKEN, tokenInfo);
}
}
/**