新增验证码、登录日志、操作日志记录
This commit is contained in:
parent
e6c56ebbb1
commit
d447ae83be
@ -11,7 +11,7 @@
|
||||
Target Server Version : 50721
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 06/04/2023 17:20:01
|
||||
Date: 07/04/2023 14:54:55
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
@ -64,6 +64,7 @@ CREATE TABLE `sys_login_log` (
|
||||
-- ----------------------------
|
||||
-- Records of sys_login_log
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_login_log` VALUES ('165965da5e98b46703683bc655004384', 'admin', '123456', '成功', 'Unknown', 'Unknown', '内网IP', '127.0.0.1', '19aa0675-d451-4232-9221-73babda49078', '2023-04-07 11:33:04');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_menu
|
||||
@ -107,6 +108,7 @@ DROP TABLE IF EXISTS `sys_oper_log`;
|
||||
CREATE TABLE `sys_oper_log` (
|
||||
`id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
|
||||
`module` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '功能模块',
|
||||
`method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作方法',
|
||||
`type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作类型',
|
||||
`message` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作描述',
|
||||
`req_param` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '请求参数',
|
||||
@ -114,9 +116,10 @@ CREATE TABLE `sys_oper_log` (
|
||||
`take_up_time` int(64) NULL DEFAULT NULL COMMENT '耗时',
|
||||
`user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作用户id',
|
||||
`user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作用户名称',
|
||||
`method` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '操作方法',
|
||||
`status` int(1) NULL DEFAULT NULL COMMENT '状态:0、成功,1、失败',
|
||||
`uri` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求url',
|
||||
`ip` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求IP',
|
||||
`unusual` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '异常信息',
|
||||
`version` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '版本号',
|
||||
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
@ -125,6 +128,7 @@ CREATE TABLE `sys_oper_log` (
|
||||
-- ----------------------------
|
||||
-- Records of sys_oper_log
|
||||
-- ----------------------------
|
||||
INSERT INTO `sys_oper_log` VALUES ('f052dcb16aeb0fa199bd82aa058e8eee', '菜单管理-当前用户权限菜单列表', 'com.tansci.controller.SysMenuController.menus', 'SELECT', '当前用户权限菜单列表', '{}', '{\"code\":200,\"message\":\"操作成功\",\"result\":[{\"children\":[{\"component\":\"Layout\",\"icon\":\"Grid\",\"id\":\"10001dsgf\",\"isShow\":true,\"meta\":{\"keepAlive\":false,\"openMode\":0,\"id\":\"10001dsgf\",\"title\":\"Iframe测试\",\"isShow\":true},\"name\":\"Iframe\",\"parentId\":\"b1851d1b13594e71840103c11a37a002\",\"path\":\"https://www.bing.com/?mkt=zh-CN\",\"sort\":6},{\"component\":\"Layout\",\"icon\":\"Grid\",\"id\":\"b1851d1b13594e71840103c11a37a003\",\"isShow\":true,\"meta\":{\"keepAlive\":false,\"openMode\":0,\"id\":\"b1851d1b13594e71840103c11a37a003\",\"title\":\"菜单管理\",\"isShow\":true},\"name\":\"menu\",\"parentId\":\"b1851d1b13594e71840103c11a37a002\",\"path\":\"/system/Menu\",\"sort\":2},{\"component\":\"Layout\",\"icon\":\"QuestionFilled\",\"id\":\"b1851d1b13594e71840103c11a37a004\",\"isShow\":true,\"meta\":{\"keepAlive\":false,\"openMode\":0,\"id\":\"b1851d1b13594e71840103c11a37a004\",\"title\":\"组织管理\",\"isShow\":true},\"name\":\"org\",\"parentId\":\"b1851d1b13594e71840103c11a37a002\",\"path\":\"/system/Org\",\"sort\":3},{\"component\":\"Layout\",\"icon\":\"Grid\",\"id\":\"b1851d1b13594e71840103c11a37a005\",\"isShow\":true,\"meta\":{\"keepAlive\":false,\"openMode\":0,\"id\":\"b1851d1b13594e71840103c11a37a005\",\"title\":\"权限管理\",\"isShow\":true},\"name\":\"role\",\"parentId\":\"b1851d1b13594e71840103c11a37a002\",\"path\":\"/system/Role\",\"sort\":4},{\"component\":\"Layout\",\"icon\":\"Grid\",\"id\":\"b1851d1b13594e71840103c11a37a006\",\"isShow\":true,\"meta\":{\"keepAlive\":false,\"openMode\":0,\"id\":\"b1851d1b13594e71840103c11a37a006\",\"title\":\"用户管理\",\"isShow\":true},\"name\":\"user\",\"parentId\":\"b1851d1b13594e71840103c11a37a002\",\"path\":\"/system/User\",\"sort\":5}],\"component\":\"Layout\",\"icon\":\"Grid\",\"id\":\"b1851d1b13594e71840103c11a37a002\",\"isShow\":true,\"meta\":{\"keepAlive\":false,\"openMode\":0,\"id\":\"b1851d1b13594e71840103c11a37a002\",\"title\":\"系统管理\",\"isShow\":true},\"name\":\"system\",\"parentId\":\"0\",\"path\":\"/system\",\"sort\":1}]}', 49, '534a37c366ec47878a6b0c85703d0bc2', NULL, 0, '/tansci/sysmenu/menus', '127.0.0.1', NULL, NULL, '2023-04-07 14:50:17');
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for sys_org
|
||||
|
||||
@ -70,3 +70,17 @@ export function logout(){
|
||||
location.reload()
|
||||
})
|
||||
}
|
||||
|
||||
// 验证码
|
||||
export function getCode(){
|
||||
return new Promise((resolve, reject) => {
|
||||
request({
|
||||
url: '/tansci/auth/code',
|
||||
method: 'get'
|
||||
}).then((res:any) => {
|
||||
resolve(res)
|
||||
}).catch((e:any) => {
|
||||
reject(e)
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -30,10 +30,10 @@ axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
|
||||
// axios实例拦截响应
|
||||
axiosInstance.interceptors.response.use(
|
||||
(response: AxiosResponse) => {
|
||||
if (response.status === 200 && response.data.code == 200) {
|
||||
if (response.status === 200 && response.data.code === 200) {
|
||||
return response;
|
||||
} else {
|
||||
ElMessage.warning(showMessage(response.status));
|
||||
ElMessage.warning(showMessage(response.data.code));
|
||||
if (response.data.code === 402) {
|
||||
logout()
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import {onBeforeMount,reactive,ref,toRefs} from "vue"
|
||||
import {onBeforeMount,onMounted,reactive,ref,toRefs} from "vue"
|
||||
import type {FormInstance} from 'element-plus'
|
||||
import {useRouter} from 'vue-router'
|
||||
import {login} from '@/api/auth'
|
||||
import {login,getCode} from '@/api/auth'
|
||||
|
||||
const router = useRouter()
|
||||
const loginFormRef = ref<FormInstance>()
|
||||
|
||||
const logo = new URL('../../assets/image/logo.png', import.meta.url).href
|
||||
const loginLogo = new URL('../../assets/image/login-icon.png', import.meta.url).href
|
||||
const codeImg = ref()
|
||||
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
@ -18,6 +19,7 @@
|
||||
loginForm: {
|
||||
username: '',
|
||||
password: '',
|
||||
code: '',
|
||||
remember: 0
|
||||
},
|
||||
})
|
||||
@ -26,11 +28,21 @@
|
||||
state.loginStyle.height = (document.body.clientHeight || document.documentElement.clientHeight) + "px"
|
||||
})
|
||||
|
||||
onMounted(() =>{
|
||||
onCode()
|
||||
})
|
||||
|
||||
function copyYear(){
|
||||
let date = new Date();
|
||||
return date.getFullYear();
|
||||
}
|
||||
|
||||
function onCode(){
|
||||
getCode().then((res:any) =>{
|
||||
codeImg.value = 'data:image/png;base64,' + res.data.result
|
||||
})
|
||||
}
|
||||
|
||||
async function onSubmit(formEl: FormInstance | undefined) {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid)=>{
|
||||
@ -38,7 +50,8 @@
|
||||
// 登录成功后设置token到缓存
|
||||
let param:any = {
|
||||
username: state.loginForm.username,
|
||||
password: state.loginForm.password
|
||||
password: state.loginForm.password,
|
||||
code: state.loginForm.code
|
||||
}
|
||||
state.loading = true;
|
||||
login(param).then((res:any) =>{
|
||||
@ -81,6 +94,10 @@
|
||||
{required: true,message: '请输入密码',trigger: 'blur'}]">
|
||||
<el-input type="password" v-model="state.loginForm.password" prefix-icon="Lock" show-password placeholder="请输入密码" style="width:100%"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" :rules="[{required: true,message: '请输入验证码',trigger: 'blur'}]">
|
||||
<el-input v-model="state.loginForm.code" prefix-icon="HelpFilled" placeholder="请输入验证码" style="width:70%; margin-right: 1%"></el-input>
|
||||
<img :src="codeImg" @click="onCode" style="width: 29%; height: 38px;">
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="state.loginForm.remember">记住密码</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
@ -21,6 +21,8 @@
|
||||
<druid-spring-boot.varsion>1.2.6</druid-spring-boot.varsion>
|
||||
<sa-token-spring-boot.varsion>1.34.0</sa-token-spring-boot.varsion>
|
||||
<knife4j-spring-boot.version>3.0.3</knife4j-spring-boot.version>
|
||||
<hutool-all.version>5.7.22</hutool-all.version>
|
||||
<fastjson2.version>2.0.26</fastjson2.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
@ -29,6 +31,11 @@
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
@ -65,6 +72,17 @@
|
||||
<version>${knife4j-spring-boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>${hutool-all.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
<version>${fastjson2.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package com.tansci.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @ClassName: Log.java
|
||||
* @ClassPath: com.tansci.common.annotation.Log.java
|
||||
* @Description: 自定义操作日志注解
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 14:05
|
||||
**/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Log {
|
||||
|
||||
/**
|
||||
* 操作模块
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String modul() default "";
|
||||
|
||||
/**
|
||||
* 操作类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* 操作说明
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String desc() default "";
|
||||
|
||||
}
|
||||
@ -0,0 +1,180 @@
|
||||
package com.tansci.common.aop;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.tansci.common.annotation.Log;
|
||||
import com.tansci.common.constant.Constants;
|
||||
import com.tansci.domain.SysOperLog;
|
||||
import com.tansci.domain.vo.SysUserSessionVo;
|
||||
import com.tansci.service.SysOperLogService;
|
||||
import com.tansci.utils.SystemUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.*;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @ClassName: OperLogAspect.java
|
||||
* @ClassPath: com.tansci.common.aop.OperLogAspect.java
|
||||
* @Description: 操作日志记录
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 14:02
|
||||
**/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
@Component
|
||||
public class OperLogAspect {
|
||||
|
||||
/**
|
||||
* 统计请求的处理时间
|
||||
*/
|
||||
ThreadLocal<Long> startTime = new ThreadLocal<>();
|
||||
@Autowired
|
||||
private SysOperLogService sysOperLogService;
|
||||
|
||||
/**
|
||||
* @MonthName: logPoinCut
|
||||
* @Description: 设置操作日志切入点 记录操作日志 在注解的位置切入代码
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 14:06
|
||||
* @Param: []
|
||||
* @return: void
|
||||
**/
|
||||
@Pointcut("@annotation(com.tansci.common.annotation.Log)")
|
||||
public void logPoinCut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @MonthName: exceptionLogPoinCut
|
||||
* @Description: 设置操作异常切入点记录异常日志 扫描所有controller包下操作
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 14:07
|
||||
* @Param: []
|
||||
* @return: void
|
||||
**/
|
||||
@Pointcut("execution(* com.tansci.controller..*.*(..))")
|
||||
public void exceptionLogPoinCut() {
|
||||
}
|
||||
|
||||
@Before("logPoinCut()")
|
||||
public void doBefore() {
|
||||
// 接收到请求,记录请求开始时间
|
||||
startTime.set(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
/**
|
||||
* @MonthName: doAfterReturning
|
||||
* @Description: 正常返回通知,拦截用户操作日志,连接点正常执行完成后执行, 如果连接点抛出异常,则不会执行
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 14:08
|
||||
* @Param: [joinPoint, keys]
|
||||
* @return: void
|
||||
**/
|
||||
@AfterReturning(value = "logPoinCut()", returning = "keys")
|
||||
public void doAfterReturning(JoinPoint joinPoint, Object keys) {
|
||||
try {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
SysUserSessionVo sessionVo = (SysUserSessionVo) StpUtil.getSession().get(String.valueOf(StpUtil.getLoginId()));
|
||||
|
||||
SysOperLog operLog = SysOperLog.builder()
|
||||
.reqParam(JSON.toJSONString(converMap(request.getParameterMap())))
|
||||
.resParam(JSON.toJSONString(keys))
|
||||
.takeUpTime(System.currentTimeMillis() - startTime.get())
|
||||
.userId(String.valueOf(StpUtil.getLoginId()))
|
||||
.userName(sessionVo.getUsername())
|
||||
.method(joinPoint.getTarget().getClass().getName() + "." + method.getName())
|
||||
.uri(request.getRequestURI())
|
||||
.ip(SystemUtils.getIp(request))
|
||||
.status(Constants.OPER_LOG_STATUS_SUCCESS)
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
Log log = method.getAnnotation(Log.class);
|
||||
if (Objects.nonNull(log)) {
|
||||
operLog.setModule(log.modul());
|
||||
operLog.setType(log.type());
|
||||
operLog.setMessage(log.desc());
|
||||
}
|
||||
|
||||
sysOperLogService.save(operLog);
|
||||
} catch (Exception e) {
|
||||
log.error("记录操作日志异常:{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @MonthName: doAfterThrowing
|
||||
* @Description: 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 14:36
|
||||
* @Param: [joinPoint, e]
|
||||
* @return: void
|
||||
**/
|
||||
@AfterThrowing(pointcut = "exceptionLogPoinCut()", throwing = "e")
|
||||
public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
|
||||
try {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
SysUserSessionVo sessionVo = (SysUserSessionVo) StpUtil.getSession().get(String.valueOf(StpUtil.getLoginId()));
|
||||
|
||||
SysOperLog operLog = SysOperLog.builder()
|
||||
.reqParam(JSON.toJSONString(converMap(request.getParameterMap())))
|
||||
.takeUpTime(System.currentTimeMillis() - startTime.get())
|
||||
.userId(String.valueOf(StpUtil.getLoginId()))
|
||||
.userName(sessionVo.getUsername())
|
||||
.method(joinPoint.getTarget().getClass().getName() + "." + method.getName())
|
||||
.uri(request.getRequestURI())
|
||||
.ip(SystemUtils.getIp(request))
|
||||
.status(Constants.OPER_LOG_STATUS_ERROR)
|
||||
.unusual(stackTraceToString(e.getClass().getName(), e.getMessage(), e.getStackTrace()))
|
||||
.createTime(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
Log log = method.getAnnotation(Log.class);
|
||||
if (Objects.nonNull(log)) {
|
||||
operLog.setModule(log.modul());
|
||||
operLog.setType(log.type());
|
||||
operLog.setMessage(log.desc());
|
||||
}
|
||||
|
||||
sysOperLogService.save(operLog);
|
||||
} catch (Exception e2) {
|
||||
log.error("记录操作日志异常:{}", e2);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> converMap(Map<String, String[]> paramMap) {
|
||||
Map<String, String> rtnMap = new HashMap<>();
|
||||
for (String key : paramMap.keySet()) {
|
||||
rtnMap.put(key, paramMap.get(key)[0]);
|
||||
}
|
||||
return rtnMap;
|
||||
}
|
||||
|
||||
public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
|
||||
StringBuffer strbuff = new StringBuffer();
|
||||
for (StackTraceElement stet : elements) {
|
||||
strbuff.append(stet + "<br/>");
|
||||
}
|
||||
String message = exceptionName + ":" + exceptionMessage + "<br/>" + strbuff.toString();
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -10,12 +10,23 @@ package com.tansci.common.constant;
|
||||
public class Constants {
|
||||
|
||||
/**
|
||||
* 未删除状态
|
||||
* 删除状态:0、正常,1、删除
|
||||
*/
|
||||
public final static Integer NOT_DEL_FALG = 0;
|
||||
/**
|
||||
* 已删除状态
|
||||
*/
|
||||
public final static Integer IS_DEL_FALG = 1;
|
||||
|
||||
/**
|
||||
* 操作日志状态:0、成功,1、失败
|
||||
*/
|
||||
public final static Integer OPER_LOG_STATUS_SUCCESS = 0;
|
||||
public final static Integer OPER_LOG_STATUS_ERROR = 1;
|
||||
|
||||
/**
|
||||
* 接口操作类型
|
||||
*/
|
||||
public final static String SELECT = "SELECT";
|
||||
public final static String INSERT = "INSERT";
|
||||
public final static String UPDATE = "INSERT";
|
||||
public final static String DELETE = "DELETE";
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ package com.tansci.controller;
|
||||
|
||||
import cn.dev33.satoken.session.SaSessionCustomUtil;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.captcha.CaptchaUtil;
|
||||
import cn.hutool.captcha.LineCaptcha;
|
||||
import com.tansci.common.WrapMapper;
|
||||
import com.tansci.common.Wrapper;
|
||||
import com.tansci.domain.SysUser;
|
||||
@ -13,6 +15,8 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @ClassName: AuthController.java
|
||||
* @ClassPath: com.tansci.controller.AuthController.java
|
||||
@ -31,8 +35,8 @@ public class AuthController {
|
||||
|
||||
@ApiOperation(value = "登录", notes = "登录")
|
||||
@PostMapping("/login")
|
||||
public Wrapper<SysUserVo> login(@RequestBody SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.login(user));
|
||||
public Wrapper<SysUserVo> login(HttpServletRequest request, @RequestBody SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.login(request, user));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "登出", notes = "登出")
|
||||
@ -43,4 +47,16 @@ public class AuthController {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, "登出成功!");
|
||||
}
|
||||
|
||||
@ApiOperation(value = "验证码", notes = "验证码")
|
||||
@GetMapping("/code")
|
||||
public Wrapper<Object> verificationCode(HttpServletRequest request) {
|
||||
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(100, 60);
|
||||
// 四则运算方式
|
||||
// lineCaptcha.setGenerator(new MathGenerator());
|
||||
// 验证码存session, 60秒过期
|
||||
request.getSession().setMaxInactiveInterval(60);
|
||||
request.getSession().setAttribute("verifyCode", lineCaptcha.getCode());
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, lineCaptcha.getImageBase64());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ package com.tansci.controller;
|
||||
|
||||
import com.tansci.common.WrapMapper;
|
||||
import com.tansci.common.Wrapper;
|
||||
import com.tansci.common.annotation.Log;
|
||||
import com.tansci.common.constant.Constants;
|
||||
import com.tansci.domain.SysMenu;
|
||||
import com.tansci.domain.vo.SysMenuVo;
|
||||
import com.tansci.service.SysMenuService;
|
||||
@ -32,12 +34,14 @@ public class SysMenuController {
|
||||
private SysMenuService sysMenuService;
|
||||
|
||||
@ApiOperation(value = "当前用户权限菜单树", notes = "当前用户权限菜单树")
|
||||
@Log(modul = "菜单管理-当前用户权限菜单树", type = Constants.SELECT, desc = "当前用户权限菜单树")
|
||||
@GetMapping("/tree")
|
||||
public Wrapper<List<SysMenu>> tree(SysMenu menu) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysMenuService.tree(menu));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "当前用户权限菜单列表", notes = "当前用户权限菜单列表")
|
||||
@Log(modul = "菜单管理-当前用户权限菜单列表", type = Constants.SELECT, desc = "当前用户权限菜单列表")
|
||||
@GetMapping("/menus")
|
||||
public Wrapper<List<SysMenuVo>> menus() {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysMenuService.menus());
|
||||
|
||||
@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.tansci.common.WrapMapper;
|
||||
import com.tansci.common.Wrapper;
|
||||
import com.tansci.common.annotation.Log;
|
||||
import com.tansci.common.constant.Constants;
|
||||
import com.tansci.domain.SysUser;
|
||||
import com.tansci.service.SysUserService;
|
||||
import io.swagger.annotations.Api;
|
||||
@ -31,36 +33,42 @@ public class SysUserController {
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@ApiOperation(value = "用户分页", notes = "用户分页")
|
||||
@Log(modul = "用户管理-用户分页", type = Constants.SELECT, desc = "用户分页")
|
||||
@GetMapping("/page")
|
||||
public Wrapper<IPage<SysUser>> page(Page page, SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.page(page, user));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "用户列表", notes = "用户列表")
|
||||
@Log(modul = "用户管理-用户列表", type = Constants.SELECT, desc = "用户列表")
|
||||
@GetMapping("/list")
|
||||
public Wrapper<List<SysUser>> list(SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.list(user));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "添加用户信息", notes = "添加用户信息")
|
||||
@Log(modul = "用户管理-添加用户信息", type = Constants.INSERT, desc = "添加用户信息")
|
||||
@PostMapping("/save")
|
||||
public Wrapper<Object> save(@RequestBody SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.insert(user));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "修改用户信息", notes = "修改用户信息")
|
||||
@Log(modul = "用户管理-修改用户信息", type = Constants.UPDATE, desc = "修改用户信息")
|
||||
@PostMapping("/update")
|
||||
public Wrapper<Object> update(@RequestBody SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.update(user));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "删除用户", notes = "删除用户")
|
||||
@Log(modul = "用户管理-删除用户", type = Constants.DELETE, desc = "删除用户")
|
||||
@GetMapping("/del")
|
||||
public Wrapper<Object> del(SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.del(user));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "修改密码", notes = "修改密码")
|
||||
@Log(modul = "用户管理-修改密码", type = Constants.UPDATE, desc = "修改密码")
|
||||
@PostMapping("/modifyPass")
|
||||
public Wrapper<Object> modifyPass(@RequestBody SysUser user) {
|
||||
return WrapMapper.wrap(Wrapper.SUCCESS_CODE, Wrapper.SUCCESS_MESSAGE, sysUserService.modifyPass(user));
|
||||
|
||||
@ -54,6 +54,9 @@ public class SysOperLog {
|
||||
@ApiModelProperty(value = "操作用户id")
|
||||
private String userId;
|
||||
|
||||
@ApiModelProperty(value = "状态:0、成功,1、失败")
|
||||
private Integer status;
|
||||
|
||||
@ApiModelProperty(value = "操作用户名称")
|
||||
private String userName;
|
||||
|
||||
@ -69,6 +72,9 @@ public class SysOperLog {
|
||||
@ApiModelProperty(value = "版本号")
|
||||
private String version;
|
||||
|
||||
@ApiModelProperty(value = "异常信息")
|
||||
private String unusual;
|
||||
|
||||
@ApiModelProperty(value = "创建时间")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", locale = "zh", timezone = "GMT+8")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@ -98,4 +98,8 @@ public class SysUser {
|
||||
@ApiModelProperty(value = "权限IDS")
|
||||
private List<String> roleIds;
|
||||
|
||||
@TableField(exist = false)
|
||||
@ApiModelProperty(value = "验证码")
|
||||
private String code;
|
||||
|
||||
}
|
||||
@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.tansci.domain.SysUser;
|
||||
import com.tansci.domain.vo.SysUserVo;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -27,7 +28,7 @@ public interface SysUserService extends IService<SysUser> {
|
||||
|
||||
Object del(SysUser user);
|
||||
|
||||
SysUserVo login(SysUser user);
|
||||
SysUserVo login(HttpServletRequest request, SysUser user);
|
||||
|
||||
Object modifyPass(SysUser user);
|
||||
|
||||
|
||||
@ -8,18 +8,22 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.tansci.common.constant.Constants;
|
||||
import com.tansci.common.exception.BusinessException;
|
||||
import com.tansci.domain.SysLoginLog;
|
||||
import com.tansci.domain.SysUser;
|
||||
import com.tansci.domain.SysUserRole;
|
||||
import com.tansci.domain.vo.SysUserSessionVo;
|
||||
import com.tansci.domain.vo.SysUserVo;
|
||||
import com.tansci.mapper.SysUserMapper;
|
||||
import com.tansci.service.SysLoginLogService;
|
||||
import com.tansci.service.SysUserRoleService;
|
||||
import com.tansci.service.SysUserService;
|
||||
import com.tansci.utils.Sha256Util;
|
||||
import com.tansci.utils.SystemUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -38,6 +42,8 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
|
||||
@Autowired
|
||||
private SysUserRoleService sysUserRoleService;
|
||||
@Autowired
|
||||
private SysLoginLogService sysLoginLogService;
|
||||
|
||||
@Override
|
||||
public IPage<SysUser> page(Page page, SysUser user) {
|
||||
@ -98,11 +104,35 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysUserVo login(SysUser user) {
|
||||
public SysUserVo login(HttpServletRequest request, SysUser user) {
|
||||
SysLoginLog loginLog = SysLoginLog.builder()
|
||||
.username(user.getUsername())
|
||||
.failPassword(user.getPassword())
|
||||
.type("成功")
|
||||
.ip(SystemUtils.getIp(request))
|
||||
.address(SystemUtils.getAddress(request))
|
||||
.browser(SystemUtils.getBrowser(request))
|
||||
.os(SystemUtils.getOS(request))
|
||||
.build();
|
||||
|
||||
try {
|
||||
// 校验验证码
|
||||
String code = (String) request.getSession().getAttribute("verifyCode");
|
||||
if (Objects.isNull(user.getCode()) || !Objects.equals(code, user.getCode())) {
|
||||
loginLog.setType("失败");
|
||||
throw new BusinessException("验证码有误,请重新获取!");
|
||||
}
|
||||
|
||||
SysUser sysUser = this.baseMapper.selectOne(Wrappers.<SysUser>lambdaQuery().eq(SysUser::getUsername, user.getUsername()));
|
||||
if (Objects.nonNull(sysUser) && Objects.equals(sysUser.getPassword(), Sha256Util.getSHA256(user.getPassword()))) {
|
||||
if (Objects.isNull(sysUser) && !Objects.equals(sysUser.getPassword(), Sha256Util.getSHA256(user.getPassword()))) {
|
||||
loginLog.setType("失败");
|
||||
throw new BusinessException("登录失败,用户名或密码有误!");
|
||||
}
|
||||
|
||||
// 生成token
|
||||
StpUtil.login(sysUser.getId());
|
||||
// 登录日志记录
|
||||
loginLog.setToken(StpUtil.getTokenInfo().getTokenValue());
|
||||
|
||||
// 用户角色到session
|
||||
List<SysUserRole> roles = sysUserRoleService.list(Wrappers.<SysUserRole>lambdaQuery().eq(SysUserRole::getUserId, sysUser.getId()));
|
||||
@ -126,8 +156,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
.loginTime(LocalDateTime.now())
|
||||
.token(StpUtil.getTokenInfo().getTokenValue())
|
||||
.build();
|
||||
} finally {
|
||||
sysLoginLogService.save(loginLog);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,11 @@
|
||||
package com.tansci.utils;
|
||||
|
||||
import cn.hutool.core.net.Ipv4Util;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.useragent.UserAgent;
|
||||
import cn.hutool.http.useragent.UserAgentUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
@ -20,48 +26,8 @@ public class SystemUtils {
|
||||
* @return: java.lang.String
|
||||
**/
|
||||
public static String getOS(HttpServletRequest request) {
|
||||
String osName = "";
|
||||
String userAgent = request.getHeader("User-Agent").toUpperCase();
|
||||
if (userAgent.contains("WINDOWS")) {
|
||||
if (userAgent.contains("WINDOWS NT 10.0")) {
|
||||
osName = "Windows 10";
|
||||
} else if (userAgent.contains("WINDOWS NT 6.3")) {
|
||||
osName = "Windows 8.1";
|
||||
} else if (userAgent.contains("WINDOWS NT 6.2")) {
|
||||
osName = "Windows 8";
|
||||
} else if (userAgent.contains("WINDOWS NT 6.1")) {
|
||||
osName = "Windows 7";
|
||||
} else if (userAgent.contains("WINDOWS NT 6.0")) {
|
||||
osName = "Windows Vista";
|
||||
} else if (userAgent.contains("WINDOWS NT 5.2")) {
|
||||
osName = "Windows XP";
|
||||
} else if (userAgent.contains("WINDOWS NT 5.1")) {
|
||||
osName = "Windows XP";
|
||||
} else if (userAgent.contains("WINDOWS NT 5.01")) {
|
||||
osName = "Windows 2000";
|
||||
} else if (userAgent.contains("WINDOWS NT 5.0")) {
|
||||
osName = "Windows 2000";
|
||||
} else if (userAgent.contains("WINDOWS NT 4.0")) {
|
||||
osName = "Windows NT 4.0";
|
||||
} else if (userAgent.contains("WINDOWS 98; WIN 9X 4.90")) {
|
||||
osName = "Windows ME";
|
||||
} else if (userAgent.contains("WINDOWS 98")) {
|
||||
osName = "Windows 98";
|
||||
} else if (userAgent.contains("WINDOWS 95")) {
|
||||
osName = "Windows 95";
|
||||
} else if (userAgent.contains("WINDOWS CE")) {
|
||||
osName = "Windows CE";
|
||||
}
|
||||
} else if (userAgent.contains("MAC")) {
|
||||
osName = "Mac";
|
||||
} else if (userAgent.contains("UNIX")) {
|
||||
osName = "UNIX";
|
||||
} else if (userAgent.contains("LINUX")) {
|
||||
osName = "Linux";
|
||||
} else if (userAgent.contains("SUNOS")) {
|
||||
osName = "SunOS";
|
||||
}
|
||||
return osName;
|
||||
UserAgent userAgent = UserAgentUtil.parse(request.getHeaders("User-Agent").toString());
|
||||
return userAgent.getBrowser().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,31 +39,12 @@ public class SystemUtils {
|
||||
* @return: java.lang.String
|
||||
**/
|
||||
public static String getBrowser(HttpServletRequest request) {
|
||||
String browserName = "";
|
||||
String userAgent = request.getHeader("User-Agent").toUpperCase();
|
||||
if (userAgent == null || userAgent.equals("")) {
|
||||
return "";
|
||||
}
|
||||
if (userAgent.indexOf("MSIE") > 0) {
|
||||
browserName = "IE";
|
||||
} else if (userAgent.indexOf("FIREFOX") > 0) {
|
||||
browserName = "Firefox";
|
||||
} else if (userAgent.indexOf("CHROME") > 0) {
|
||||
browserName = "Chrome";
|
||||
} else if (userAgent.indexOf("SAFARI") > 0) {
|
||||
browserName = "Safari";
|
||||
} else if (userAgent.indexOf("CAMINO") > 0) {
|
||||
browserName = "Camino";
|
||||
} else if (userAgent.indexOf("KONQUEROR") > 0) {
|
||||
browserName = "Konqueror";
|
||||
} else if (userAgent.indexOf("EDGE") > 0) {
|
||||
browserName = "Microsoft Edge";
|
||||
}
|
||||
return browserName;
|
||||
UserAgent userAgent = UserAgentUtil.parse(request.getHeaders("User-Agent").toString());
|
||||
return userAgent.getOs().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @methodName:getIpAddress
|
||||
* @methodName:getIp
|
||||
* @description:获取IP地址
|
||||
* @author:tanyp
|
||||
* @dateTime:2022/2/15 14:19
|
||||
@ -105,7 +52,7 @@ public class SystemUtils {
|
||||
* @Return: java.lang.String
|
||||
* @editNote:
|
||||
*/
|
||||
public static String getIpAddress(HttpServletRequest request) {
|
||||
public static String getIp(HttpServletRequest request) {
|
||||
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
@ -148,4 +95,26 @@ public class SystemUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @MonthName: getAddress
|
||||
* @Description: 获取地址
|
||||
* @Author: tanyp
|
||||
* @Date: 2023/4/7 11:24
|
||||
* @Param: [ip]
|
||||
* @return: java.lang.String
|
||||
**/
|
||||
public static String getAddress(HttpServletRequest request) {
|
||||
String ip = getIp(request);
|
||||
try {
|
||||
if (Ipv4Util.isInnerIP(ip)) {
|
||||
return "内网IP";
|
||||
}
|
||||
return JSONUtil.parseObj(HttpUtil.get("https://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip)).getStr("addr");
|
||||
} catch (IllegalArgumentException e) {
|
||||
return "内网IP";
|
||||
} catch (Exception e) {
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user