mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2026-01-01 18:05:28 +08:00
JeecgBoot 2.3 里程碑版本发布,支持微服务和单体自由切换、提供新行编辑表格JVXETable
This commit is contained in:
@ -0,0 +1,84 @@
|
||||
package org.jeecg.common.api;
|
||||
|
||||
import org.jeecg.common.system.vo.DynamicDataSourceModel;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
|
||||
import org.jeecg.common.system.vo.SysUserCacheInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public interface CommonAPI {
|
||||
|
||||
/**
|
||||
* 1查询用户角色信息
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
Set<String> queryUserRoles(String username);
|
||||
|
||||
|
||||
/**
|
||||
* 2查询用户权限信息
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
Set<String> queryUserAuths(String username);
|
||||
|
||||
/**
|
||||
* 3根据 id 查询数据库中存储的 DynamicDataSourceModel
|
||||
*
|
||||
* @param dbSourceId
|
||||
* @return
|
||||
*/
|
||||
DynamicDataSourceModel getDynamicDbSourceById(String dbSourceId);
|
||||
|
||||
/**
|
||||
* 4根据 code 查询数据库中存储的 DynamicDataSourceModel
|
||||
*
|
||||
* @param dbSourceCode
|
||||
* @return
|
||||
*/
|
||||
DynamicDataSourceModel getDynamicDbSourceByCode(String dbSourceCode);
|
||||
|
||||
/**
|
||||
* 5根据用户账号查询用户信息
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
public LoginUser getUserByName(String username);
|
||||
|
||||
|
||||
/**
|
||||
* 6字典表的 翻译
|
||||
* @param table
|
||||
* @param text
|
||||
* @param code
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
String translateDictFromTable(String table, String text, String code, String key);
|
||||
|
||||
/**
|
||||
* 7普通字典的翻译
|
||||
* @param code
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
String translateDict(String code, String key);
|
||||
|
||||
/**
|
||||
* 8查询数据权限
|
||||
* @return
|
||||
*/
|
||||
List<SysPermissionDataRuleModel> queryPermissionDataRule(String component, String requestPath, String username);
|
||||
|
||||
|
||||
/**
|
||||
* 9查询用户信息
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
SysUserCacheInfo getCacheUser(String username);
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package org.jeecg.common.api.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 文件下载
|
||||
* cloud api 用到的接口传输对象
|
||||
*/
|
||||
@Data
|
||||
public class FileDownDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 6749126258686446019L;
|
||||
|
||||
private String filePath;
|
||||
private String uploadpath;
|
||||
private String uploadType;
|
||||
private HttpServletResponse response;
|
||||
|
||||
public FileDownDTO(){}
|
||||
|
||||
public FileDownDTO(String filePath, String uploadpath, String uploadType,HttpServletResponse response){
|
||||
this.filePath = filePath;
|
||||
this.uploadpath = uploadpath;
|
||||
this.uploadType = uploadType;
|
||||
this.response = response;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
package org.jeecg.common.api.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
* cloud api 用到的接口传输对象
|
||||
*/
|
||||
@Data
|
||||
public class FileUploadDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -4111953058578954386L;
|
||||
|
||||
private MultipartFile file;
|
||||
|
||||
private String bizPath;
|
||||
|
||||
private String uploadType;
|
||||
|
||||
private String customBucket;
|
||||
|
||||
public FileUploadDTO(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单上传 构造器1
|
||||
* @param file
|
||||
* @param bizPath
|
||||
* @param uploadType
|
||||
*/
|
||||
public FileUploadDTO(MultipartFile file,String bizPath,String uploadType){
|
||||
this.file = file;
|
||||
this.bizPath = bizPath;
|
||||
this.uploadType = uploadType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 申明桶 文件上传 构造器2
|
||||
* @param file
|
||||
* @param bizPath
|
||||
* @param uploadType
|
||||
* @param customBucket
|
||||
*/
|
||||
public FileUploadDTO(MultipartFile file,String bizPath,String uploadType,String customBucket){
|
||||
this.file = file;
|
||||
this.bizPath = bizPath;
|
||||
this.uploadType = uploadType;
|
||||
this.customBucket = customBucket;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package org.jeecg.common.api.dto;
|
||||
import lombok.Data;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 日志对象
|
||||
* cloud api 用到的接口传输对象
|
||||
*/
|
||||
@Data
|
||||
public class LogDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8482720462943906924L;
|
||||
|
||||
/**内容*/
|
||||
private String logContent;
|
||||
|
||||
/**日志类型(0:操作日志;1:登录日志;2:定时任务) */
|
||||
private Integer logType;
|
||||
|
||||
/**操作类型(1:添加;2:修改;3:删除;) */
|
||||
private Integer operateType;
|
||||
|
||||
/**登录用户 */
|
||||
private LoginUser loginUser;
|
||||
|
||||
private String id;
|
||||
private String createBy;
|
||||
private Date createTime;
|
||||
private Long costTime;
|
||||
private String ip;
|
||||
|
||||
/**请求参数 */
|
||||
private String requestParam;
|
||||
|
||||
/**请求类型*/
|
||||
private String requestType;
|
||||
|
||||
/**请求路径*/
|
||||
private String requestUrl;
|
||||
|
||||
/**请求方法 */
|
||||
private String method;
|
||||
|
||||
/**操作人用户名称*/
|
||||
private String username;
|
||||
|
||||
/**操作人用户账户*/
|
||||
private String userid;
|
||||
|
||||
public LogDTO(){
|
||||
|
||||
}
|
||||
|
||||
public LogDTO(String logContent, Integer logType, Integer operatetype){
|
||||
this.logContent = logContent;
|
||||
this.logType = logType;
|
||||
this.operateType = operatetype;
|
||||
}
|
||||
|
||||
public LogDTO(String logContent, Integer logType, Integer operatetype, LoginUser loginUser){
|
||||
this.logContent = logContent;
|
||||
this.logType = logType;
|
||||
this.operateType = operatetype;
|
||||
this.loginUser = loginUser;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package org.jeecg.common.api.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* online 拦截器权限判断
|
||||
* cloud api 用到的接口传输对象
|
||||
*/
|
||||
@Data
|
||||
public class OnlineAuthDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1771827545416418203L;
|
||||
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 可能的请求地址
|
||||
*/
|
||||
private List<String> possibleUrl;
|
||||
|
||||
/**
|
||||
* online开发的菜单地址
|
||||
*/
|
||||
private String onlineFormUrl;
|
||||
|
||||
public OnlineAuthDTO(){
|
||||
|
||||
}
|
||||
|
||||
public OnlineAuthDTO(String username, List<String> possibleUrl, String onlineFormUrl){
|
||||
this.username = username;
|
||||
this.possibleUrl = possibleUrl;
|
||||
this.onlineFormUrl = onlineFormUrl;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package org.jeecg.common.api.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 带业务参数的消息
|
||||
*/
|
||||
@Data
|
||||
public class BusMessageDTO extends MessageDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 9104793287983367669L;
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private String busType;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String busId;
|
||||
|
||||
public BusMessageDTO(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 带业务参数的消息
|
||||
* @param fromUser
|
||||
* @param toUser
|
||||
* @param title
|
||||
* @param msgContent
|
||||
* @param msgCategory
|
||||
* @param busType
|
||||
* @param busId
|
||||
*/
|
||||
public BusMessageDTO(String fromUser, String toUser, String title, String msgContent, String msgCategory, String busType, String busId){
|
||||
super(fromUser, toUser, title, msgContent, msgCategory);
|
||||
this.busId = busId;
|
||||
this.busType = busType;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package org.jeecg.common.api.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 带业务参数的模板消息
|
||||
*/
|
||||
@Data
|
||||
public class BusTemplateMessageDTO extends TemplateMessageDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -4277810906346929459L;
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private String busType;
|
||||
|
||||
/**
|
||||
* 业务id
|
||||
*/
|
||||
private String busId;
|
||||
|
||||
public BusTemplateMessageDTO(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 带业务参数的模板消息
|
||||
* @param fromUser
|
||||
* @param toUser
|
||||
* @param title
|
||||
* @param templateParam
|
||||
* @param templateCode
|
||||
* @param busType
|
||||
* @param busId
|
||||
*/
|
||||
public BusTemplateMessageDTO(String fromUser, String toUser, String title, Map<String, String> templateParam, String templateCode, String busType, String busId){
|
||||
super(fromUser, toUser, title, templateParam, templateCode);
|
||||
this.busId = busId;
|
||||
this.busType = busType;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package org.jeecg.common.api.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 普通消息
|
||||
*/
|
||||
@Data
|
||||
public class MessageDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -5690444483968058442L;
|
||||
|
||||
/**
|
||||
* 发送人(用户登录账户)
|
||||
*/
|
||||
protected String fromUser;
|
||||
|
||||
/**
|
||||
* 发送给(用户登录账户)
|
||||
*/
|
||||
protected String toUser;
|
||||
|
||||
/**
|
||||
* 消息主题
|
||||
*/
|
||||
protected String title;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
protected String content;
|
||||
|
||||
/**
|
||||
* 消息类型 1:消息 2:系统消息
|
||||
*/
|
||||
protected String category;
|
||||
|
||||
|
||||
public MessageDTO(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造器1 系统消息
|
||||
*/
|
||||
public MessageDTO(String fromUser,String toUser,String title, String content){
|
||||
this.fromUser = fromUser;
|
||||
this.toUser = toUser;
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
//默认 都是2系统消息
|
||||
this.category = CommonConstant.MSG_CATEGORY_2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造器2 支持设置category 1:消息 2:系统消息
|
||||
*/
|
||||
public MessageDTO(String fromUser,String toUser,String title, String content, String category){
|
||||
this.fromUser = fromUser;
|
||||
this.toUser = toUser;
|
||||
this.title = title;
|
||||
this.content = content;
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package org.jeecg.common.api.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 消息模板dto
|
||||
*/
|
||||
@Data
|
||||
public class TemplateDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 5848247133907528650L;
|
||||
|
||||
/**
|
||||
* 模板编码
|
||||
*/
|
||||
protected String templateCode;
|
||||
|
||||
/**
|
||||
* 模板参数
|
||||
*/
|
||||
protected Map<String, String> templateParam;
|
||||
|
||||
/**
|
||||
* 构造器 通过设置模板参数和模板编码 作为参数获取消息内容
|
||||
*/
|
||||
public TemplateDTO(String templateCode, Map<String, String> templateParam){
|
||||
this.templateCode = templateCode;
|
||||
this.templateParam = templateParam;
|
||||
}
|
||||
|
||||
public TemplateDTO(){
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package org.jeecg.common.api.dto.message;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 模板消息
|
||||
*/
|
||||
@Data
|
||||
public class TemplateMessageDTO extends TemplateDTO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 411137565170647585L;
|
||||
|
||||
|
||||
/**
|
||||
* 发送人(用户登录账户)
|
||||
*/
|
||||
protected String fromUser;
|
||||
|
||||
/**
|
||||
* 发送给(用户登录账户)
|
||||
*/
|
||||
protected String toUser;
|
||||
|
||||
/**
|
||||
* 消息主题
|
||||
*/
|
||||
protected String title;
|
||||
|
||||
|
||||
public TemplateMessageDTO(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造器1 发模板消息用
|
||||
*/
|
||||
public TemplateMessageDTO(String fromUser, String toUser,String title, Map<String, String> templateParam, String templateCode){
|
||||
super(templateCode, templateParam);
|
||||
this.fromUser = fromUser;
|
||||
this.toUser = toUser;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -33,7 +33,7 @@ public class OaWpsModel implements Serializable {
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@TableId(type = IdType.ID_WORKER_STR)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@ApiModelProperty(value = "id")
|
||||
private String id;
|
||||
/**
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package org.jeecg.common.api.vo;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
@ -58,8 +60,8 @@ public class Result<T> implements Serializable {
|
||||
this.success = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static Result<Object> ok() {
|
||||
Result<Object> r = new Result<Object>();
|
||||
r.setSuccess(true);
|
||||
@ -67,7 +69,8 @@ public class Result<T> implements Serializable {
|
||||
r.setMessage("成功");
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static Result<Object> ok(String msg) {
|
||||
Result<Object> r = new Result<Object>();
|
||||
r.setSuccess(true);
|
||||
@ -75,7 +78,8 @@ public class Result<T> implements Serializable {
|
||||
r.setMessage(msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
public static Result<Object> ok(Object data) {
|
||||
Result<Object> r = new Result<Object>();
|
||||
r.setSuccess(true);
|
||||
@ -83,6 +87,31 @@ public class Result<T> implements Serializable {
|
||||
r.setResult(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static<T> Result<T> OK() {
|
||||
Result<T> r = new Result<T>();
|
||||
r.setSuccess(true);
|
||||
r.setCode(CommonConstant.SC_OK_200);
|
||||
r.setMessage("成功");
|
||||
return r;
|
||||
}
|
||||
|
||||
public static<T> Result<T> OK(T data) {
|
||||
Result<T> r = new Result<T>();
|
||||
r.setSuccess(true);
|
||||
r.setCode(CommonConstant.SC_OK_200);
|
||||
r.setResult(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static<T> Result<T> OK(String msg, T data) {
|
||||
Result<T> r = new Result<T>();
|
||||
r.setSuccess(true);
|
||||
r.setCode(CommonConstant.SC_OK_200);
|
||||
r.setMessage(msg);
|
||||
r.setResult(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
public static Result<Object> error(String msg) {
|
||||
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
|
||||
@ -108,4 +137,8 @@ public class Result<T> implements Serializable {
|
||||
public static Result<Object> noauth(String msg) {
|
||||
return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
private String onlTable;
|
||||
|
||||
}
|
||||
@ -0,0 +1,269 @@
|
||||
package org.jeecg.common.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.serializer.PropertyFilter;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.jeecg.common.api.dto.LogDTO;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.ModuleType;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.IPUtils;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* 系统日志,切面处理类
|
||||
*
|
||||
* @Author scott
|
||||
* @email jeecgos@163.com
|
||||
* @Date 2018年1月14日
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class AutoLogAspect {
|
||||
|
||||
@Resource
|
||||
private BaseCommonService baseCommonService;
|
||||
|
||||
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.AutoLog)")
|
||||
public void logPointCut() {
|
||||
|
||||
}
|
||||
|
||||
@Around("logPointCut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
long beginTime = System.currentTimeMillis();
|
||||
//执行方法
|
||||
Object result = point.proceed();
|
||||
//执行时长(毫秒)
|
||||
long time = System.currentTimeMillis() - beginTime;
|
||||
|
||||
//保存日志
|
||||
saveSysLog(point, time, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
LogDTO dto = new LogDTO();
|
||||
AutoLog syslog = method.getAnnotation(AutoLog.class);
|
||||
if(syslog != null){
|
||||
//update-begin-author:taoyan date:
|
||||
String content = syslog.value();
|
||||
if(syslog.module()== ModuleType.ONLINE){
|
||||
content = getOnlineLogContent(obj, content);
|
||||
}
|
||||
//注解上的描述,操作日志内容
|
||||
dto.setLogType(syslog.logType());
|
||||
dto.setLogContent(content);
|
||||
}
|
||||
|
||||
//请求的方法名
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
String methodName = signature.getName();
|
||||
dto.setMethod(className + "." + methodName + "()");
|
||||
|
||||
|
||||
//设置操作类型
|
||||
if (dto.getLogType() == CommonConstant.LOG_TYPE_2) {
|
||||
dto.setOperateType(getOperateType(methodName, syslog.operateType()));
|
||||
}
|
||||
|
||||
//获取request
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
//请求的参数
|
||||
dto.setRequestParam(getReqestParams(request,joinPoint));
|
||||
//设置IP地址
|
||||
dto.setIp(IPUtils.getIpAddr(request));
|
||||
//获取登录用户信息
|
||||
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
if(sysUser!=null){
|
||||
dto.setUserid(sysUser.getUsername());
|
||||
dto.setUsername(sysUser.getRealname());
|
||||
|
||||
}
|
||||
//耗时
|
||||
dto.setCostTime(time);
|
||||
dto.setCreateTime(new Date());
|
||||
//保存系统日志
|
||||
baseCommonService.addLog(dto);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取操作类型
|
||||
*/
|
||||
private int getOperateType(String methodName,int operateType) {
|
||||
if (operateType > 0) {
|
||||
return operateType;
|
||||
}
|
||||
if (methodName.startsWith("list")) {
|
||||
return CommonConstant.OPERATE_TYPE_1;
|
||||
}
|
||||
if (methodName.startsWith("add")) {
|
||||
return CommonConstant.OPERATE_TYPE_2;
|
||||
}
|
||||
if (methodName.startsWith("edit")) {
|
||||
return CommonConstant.OPERATE_TYPE_3;
|
||||
}
|
||||
if (methodName.startsWith("delete")) {
|
||||
return CommonConstant.OPERATE_TYPE_4;
|
||||
}
|
||||
if (methodName.startsWith("import")) {
|
||||
return CommonConstant.OPERATE_TYPE_5;
|
||||
}
|
||||
if (methodName.startsWith("export")) {
|
||||
return CommonConstant.OPERATE_TYPE_6;
|
||||
}
|
||||
return CommonConstant.OPERATE_TYPE_1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Description: 获取请求参数
|
||||
* @author: scott
|
||||
* @date: 2020/4/16 0:10
|
||||
* @param request: request
|
||||
* @param joinPoint: joinPoint
|
||||
* @Return: java.lang.String
|
||||
*/
|
||||
private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
|
||||
String httpMethod = request.getMethod();
|
||||
String params = "";
|
||||
if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
|
||||
Object[] paramsArray = joinPoint.getArgs();
|
||||
// java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
|
||||
// https://my.oschina.net/mengzhang6/blog/2395893
|
||||
Object[] arguments = new Object[paramsArray.length];
|
||||
for (int i = 0; i < paramsArray.length; i++) {
|
||||
if (paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
|
||||
//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
|
||||
//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response
|
||||
continue;
|
||||
}
|
||||
arguments[i] = paramsArray[i];
|
||||
}
|
||||
//update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
|
||||
PropertyFilter profilter = new PropertyFilter() {
|
||||
@Override
|
||||
public boolean apply(Object o, String name, Object value) {
|
||||
if(value!=null && value.toString().length()>500){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
params = JSONObject.toJSONString(arguments, profilter);
|
||||
//update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
|
||||
} else {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
// 请求的方法参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
// 请求的方法参数名称
|
||||
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
|
||||
String[] paramNames = u.getParameterNames(method);
|
||||
if (args != null && paramNames != null) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
params += " " + paramNames[i] + ": " + args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* online日志内容拼接
|
||||
* @param obj
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
private String getOnlineLogContent(Object obj, String content){
|
||||
if (Result.class.isInstance(obj)){
|
||||
Result res = (Result)obj;
|
||||
String msg = res.getMessage();
|
||||
String tableName = res.getOnlTable();
|
||||
if(oConvertUtils.isNotEmpty(tableName)){
|
||||
content+=",表名:"+tableName;
|
||||
}
|
||||
if(res.isSuccess()){
|
||||
content+= ","+(oConvertUtils.isEmpty(msg)?"操作成功":msg);
|
||||
}else{
|
||||
content+= ","+(oConvertUtils.isEmpty(msg)?"操作失败":msg);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/* private void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj) {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
SysLog sysLog = new SysLog();
|
||||
AutoLog syslog = method.getAnnotation(AutoLog.class);
|
||||
if(syslog != null){
|
||||
//update-begin-author:taoyan date:
|
||||
String content = syslog.value();
|
||||
if(syslog.module()== ModuleType.ONLINE){
|
||||
content = getOnlineLogContent(obj, content);
|
||||
}
|
||||
//注解上的描述,操作日志内容
|
||||
sysLog.setLogContent(content);
|
||||
sysLog.setLogType(syslog.logType());
|
||||
}
|
||||
|
||||
//请求的方法名
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
String methodName = signature.getName();
|
||||
sysLog.setMethod(className + "." + methodName + "()");
|
||||
|
||||
|
||||
//设置操作类型
|
||||
if (sysLog.getLogType() == CommonConstant.LOG_TYPE_2) {
|
||||
sysLog.setOperateType(getOperateType(methodName, syslog.operateType()));
|
||||
}
|
||||
|
||||
//获取request
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
//请求的参数
|
||||
sysLog.setRequestParam(getReqestParams(request,joinPoint));
|
||||
|
||||
//设置IP地址
|
||||
sysLog.setIp(IPUtils.getIpAddr(request));
|
||||
|
||||
//获取登录用户信息
|
||||
LoginUser sysUser = (LoginUser)SecurityUtils.getSubject().getPrincipal();
|
||||
if(sysUser!=null){
|
||||
sysLog.setUserid(sysUser.getUsername());
|
||||
sysLog.setUsername(sysUser.getRealname());
|
||||
|
||||
}
|
||||
//耗时
|
||||
sysLog.setCostTime(time);
|
||||
sysLog.setCreateTime(new Date());
|
||||
//保存系统日志
|
||||
sysLogService.save(sysLog);
|
||||
}*/
|
||||
}
|
||||
@ -0,0 +1,164 @@
|
||||
package org.jeecg.common.aspect;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.Dict;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Description: 字典aop类
|
||||
* @Author: dangzhenghui
|
||||
* @Date: 2019-3-17 21:50
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DictAspect {
|
||||
|
||||
@Autowired
|
||||
private CommonAPI commonAPI;
|
||||
|
||||
// 定义切点Pointcut
|
||||
@Pointcut("execution(public * org.jeecg.modules..*.*Controller.*(..))")
|
||||
public void excudeService() {
|
||||
}
|
||||
|
||||
@Around("excudeService()")
|
||||
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
||||
long time1=System.currentTimeMillis();
|
||||
Object result = pjp.proceed();
|
||||
long time2=System.currentTimeMillis();
|
||||
log.debug("获取JSON数据 耗时:"+(time2-time1)+"ms");
|
||||
long start=System.currentTimeMillis();
|
||||
this.parseDictText(result);
|
||||
long end=System.currentTimeMillis();
|
||||
log.debug("解析注入JSON数据 耗时"+(end-start)+"ms");
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本方法针对返回对象为Result 的IPage的分页列表数据进行动态字典注入
|
||||
* 字典注入实现 通过对实体类添加注解@dict 来标识需要的字典内容,字典分为单字典code即可 ,table字典 code table text配合使用与原来jeecg的用法相同
|
||||
* 示例为SysUser 字段为sex 添加了注解@Dict(dicCode = "sex") 会在字典服务立马查出来对应的text 然后在请求list的时候将这个字典text,已字段名称加_dictText形式返回到前端
|
||||
* 例输入当前返回值的就会多出一个sex_dictText字段
|
||||
* {
|
||||
* sex:1,
|
||||
* sex_dictText:"男"
|
||||
* }
|
||||
* 前端直接取值sext_dictText在table里面无需再进行前端的字典转换了
|
||||
* customRender:function (text) {
|
||||
* if(text==1){
|
||||
* return "男";
|
||||
* }else if(text==2){
|
||||
* return "女";
|
||||
* }else{
|
||||
* return text;
|
||||
* }
|
||||
* }
|
||||
* 目前vue是这么进行字典渲染到table上的多了就很麻烦了 这个直接在服务端渲染完成前端可以直接用
|
||||
* @param result
|
||||
*/
|
||||
private void parseDictText(Object result) {
|
||||
if (result instanceof Result) {
|
||||
if (((Result) result).getResult() instanceof IPage) {
|
||||
List<JSONObject> items = new ArrayList<>();
|
||||
for (Object record : ((IPage) ((Result) result).getResult()).getRecords()) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
String json="{}";
|
||||
try {
|
||||
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
|
||||
json = mapper.writeValueAsString(record);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("json解析失败"+e.getMessage(),e);
|
||||
}
|
||||
JSONObject item = JSONObject.parseObject(json);
|
||||
//update-begin--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
|
||||
//for (Field field : record.getClass().getDeclaredFields()) {
|
||||
for (Field field : oConvertUtils.getAllFields(record)) {
|
||||
//update-end--Author:scott -- Date:20190603 ----for:解决继承实体字段无法翻译问题------
|
||||
if (field.getAnnotation(Dict.class) != null) {
|
||||
String code = field.getAnnotation(Dict.class).dicCode();
|
||||
String text = field.getAnnotation(Dict.class).dicText();
|
||||
String table = field.getAnnotation(Dict.class).dictTable();
|
||||
String key = String.valueOf(item.get(field.getName()));
|
||||
|
||||
//翻译字典值对应的txt
|
||||
String textValue = translateDictValue(code, text, table, key);
|
||||
|
||||
log.debug(" 字典Val : "+ textValue);
|
||||
log.debug(" __翻译字典字段__ "+field.getName() + CommonConstant.DICT_TEXT_SUFFIX+": "+ textValue);
|
||||
item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
|
||||
}
|
||||
//date类型默认转换string格式化日期
|
||||
if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
|
||||
SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
|
||||
}
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
((IPage) ((Result) result).getResult()).setRecords(items);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 翻译字典文本
|
||||
* @param code
|
||||
* @param text
|
||||
* @param table
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private String translateDictValue(String code, String text, String table, String key) {
|
||||
if(oConvertUtils.isEmpty(key)) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer textValue=new StringBuffer();
|
||||
String[] keys = key.split(",");
|
||||
for (String k : keys) {
|
||||
String tmpValue = null;
|
||||
log.debug(" 字典 key : "+ k);
|
||||
if (k.trim().length() == 0) {
|
||||
continue; //跳过循环
|
||||
}
|
||||
if (!StringUtils.isEmpty(table)){
|
||||
log.debug("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
|
||||
tmpValue= commonAPI.translateDictFromTable(table,text,code,k.trim());
|
||||
}else {
|
||||
tmpValue = commonAPI.translateDict(code, k.trim());
|
||||
}
|
||||
if (tmpValue != null) {
|
||||
if (!"".equals(textValue.toString())) {
|
||||
textValue.append(",");
|
||||
}
|
||||
textValue.append(tmpValue);
|
||||
}
|
||||
|
||||
}
|
||||
return textValue.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
package org.jeecg.common.aspect;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.aspect.annotation.PermissionData;
|
||||
import org.jeecg.common.system.util.JeecgDataAutorUtils;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
|
||||
import org.jeecg.common.system.vo.SysUserCacheInfo;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据权限切面处理类
|
||||
* 当被请求的方法有注解PermissionData时,会在往当前request中写入数据权限信息
|
||||
* @Date 2019年4月10日
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
public class PermissionDataAspect {
|
||||
|
||||
@Autowired
|
||||
private CommonAPI commonAPI;
|
||||
|
||||
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.PermissionData)")
|
||||
public void pointCut() {
|
||||
|
||||
}
|
||||
|
||||
@Around("pointCut()")
|
||||
public Object arround(ProceedingJoinPoint point) throws Throwable{
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
PermissionData pd = method.getAnnotation(PermissionData.class);
|
||||
String component = pd.pageComponent();
|
||||
|
||||
String requestMethod = request.getMethod();
|
||||
String requestPath = request.getRequestURI().substring(request.getContextPath().length());
|
||||
requestPath = filterUrl(requestPath);
|
||||
log.info("拦截请求 >> "+requestPath+";请求类型 >> "+requestMethod);
|
||||
String username = JwtUtil.getUserNameByToken(request);
|
||||
//查询数据权限信息
|
||||
//TODO 微服务情况下也得支持缓存机制
|
||||
List<SysPermissionDataRuleModel> dataRules = commonAPI.queryPermissionDataRule(component, requestPath, username);
|
||||
if(dataRules!=null && dataRules.size()>0) {
|
||||
//临时存储
|
||||
JeecgDataAutorUtils.installDataSearchConditon(request, dataRules);
|
||||
//TODO 微服务情况下也得支持缓存机制
|
||||
SysUserCacheInfo userinfo = commonAPI.getCacheUser(username);
|
||||
JeecgDataAutorUtils.installUserInfo(request, userinfo);
|
||||
}
|
||||
return point.proceed();
|
||||
}
|
||||
|
||||
private String filterUrl(String requestPath){
|
||||
String url = "";
|
||||
if(oConvertUtils.isNotEmpty(requestPath)){
|
||||
url = requestPath.replace("\\", "/");
|
||||
url = requestPath.replace("//", "/");
|
||||
if(url.indexOf("//")>=0){
|
||||
url = filterUrl(url);
|
||||
}
|
||||
/*if(url.startsWith("/")){
|
||||
url=url.substring(1);
|
||||
}*/
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求地址
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private String getJgAuthRequsetPath(HttpServletRequest request) {
|
||||
String queryString = request.getQueryString();
|
||||
String requestPath = request.getRequestURI();
|
||||
if(oConvertUtils.isNotEmpty(queryString)){
|
||||
requestPath += "?" + queryString;
|
||||
}
|
||||
if (requestPath.indexOf("&") > -1) {// 去掉其他参数(保留一个参数) 例如:loginController.do?login
|
||||
requestPath = requestPath.substring(0, requestPath.indexOf("&"));
|
||||
}
|
||||
if(requestPath.indexOf("=")!=-1){
|
||||
if(requestPath.indexOf(".do")!=-1){
|
||||
requestPath = requestPath.substring(0,requestPath.indexOf(".do")+3);
|
||||
}else{
|
||||
requestPath = requestPath.substring(0,requestPath.indexOf("?"));
|
||||
}
|
||||
}
|
||||
requestPath = requestPath.substring(request.getContextPath().length() + 1);// 去掉项目路径
|
||||
return filterUrl(requestPath);
|
||||
}
|
||||
|
||||
private boolean moHuContain(List<String> list,String key){
|
||||
for(String str : list){
|
||||
if(key.contains(str)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package org.jeecg.common.aspect;
|
||||
|
||||
/**
|
||||
* @Author scott
|
||||
* @Date 2020/1/14 13:36
|
||||
* @Description: 请求URL与菜单路由URL转换规则(方便于采用菜单路由URL来配置数据权限规则)
|
||||
*/
|
||||
public enum UrlMatchEnum {
|
||||
CGFORM_DATA("/online/cgform/api/getData/", "/online/cgformList/"),
|
||||
CGFORM_EXCEL_DATA("/online/cgform/api/exportXls/", "/online/cgformList/"),
|
||||
CGFORM_TREE_DATA("/online/cgform/api/getTreeData/", "/online/cgformList/"),
|
||||
CGREPORT_DATA("/online/cgreport/api/getColumnsAndData/", "/online/cgreport/"),
|
||||
CGREPORT_EXCEL_DATA("/online/cgreport/api/exportXls/", "/online/cgreport/");
|
||||
|
||||
|
||||
UrlMatchEnum(String url, String match_url) {
|
||||
this.url = url;
|
||||
this.match_url = match_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request 请求 URL前缀
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 菜单路由 URL前缀 (对应菜单路径)
|
||||
*/
|
||||
private String match_url;
|
||||
|
||||
/**
|
||||
* 根据req url 获取到菜单配置路径(前端页面路由URL)
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
public static String getMatchResultByUrl(String url) {
|
||||
//获取到枚举
|
||||
UrlMatchEnum[] values = UrlMatchEnum.values();
|
||||
//加强for循环进行遍历操作
|
||||
for (UrlMatchEnum lr : values) {
|
||||
//如果遍历获取的type和参数type一致
|
||||
if (url.indexOf(lr.url) != -1) {
|
||||
//返回type对象的desc
|
||||
return url.replace(lr.url, lr.match_url);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
/**
|
||||
* 比如request真实请求URL: /online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a
|
||||
* 转换匹配路由URL后(对应配置的菜单路径):/online/cgformList/81fcf7d8922d45069b0d5ba983612d3a
|
||||
*/
|
||||
System.out.println(UrlMatchEnum.getMatchResultByUrl("/online/cgform/api/getData/81fcf7d8922d45069b0d5ba983612d3a"));
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
package org.jeecg.common.aspect.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.ModuleType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 系统日志注解
|
||||
@ -40,4 +37,10 @@ public @interface AutoLog {
|
||||
* @return (1查询,2添加,3修改,4删除)
|
||||
*/
|
||||
int operateType() default 0;
|
||||
|
||||
/**
|
||||
* 模块类型 默认为common
|
||||
* @return
|
||||
*/
|
||||
ModuleType module() default ModuleType.COMMON;
|
||||
}
|
||||
|
||||
@ -7,6 +7,11 @@ package org.jeecg.common.constant;
|
||||
*/
|
||||
public interface CacheConstant {
|
||||
|
||||
/**
|
||||
* 缓存用户jwt
|
||||
*/
|
||||
public static final String SYS_USERS_CACHE_JWT = "sys:cache:user:jwt";
|
||||
|
||||
/**
|
||||
* 字典信息缓存
|
||||
*/
|
||||
@ -15,6 +20,7 @@ public interface CacheConstant {
|
||||
* 表字典信息缓存
|
||||
*/
|
||||
public static final String SYS_DICT_TABLE_CACHE = "sys:cache:dictTable";
|
||||
public static final String SYS_DICT_TABLE_BY_KEYS_CACHE = SYS_DICT_TABLE_CACHE + "ByKeys";
|
||||
|
||||
/**
|
||||
* 数据权限配置缓存
|
||||
@ -48,4 +54,14 @@ public interface CacheConstant {
|
||||
*/
|
||||
public static final String SYS_DYNAMICDB_CACHE = "sys:cache:dbconnect:dynamic:";
|
||||
|
||||
/**
|
||||
* gateway路由缓存
|
||||
*/
|
||||
public static final String GATEWAY_ROUTES = "geteway_routes";
|
||||
|
||||
|
||||
/**
|
||||
* gateway路由 reload key
|
||||
*/
|
||||
public static final String ROUTE_JVM_RELOAD_TOPIC = "gateway_jvm_route_reload_topic";
|
||||
}
|
||||
|
||||
@ -257,6 +257,16 @@ public interface CommonConstant {
|
||||
*/
|
||||
public static final String IM_SOCKET_TYPE = "chatMessage";
|
||||
|
||||
/**
|
||||
* 在线聊天 是否开启默认添加好友 1是 0否
|
||||
*/
|
||||
public static final String IM_DEFAULT_ADD_FRIEND = "1";
|
||||
|
||||
/**
|
||||
* 在线聊天 用户好友缓存前缀
|
||||
*/
|
||||
public static final String IM_PREFIX_USER_FRIEND_CACHE = "im_prefix_user_friend_";
|
||||
|
||||
/**
|
||||
* 考勤补卡业务状态 (1:同意 2:不同意)
|
||||
*/
|
||||
@ -277,4 +287,17 @@ public interface CommonConstant {
|
||||
*/
|
||||
public static final String WPS_TYPE_1="1";
|
||||
public static final String WPS_TYPE_2="2";
|
||||
|
||||
|
||||
public final static String X_ACCESS_TOKEN = "X-Access-Token";
|
||||
|
||||
/**
|
||||
* 多租户 请求头
|
||||
*/
|
||||
public final static String TENANT_ID = "tenant_id";
|
||||
|
||||
/**
|
||||
* 微服务读取配置文件属性 服务地址
|
||||
*/
|
||||
public final static String CLOUD_SERVER_KEY = "spring.cloud.nacos.discovery.server-addr";
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright (c) 2019-2020, 冷冷 (wangiegie@gmail.com).
|
||||
* * <p>
|
||||
* * Licensed under the GNU Lesser General Public License 3.0 (the "License");
|
||||
* * you may not use this file except in compliance with the License.
|
||||
* * You may obtain a copy of the License at
|
||||
* * <p>
|
||||
* * https://www.gnu.org/licenses/lgpl.html
|
||||
* * <p>
|
||||
* * Unless required by applicable law or agreed to in writing, software
|
||||
* * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* * See the License for the specific language governing permissions and
|
||||
* * limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.jeecg.common.constant;
|
||||
|
||||
/**
|
||||
* @author scott
|
||||
* @date 2019年05月18日
|
||||
* 服务名称
|
||||
*/
|
||||
public interface ServiceNameConstants {
|
||||
|
||||
/**
|
||||
* 系统管理 admin
|
||||
*/
|
||||
String SYSTEM_SERVICE = "jeecg-system";
|
||||
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package org.jeecg.common.constant;
|
||||
|
||||
/**
|
||||
* VXESocket 常量
|
||||
*/
|
||||
public class VXESocketConst {
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*/
|
||||
public static final String TYPE = "type";
|
||||
/**
|
||||
* 消息数据
|
||||
*/
|
||||
public static final String DATA = "data";
|
||||
|
||||
/**
|
||||
* 消息类型:心跳检测
|
||||
*/
|
||||
public static final String TYPE_HB = "heart_beat";
|
||||
/**
|
||||
* 消息类型:通用数据传递
|
||||
*/
|
||||
public static final String TYPE_CSD = "common_send_date";
|
||||
/**
|
||||
* 消息类型:更新vxe table数据
|
||||
*/
|
||||
public static final String TYPE_UVT = "update_vxe_table";
|
||||
|
||||
}
|
||||
@ -22,10 +22,18 @@ public enum CgformEnum {
|
||||
* 多表
|
||||
*/
|
||||
ERP(2, "erp", "/jeecg/code-template-online", "erp.onetomany", "ERP风格"),
|
||||
/**
|
||||
* 多表(jvxe风格)
|
||||
* */
|
||||
JVXE_TABLE(2, "jvxe", "/jeecg/code-template-online", "jvxe.onetomany", "JVXE风格"),
|
||||
/**
|
||||
* 多表(内嵌子表风格)
|
||||
*/
|
||||
INNER_TABLE(2, "innerTable", "/jeecg/code-template-online", "inner-table.onetomany", "内嵌子表风格"),
|
||||
/**
|
||||
* 多表(tab风格)
|
||||
* */
|
||||
TAB(2, "tab", "/jeecg/code-template-online", "tab.onetomany", "Tab风格"),
|
||||
/**
|
||||
* 树形列表
|
||||
*/
|
||||
@ -55,9 +63,10 @@ public enum CgformEnum {
|
||||
/**
|
||||
* 构造器
|
||||
*
|
||||
* @param type
|
||||
* @param code
|
||||
* @param templatePath
|
||||
* @param type 类型 1/单表 2/一对多 3/树
|
||||
* @param code 模板编码
|
||||
* @param templatePath 模板路径
|
||||
* @param stylePath 模板子路径
|
||||
* @param note
|
||||
*/
|
||||
CgformEnum(int type, String code, String templatePath, String stylePath, String note) {
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package org.jeecg.common.constant.enums;
|
||||
|
||||
/**
|
||||
* 日志按模块分类
|
||||
*/
|
||||
public enum ModuleType {
|
||||
|
||||
/**
|
||||
* 普通
|
||||
*/
|
||||
COMMON,
|
||||
|
||||
/**
|
||||
* online
|
||||
*/
|
||||
ONLINE;
|
||||
}
|
||||
@ -1,336 +0,0 @@
|
||||
package org.jeecg.common.system.api;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||
import org.jeecg.common.system.vo.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @Description: 底层共通业务API,提供其他独立模块调用
|
||||
* @Author: scott
|
||||
* @Date:2019-4-20
|
||||
* @Version:V1.0
|
||||
*/
|
||||
public interface ISysBaseAPI {
|
||||
|
||||
/**
|
||||
* 日志添加
|
||||
* @param LogContent 内容
|
||||
* @param logType 日志类型(0:操作日志;1:登录日志;2:定时任务)
|
||||
* @param operatetype 操作类型(1:添加;2:修改;3:删除;)
|
||||
*/
|
||||
void addLog(String LogContent, Integer logType, Integer operatetype);
|
||||
|
||||
/**
|
||||
* 根据用户账号查询用户信息
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
public LoginUser getUserByName(String username);
|
||||
|
||||
/**
|
||||
* 根据用户id查询用户信息
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LoginUser getUserById(String id);
|
||||
|
||||
/**
|
||||
* 通过用户账号查询角色集合
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
public List<String> getRolesByUsername(String username);
|
||||
|
||||
/**
|
||||
* 通过用户账号查询部门集合
|
||||
* @param username
|
||||
* @return 部门 id
|
||||
*/
|
||||
List<String> getDepartIdsByUsername(String username);
|
||||
|
||||
/**
|
||||
* 通过用户账号查询部门 name
|
||||
* @param username
|
||||
* @return 部门 name
|
||||
*/
|
||||
List<String> getDepartNamesByUsername(String username);
|
||||
|
||||
/**
|
||||
* 获取当前数据库类型
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public String getDatabaseType() throws SQLException;
|
||||
|
||||
/**
|
||||
* 获取数据字典
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
public List<DictModel> queryDictItemsByCode(String code);
|
||||
|
||||
/** 查询所有的父级字典,按照create_time排序 */
|
||||
public List<DictModel> queryAllDict();
|
||||
|
||||
/**
|
||||
* 查询所有分类字典
|
||||
* @return
|
||||
*/
|
||||
public List<SysCategoryModel> queryAllDSysCategory();
|
||||
|
||||
/**
|
||||
* 获取表数据字典
|
||||
* @param table
|
||||
* @param text
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
List<DictModel> queryTableDictItemsByCode(String table, String text, String code);
|
||||
|
||||
/**
|
||||
* 查询所有部门 作为字典信息 id -->value,departName -->text
|
||||
* @return
|
||||
*/
|
||||
public List<DictModel> queryAllDepartBackDictModel();
|
||||
|
||||
/**
|
||||
* 查询所有部门,拼接查询条件
|
||||
* @return
|
||||
*/
|
||||
List<JSONObject> queryAllDepart(Wrapper wrapper);
|
||||
|
||||
/**
|
||||
* 发送系统消息
|
||||
* @param fromUser 发送人(用户登录账户)
|
||||
* @param toUser 发送给(用户登录账户)
|
||||
* @param title 消息主题
|
||||
* @param msgContent 消息内容
|
||||
*/
|
||||
public void sendSysAnnouncement(String fromUser,String toUser,String title, String msgContent);
|
||||
|
||||
/**
|
||||
* 发送系统消息
|
||||
* @param fromUser 发送人(用户登录账户)
|
||||
* @param toUser 发送给(用户登录账户)
|
||||
* @param title 通知标题
|
||||
* @param map 模板参数
|
||||
* @param templateCode 模板编码
|
||||
*/
|
||||
public void sendSysAnnouncement(String fromUser, String toUser,String title, Map<String, String> map, String templateCode);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param fromUser 发送人(用户登录账户)
|
||||
* @param toUser 发送给(用户登录账户)
|
||||
* @param title 通知标题
|
||||
* @param map 模板参数
|
||||
* @param templateCode 模板编码
|
||||
* @param busType 业务类型
|
||||
* @param busId 业务id
|
||||
*/
|
||||
public void sendSysAnnouncement(String fromUser, String toUser,String title, Map<String, String> map, String templateCode,String busType,String busId);
|
||||
|
||||
/**
|
||||
* 通过消息中心模板,生成推送内容
|
||||
*
|
||||
* @param templateCode 模板编码
|
||||
* @param map 模板参数
|
||||
* @return
|
||||
*/
|
||||
public String parseTemplateByCode(String templateCode, Map<String, String> map);
|
||||
|
||||
|
||||
/**
|
||||
* 发送系统消息
|
||||
* @param fromUser 发送人(用户登录账户)
|
||||
* @param toUser 发送给(用户登录账户)
|
||||
* @param title 消息主题
|
||||
* @param msgContent 消息内容
|
||||
* @param setMsgCategory 消息类型 1:消息2:系统消息
|
||||
*/
|
||||
public void sendSysAnnouncement(String fromUser, String toUser, String title, String msgContent, String setMsgCategory);
|
||||
|
||||
/**queryTableDictByKeys
|
||||
* 发送系统消息
|
||||
* @param fromUser 发送人(用户登录账户)
|
||||
* @param toUser 发送给(用户登录账户)
|
||||
* @param title 消息主题
|
||||
* @param msgContent 消息内容
|
||||
* @param setMsgCategory 消息类型 1:消息2:系统消息
|
||||
* @param busType 业务类型
|
||||
* @param busId 业务id
|
||||
*/
|
||||
public void sendSysAnnouncement(String fromUser, String toUser, String title, String msgContent, String setMsgCategory,String busType,String busId);
|
||||
|
||||
/**
|
||||
* 根据业务类型及业务id修改消息已读
|
||||
* @param busType
|
||||
* @param busId
|
||||
*/
|
||||
public void updateSysAnnounReadFlag(String busType,String busId);
|
||||
/**
|
||||
* 查询表字典 支持过滤数据
|
||||
* @param table
|
||||
* @param text
|
||||
* @param code
|
||||
* @param filterSql
|
||||
* @return
|
||||
*/
|
||||
public List<DictModel> queryFilterTableDictInfo(String table, String text, String code, String filterSql);
|
||||
|
||||
/**
|
||||
* 查询指定table的 text code 获取字典,包含text和value
|
||||
* @param table
|
||||
* @param text
|
||||
* @param code
|
||||
* @param keyArray
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public List<String> queryTableDictByKeys(String table, String text, String code, String[] keyArray);
|
||||
|
||||
/**
|
||||
* 获取所有有效用户
|
||||
* @return
|
||||
*/
|
||||
public List<ComboModel> queryAllUser();
|
||||
|
||||
/**
|
||||
* 获取所有有效用户 带参
|
||||
* userIds 默认选中用户
|
||||
* @return
|
||||
*/
|
||||
public JSONObject queryAllUser(String[] userIds, int pageNo, int pageSize);
|
||||
|
||||
/**
|
||||
* 获取所有有效用户 拼接查询条件
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<JSONObject> queryAllUser(Wrapper wrapper);
|
||||
|
||||
/**
|
||||
* 获取所有角色
|
||||
* @return
|
||||
*/
|
||||
public List<ComboModel> queryAllRole();
|
||||
|
||||
/**
|
||||
* 获取所有角色 带参
|
||||
* roleIds 默认选中角色
|
||||
* @return
|
||||
*/
|
||||
public List<ComboModel> queryAllRole(String[] roleIds );
|
||||
|
||||
/**
|
||||
* 通过用户账号查询角色Id集合
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
public List<String> getRoleIdsByUsername(String username);
|
||||
|
||||
/**
|
||||
* 通过部门编号查询部门id
|
||||
* @param orgCode
|
||||
* @return
|
||||
*/
|
||||
public String getDepartIdsByOrgCode(String orgCode);
|
||||
|
||||
/**
|
||||
* 查询上一级部门
|
||||
* @param departId
|
||||
* @return
|
||||
*/
|
||||
public DictModel getParentDepartId(String departId);
|
||||
|
||||
/**
|
||||
* 查询所有部门
|
||||
* @return
|
||||
*/
|
||||
public List<SysDepartModel> getAllSysDepart();
|
||||
|
||||
/**
|
||||
* 根据 id 查询数据库中存储的 DynamicDataSourceModel
|
||||
*
|
||||
* @param dbSourceId
|
||||
* @return
|
||||
*/
|
||||
DynamicDataSourceModel getDynamicDbSourceById(String dbSourceId);
|
||||
|
||||
/**
|
||||
* 根据 code 查询数据库中存储的 DynamicDataSourceModel
|
||||
*
|
||||
* @param dbSourceCode
|
||||
* @return
|
||||
*/
|
||||
DynamicDataSourceModel getDynamicDbSourceByCode(String dbSourceCode);
|
||||
|
||||
/**
|
||||
* 根据部门Id获取部门负责人
|
||||
* @param deptId
|
||||
* @return
|
||||
*/
|
||||
public List<String> getDeptHeadByDepId(String deptId);
|
||||
|
||||
/**
|
||||
* 文件上传
|
||||
* @param file 文件
|
||||
* @param bizPath 自定义路径
|
||||
* @param uploadType 上传方式
|
||||
* @return
|
||||
*/
|
||||
public String upload(MultipartFile file,String bizPath,String uploadType);
|
||||
|
||||
/**
|
||||
* 文件上传 自定义桶
|
||||
* @param file
|
||||
* @param bizPath
|
||||
* @param uploadType
|
||||
* @param customBucket
|
||||
* @return
|
||||
*/
|
||||
public String upload(MultipartFile file,String bizPath,String uploadType,String customBucket);
|
||||
|
||||
/**
|
||||
* 文档管理文件下载预览
|
||||
* @param filePath
|
||||
* @param uploadpath
|
||||
* @param response
|
||||
*/
|
||||
public void viewAndDownload(String filePath, String uploadpath, String uploadType,HttpServletResponse response);
|
||||
|
||||
|
||||
/**
|
||||
* 给指定用户发消息
|
||||
* @param userIds
|
||||
* @param cmd
|
||||
*/
|
||||
public void sendWebSocketMsg(String[] userIds, String cmd);
|
||||
|
||||
/**
|
||||
* 根据id获取所有参与用户
|
||||
* userIds
|
||||
* @return
|
||||
*/
|
||||
public List<LoginUser> queryAllUserByIds(String[] userIds);
|
||||
/**
|
||||
* 将会议签到信息推动到预览
|
||||
* userIds
|
||||
* @return
|
||||
* @param userId
|
||||
*/
|
||||
void meetingSignWebsocket(String userId);
|
||||
/**
|
||||
* 根据name获取所有参与用户
|
||||
* userNames
|
||||
* @return
|
||||
*/
|
||||
List<LoginUser> queryUserByNames(String[] userNames);
|
||||
}
|
||||
@ -26,7 +26,7 @@ public class JeecgEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** ID */
|
||||
@TableId(type = IdType.ID_WORKER_STR)
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
@ApiModelProperty(value = "ID")
|
||||
private java.lang.String id;
|
||||
/** 创建人 */
|
||||
|
||||
@ -1,382 +0,0 @@
|
||||
package org.jeecg.common.system.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.util.CommonUtils;
|
||||
import org.jeecg.common.util.RestUtil;
|
||||
import org.jeecg.common.util.TokenUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 用户表 前端控制器
|
||||
* </p>
|
||||
*
|
||||
* @Author scott
|
||||
* @since 2018-12-20
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/sys/common")
|
||||
public class CommonController {
|
||||
|
||||
@Autowired
|
||||
private ISysBaseAPI sysBaseAPI;
|
||||
|
||||
@Value(value = "${jeecg.path.upload}")
|
||||
private String uploadpath;
|
||||
|
||||
/**
|
||||
* 本地:local minio:minio 阿里:alioss
|
||||
*/
|
||||
@Value(value="${jeecg.uploadType}")
|
||||
private String uploadType;
|
||||
|
||||
/**
|
||||
* @Author 政辉
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/403")
|
||||
public Result<?> noauth() {
|
||||
return Result.error("没有权限,请联系管理员授权");
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件上传统一方法
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/upload")
|
||||
public Result<?> upload(HttpServletRequest request, HttpServletResponse response) {
|
||||
Result<?> result = new Result<>();
|
||||
String savePath = "";
|
||||
String bizPath = request.getParameter("biz");
|
||||
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象
|
||||
if(oConvertUtils.isEmpty(bizPath)){
|
||||
if(CommonConstant.UPLOAD_TYPE_OSS.equals(uploadType)){
|
||||
//未指定目录,则用阿里云默认目录 upload
|
||||
bizPath = "upload";
|
||||
//result.setMessage("使用阿里云文件上传时,必须添加目录!");
|
||||
//result.setSuccess(false);
|
||||
//return result;
|
||||
}else{
|
||||
bizPath = "";
|
||||
}
|
||||
}
|
||||
if(CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)){
|
||||
//针对jeditor编辑器如何使 lcaol模式,采用 base64格式存储
|
||||
String jeditor = request.getParameter("jeditor");
|
||||
if(oConvertUtils.isNotEmpty(jeditor)){
|
||||
result.setMessage(CommonConstant.UPLOAD_TYPE_LOCAL);
|
||||
result.setSuccess(true);
|
||||
return result;
|
||||
}else{
|
||||
savePath = this.uploadLocal(file,bizPath);
|
||||
}
|
||||
}else{
|
||||
savePath = sysBaseAPI.upload(file,bizPath,uploadType);
|
||||
}
|
||||
if(oConvertUtils.isNotEmpty(savePath)){
|
||||
result.setMessage(savePath);
|
||||
result.setSuccess(true);
|
||||
}else {
|
||||
result.setMessage("上传失败!");
|
||||
result.setSuccess(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 本地文件上传
|
||||
* @param mf 文件
|
||||
* @param bizPath 自定义路径
|
||||
* @return
|
||||
*/
|
||||
private String uploadLocal(MultipartFile mf,String bizPath){
|
||||
try {
|
||||
String ctxPath = uploadpath;
|
||||
String fileName = null;
|
||||
File file = new File(ctxPath + File.separator + bizPath + File.separator );
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();// 创建文件根目录
|
||||
}
|
||||
String orgName = mf.getOriginalFilename();// 获取文件名
|
||||
orgName = CommonUtils.getFileName(orgName);
|
||||
if(orgName.indexOf(".")!=-1){
|
||||
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.indexOf("."));
|
||||
}else{
|
||||
fileName = orgName+ "_" + System.currentTimeMillis();
|
||||
}
|
||||
String savePath = file.getPath() + File.separator + fileName;
|
||||
File savefile = new File(savePath);
|
||||
FileCopyUtils.copy(mf.getBytes(), savefile);
|
||||
String dbpath = null;
|
||||
if(oConvertUtils.isNotEmpty(bizPath)){
|
||||
dbpath = bizPath + File.separator + fileName;
|
||||
}else{
|
||||
dbpath = fileName;
|
||||
}
|
||||
if (dbpath.contains("\\")) {
|
||||
dbpath = dbpath.replace("\\", "/");
|
||||
}
|
||||
return dbpath;
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// @PostMapping(value = "/upload2")
|
||||
// public Result<?> upload2(HttpServletRequest request, HttpServletResponse response) {
|
||||
// Result<?> result = new Result<>();
|
||||
// try {
|
||||
// String ctxPath = uploadpath;
|
||||
// String fileName = null;
|
||||
// String bizPath = "files";
|
||||
// String tempBizPath = request.getParameter("biz");
|
||||
// if(oConvertUtils.isNotEmpty(tempBizPath)){
|
||||
// bizPath = tempBizPath;
|
||||
// }
|
||||
// String nowday = new SimpleDateFormat("yyyyMMdd").format(new Date());
|
||||
// File file = new File(ctxPath + File.separator + bizPath + File.separator + nowday);
|
||||
// if (!file.exists()) {
|
||||
// file.mkdirs();// 创建文件根目录
|
||||
// }
|
||||
// MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
|
||||
// MultipartFile mf = multipartRequest.getFile("file");// 获取上传文件对象
|
||||
// String orgName = mf.getOriginalFilename();// 获取文件名
|
||||
// fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.indexOf("."));
|
||||
// String savePath = file.getPath() + File.separator + fileName;
|
||||
// File savefile = new File(savePath);
|
||||
// FileCopyUtils.copy(mf.getBytes(), savefile);
|
||||
// String dbpath = bizPath + File.separator + nowday + File.separator + fileName;
|
||||
// if (dbpath.contains("\\")) {
|
||||
// dbpath = dbpath.replace("\\", "/");
|
||||
// }
|
||||
// result.setMessage(dbpath);
|
||||
// result.setSuccess(true);
|
||||
// } catch (IOException e) {
|
||||
// result.setSuccess(false);
|
||||
// result.setMessage(e.getMessage());
|
||||
// log.error(e.getMessage(), e);
|
||||
// }
|
||||
// return result;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 预览图片&下载文件
|
||||
* 请求地址:http://localhost:8080/common/static/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
*/
|
||||
@GetMapping(value = "/static/**")
|
||||
public void view(HttpServletRequest request, HttpServletResponse response) {
|
||||
// ISO-8859-1 ==> UTF-8 进行编码转换
|
||||
String imgPath = extractPathFromPattern(request);
|
||||
if(oConvertUtils.isEmpty(imgPath) || imgPath=="null"){
|
||||
return;
|
||||
}
|
||||
// 其余处理略
|
||||
InputStream inputStream = null;
|
||||
OutputStream outputStream = null;
|
||||
try {
|
||||
imgPath = imgPath.replace("..", "");
|
||||
if (imgPath.endsWith(",")) {
|
||||
imgPath = imgPath.substring(0, imgPath.length() - 1);
|
||||
}
|
||||
String filePath = uploadpath + File.separator + imgPath;
|
||||
File file = new File(filePath);
|
||||
if(!file.exists()){
|
||||
response.setStatus(404);
|
||||
throw new RuntimeException("文件不存在..");
|
||||
}
|
||||
response.setContentType("application/force-download");// 设置强制下载不打开
|
||||
response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
|
||||
inputStream = new BufferedInputStream(new FileInputStream(filePath));
|
||||
outputStream = response.getOutputStream();
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = inputStream.read(buf)) > 0) {
|
||||
outputStream.write(buf, 0, len);
|
||||
}
|
||||
response.flushBuffer();
|
||||
} catch (IOException e) {
|
||||
log.error("预览文件失败" + e.getMessage());
|
||||
response.setStatus(404);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
try {
|
||||
inputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
if (outputStream != null) {
|
||||
try {
|
||||
outputStream.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 下载文件
|
||||
// * 请求地址:http://localhost:8080/common/download/{user/20190119/e1fe9925bc315c60addea1b98eb1cb1349547719_1547866868179.jpg}
|
||||
// *
|
||||
// * @param request
|
||||
// * @param response
|
||||
// * @throws Exception
|
||||
// */
|
||||
// @GetMapping(value = "/download/**")
|
||||
// public void download(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
// // ISO-8859-1 ==> UTF-8 进行编码转换
|
||||
// String filePath = extractPathFromPattern(request);
|
||||
// // 其余处理略
|
||||
// InputStream inputStream = null;
|
||||
// OutputStream outputStream = null;
|
||||
// try {
|
||||
// filePath = filePath.replace("..", "");
|
||||
// if (filePath.endsWith(",")) {
|
||||
// filePath = filePath.substring(0, filePath.length() - 1);
|
||||
// }
|
||||
// String localPath = uploadpath;
|
||||
// String downloadFilePath = localPath + File.separator + filePath;
|
||||
// File file = new File(downloadFilePath);
|
||||
// if (file.exists()) {
|
||||
// response.setContentType("application/force-download");// 设置强制下载不打开
|
||||
// response.addHeader("Content-Disposition", "attachment;fileName=" + new String(file.getName().getBytes("UTF-8"),"iso-8859-1"));
|
||||
// inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
// outputStream = response.getOutputStream();
|
||||
// byte[] buf = new byte[1024];
|
||||
// int len;
|
||||
// while ((len = inputStream.read(buf)) > 0) {
|
||||
// outputStream.write(buf, 0, len);
|
||||
// }
|
||||
// response.flushBuffer();
|
||||
// }
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// log.info("文件下载失败" + e.getMessage());
|
||||
// // e.printStackTrace();
|
||||
// } finally {
|
||||
// if (inputStream != null) {
|
||||
// try {
|
||||
// inputStream.close();
|
||||
// } catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// if (outputStream != null) {
|
||||
// try {
|
||||
// outputStream.close();
|
||||
// } catch (IOException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
||||
/**
|
||||
* @功能:pdf预览Iframe
|
||||
* @param modelAndView
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/pdf/pdfPreviewIframe")
|
||||
public ModelAndView pdfPreviewIframe(ModelAndView modelAndView) {
|
||||
modelAndView.setViewName("pdfPreviewIframe");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把指定URL后的字符串全部截断当成参数
|
||||
* 这么做是为了防止URL中包含中文或者特殊字符(/等)时,匹配不了的问题
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
private static String extractPathFromPattern(final HttpServletRequest request) {
|
||||
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
|
||||
String bestMatchPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
|
||||
return new AntPathMatcher().extractPathWithinPattern(bestMatchPattern, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 中转HTTP请求,解决跨域问题
|
||||
*
|
||||
* @param url 必填:请求地址
|
||||
* @return
|
||||
*/
|
||||
@RequestMapping("/transitRESTful")
|
||||
public Result transitRESTful(@RequestParam("url") String url, HttpServletRequest request) {
|
||||
try {
|
||||
ServletServerHttpRequest httpRequest = new ServletServerHttpRequest(request);
|
||||
// 中转请求method、body
|
||||
HttpMethod method = httpRequest.getMethod();
|
||||
JSONObject params;
|
||||
try {
|
||||
params = JSON.parseObject(JSON.toJSONString(httpRequest.getBody()));
|
||||
} catch (Exception e) {
|
||||
params = new JSONObject();
|
||||
}
|
||||
// 中转请求问号参数
|
||||
JSONObject variables = JSON.parseObject(JSON.toJSONString(request.getParameterMap()));
|
||||
variables.remove("url");
|
||||
// 在 headers 里传递Token
|
||||
String token = TokenUtils.getTokenByRequest(request);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("X-Access-Token", token);
|
||||
// 发送请求
|
||||
String httpURL = URLDecoder.decode(url, "UTF-8");
|
||||
ResponseEntity<String> response = RestUtil.request(httpURL, method, headers , variables, params, String.class);
|
||||
// 封装返回结果
|
||||
Result<Object> result = new Result<>();
|
||||
int statusCode = response.getStatusCodeValue();
|
||||
result.setCode(statusCode);
|
||||
result.setSuccess(statusCode == 200);
|
||||
String responseBody = response.getBody();
|
||||
try {
|
||||
// 尝试将返回结果转为JSON
|
||||
Object json = JSON.parse(responseBody);
|
||||
result.setResult(json);
|
||||
} catch (Exception e) {
|
||||
// 转成JSON失败,直接返回原始数据
|
||||
result.setResult(responseBody);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.debug("中转HTTP请求失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,23 +1,26 @@
|
||||
package org.jeecg.common.system.query;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.beanutils.PropertyUtils;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.DataBaseConstant;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.util.JeecgDataAutorUtils;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
|
||||
import org.jeecg.common.util.CommonUtils;
|
||||
import org.jeecg.common.util.DateUtils;
|
||||
import org.jeecg.common.util.SqlInjectionUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecgframework.core.util.ApplicationContextUtil;
|
||||
import org.springframework.util.NumberUtils;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URLDecoder;
|
||||
import java.text.ParseException;
|
||||
@ -115,7 +118,26 @@ public class QueryGenerator {
|
||||
if (judgedIsUselessField(name)|| !PropertyUtils.isReadable(searchObj, name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Object value = PropertyUtils.getSimpleProperty(searchObj, name);
|
||||
// update-begin--Author:taoyan Date:20200910 for:issues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
|
||||
//如果字段加注解了@TableField(exist = false),不走DB查询
|
||||
//TODO 存在缺陷,这个写法 clazz.getDeclaredField(name) 获取不到继承的父实体字段
|
||||
try {
|
||||
if (oConvertUtils.isNotEmpty(value)) {
|
||||
Field field = searchObj.getClass().getDeclaredField(name);
|
||||
if (field != null) {
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
if (tableField != null && tableField.exist() == false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// update-end--Author:taoyan Date:20200910 for:issues/1671 如果字段加注解了@TableField(exist = false),不走DB查询 -------
|
||||
|
||||
//数据权限查询
|
||||
if(ruleMap.containsKey(name)) {
|
||||
addRuleToQueryWrapper(ruleMap.get(name), name, origDescriptors[i].getPropertyType(), queryWrapper);
|
||||
@ -140,7 +162,6 @@ public class QueryGenerator {
|
||||
|
||||
//判断单值 参数带不同标识字符串 走不同的查询
|
||||
//TODO 这种前后带逗号的支持分割后模糊查询需要否 使多选字段的查询生效
|
||||
Object value = PropertyUtils.getSimpleProperty(searchObj, name);
|
||||
if (null != value && value.toString().startsWith(COMMA) && value.toString().endsWith(COMMA)) {
|
||||
String multiLikeval = value.toString().replace(",,", COMMA);
|
||||
String[] vals = multiLikeval.substring(1, multiLikeval.length()).split(COMMA);
|
||||
@ -446,9 +467,14 @@ public class QueryGenerator {
|
||||
queryWrapper.in(name, (Object[])value.toString().split(","));
|
||||
}else if(value instanceof String[]) {
|
||||
queryWrapper.in(name, (Object[]) value);
|
||||
}
|
||||
//update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
|
||||
else if(value.getClass().isArray()) {
|
||||
queryWrapper.in(name, (Object[])value);
|
||||
}else {
|
||||
queryWrapper.in(name, value);
|
||||
}
|
||||
//update-end-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
|
||||
break;
|
||||
case LIKE:
|
||||
queryWrapper.like(name, value);
|
||||
@ -498,6 +524,30 @@ public class QueryGenerator {
|
||||
}
|
||||
return ruleMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求对应的数据权限规则
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, SysPermissionDataRuleModel> getRuleMap(List<SysPermissionDataRuleModel> list) {
|
||||
Map<String, SysPermissionDataRuleModel> ruleMap = new HashMap<String, SysPermissionDataRuleModel>();
|
||||
if(list==null){
|
||||
list =JeecgDataAutorUtils.loadDataSearchConditon();
|
||||
}
|
||||
if(list != null&&list.size()>0){
|
||||
if(list.get(0)==null){
|
||||
return ruleMap;
|
||||
}
|
||||
for (SysPermissionDataRuleModel rule : list) {
|
||||
String column = rule.getRuleColumn();
|
||||
if(QueryRuleEnum.SQL_RULES.getValue().equals(rule.getRuleConditions())) {
|
||||
column = SQL_RULES_COLUMN+rule.getId();
|
||||
}
|
||||
ruleMap.put(column, rule);
|
||||
}
|
||||
}
|
||||
return ruleMap;
|
||||
}
|
||||
|
||||
private static void addRuleToQueryWrapper(SysPermissionDataRuleModel dataRule, String name, Class propertyType, QueryWrapper<?> queryWrapper) {
|
||||
QueryRuleEnum rule = QueryRuleEnum.getByValue(dataRule.getRuleConditions());
|
||||
@ -525,10 +575,7 @@ public class QueryGenerator {
|
||||
}
|
||||
|
||||
public static String converRuleValue(String ruleValue) {
|
||||
String value = JwtUtil.getSessionData(ruleValue);
|
||||
if(oConvertUtils.isEmpty(value)) {
|
||||
value = JwtUtil.getUserSystemData(ruleValue,null);
|
||||
}
|
||||
String value = JwtUtil.getUserSystemData(ruleValue,null);
|
||||
return value!= null ? value : ruleValue;
|
||||
}
|
||||
|
||||
@ -728,8 +775,7 @@ public class QueryGenerator {
|
||||
|
||||
/**
|
||||
* 根据权限相关配置生成相关的SQL 语句
|
||||
* @param searchObj
|
||||
* @param parameterMap
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@ -771,8 +817,8 @@ public class QueryGenerator {
|
||||
|
||||
/**
|
||||
* 根据权限相关配置 组装mp需要的权限
|
||||
* @param searchObj
|
||||
* @param parameterMap
|
||||
* @param queryWrapper
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static void installAuthMplus(QueryWrapper<?> queryWrapper,Class<?> clazz) {
|
||||
@ -847,17 +893,7 @@ public class QueryGenerator {
|
||||
* 获取系统数据库类型
|
||||
*/
|
||||
private static String getDbType(){
|
||||
if(oConvertUtils.isNotEmpty(DB_TYPE)){
|
||||
return DB_TYPE;
|
||||
}
|
||||
try {
|
||||
ISysBaseAPI sysBaseAPI = ApplicationContextUtil.getContext().getBean(ISysBaseAPI.class);
|
||||
DB_TYPE = sysBaseAPI.getDatabaseType();
|
||||
return DB_TYPE;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return DB_TYPE;
|
||||
return CommonUtils.getDatabaseType();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -75,11 +75,28 @@ public class JeecgDataAutorUtils {
|
||||
request.setAttribute(MENU_DATA_AUTHOR_RULE_SQL,sql);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将用户信息存到request
|
||||
* @param request
|
||||
* @param userinfo
|
||||
*/
|
||||
public static synchronized void installUserInfo(HttpServletRequest request, SysUserCacheInfo userinfo) {
|
||||
request.setAttribute(SYS_USER_INFO, userinfo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将用户信息存到request
|
||||
* @param userinfo
|
||||
*/
|
||||
public static synchronized void installUserInfo(SysUserCacheInfo userinfo) {
|
||||
SpringContextUtils.getHttpServletRequest().setAttribute(SYS_USER_INFO, userinfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从request获取用户信息
|
||||
* @return
|
||||
*/
|
||||
public static synchronized SysUserCacheInfo loadUserInfo() {
|
||||
return (SysUserCacheInfo) SpringContextUtils.getHttpServletRequest().getAttribute(SYS_USER_INFO);
|
||||
|
||||
|
||||
@ -174,10 +174,15 @@ public class JwtUtil {
|
||||
}
|
||||
//替换为系统用户所拥有的所有机构编码
|
||||
else if (key.equals(DataBaseConstant.SYS_MULTI_ORG_CODE)|| key.toLowerCase().equals(DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE)) {
|
||||
if(user.isOneDepart()) {
|
||||
returnValue = user.getSysMultiOrgCode().get(0);
|
||||
}else {
|
||||
returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
|
||||
if(user==null){
|
||||
//TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
|
||||
returnValue = sysUser.getOrgCode();
|
||||
}else{
|
||||
if(user.isOneDepart()) {
|
||||
returnValue = user.getSysMultiOrgCode().get(0);
|
||||
}else {
|
||||
returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
//替换为当前系统时间(年月日)
|
||||
|
||||
@ -113,4 +113,7 @@ public class LoginUser {
|
||||
/**多租户id配置,编辑用户的时候设置*/
|
||||
private String relTenantIds;
|
||||
|
||||
/**设备id uniapp推送用*/
|
||||
private String clientId;
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +1,23 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.DataBaseConstant;
|
||||
import org.jeecg.common.exception.JeecgBootException;
|
||||
import org.jeecg.common.util.oss.OssBootUtil;
|
||||
import org.jeecgframework.poi.util.PoiPublicUtil;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
|
||||
@Slf4j
|
||||
public class CommonUtils {
|
||||
|
||||
public static String uploadOnlineImage(byte[] data,String basePath,String bizPath,String uploadType){
|
||||
@ -61,4 +70,81 @@ public class CommonUtils {
|
||||
fileName = fileName.replace("=","").replace(",","").replace("&","").replace("#", "");
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一全局上传
|
||||
* @Return: java.lang.String
|
||||
*/
|
||||
public static String upload(MultipartFile file, String bizPath, String uploadType) {
|
||||
String url = "";
|
||||
if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
|
||||
url = MinioUtil.upload(file,bizPath);
|
||||
}else{
|
||||
url = OssBootUtil.upload(file,bizPath);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一全局上传 带桶
|
||||
* @Return: java.lang.String
|
||||
*/
|
||||
public static String upload(MultipartFile file, String bizPath, String uploadType, String customBucket) {
|
||||
String url = "";
|
||||
if(CommonConstant.UPLOAD_TYPE_MINIO.equals(uploadType)){
|
||||
url = MinioUtil.upload(file,bizPath,customBucket);
|
||||
}else{
|
||||
url = OssBootUtil.upload(file,bizPath,customBucket);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/** 当前系统数据库类型 */
|
||||
private static String DB_TYPE = "";
|
||||
public static String getDatabaseType() {
|
||||
if(oConvertUtils.isNotEmpty(DB_TYPE)){
|
||||
return DB_TYPE;
|
||||
}
|
||||
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
|
||||
try {
|
||||
return getDatabaseTypeByDataSource(dataSource);
|
||||
} catch (SQLException e) {
|
||||
//e.printStackTrace();
|
||||
log.warn(e.getMessage());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库类型
|
||||
* @param dataSource
|
||||
* @return
|
||||
* @throws SQLException
|
||||
*/
|
||||
private static String getDatabaseTypeByDataSource(DataSource dataSource) throws SQLException{
|
||||
if("".equals(DB_TYPE)) {
|
||||
Connection connection = dataSource.getConnection();
|
||||
try {
|
||||
DatabaseMetaData md = connection.getMetaData();
|
||||
String dbType = md.getDatabaseProductName().toLowerCase();
|
||||
if(dbType.indexOf("mysql")>=0) {
|
||||
DB_TYPE = DataBaseConstant.DB_TYPE_MYSQL;
|
||||
}else if(dbType.indexOf("oracle")>=0) {
|
||||
DB_TYPE = DataBaseConstant.DB_TYPE_ORACLE;
|
||||
}else if(dbType.indexOf("sqlserver")>=0||dbType.indexOf("sql server")>=0) {
|
||||
DB_TYPE = DataBaseConstant.DB_TYPE_SQLSERVER;
|
||||
}else if(dbType.indexOf("postgresql")>=0) {
|
||||
DB_TYPE = DataBaseConstant.DB_TYPE_POSTGRESQL;
|
||||
}else {
|
||||
throw new JeecgBootException("数据库类型:["+dbType+"]不识别!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
}finally {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
return DB_TYPE;
|
||||
|
||||
}
|
||||
}
|
||||
@ -6,8 +6,12 @@ public enum DySmsEnum {
|
||||
|
||||
LOGIN_TEMPLATE_CODE("SMS_175435174","JEECG","code"),
|
||||
FORGET_PASSWORD_TEMPLATE_CODE("SMS_175435174","JEECG","code"),
|
||||
REGISTER_TEMPLATE_CODE("SMS_175430166","JEECG","code");
|
||||
|
||||
REGISTER_TEMPLATE_CODE("SMS_175430166","JEECG","code"),
|
||||
/**会议通知*/
|
||||
MEET_NOTICE_TEMPLATE_CODE("SMS_201480469","H5活动之家","username,title,minute,time"),
|
||||
/**我的计划通知*/
|
||||
PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","H5活动之家","username,title,time");
|
||||
|
||||
/**
|
||||
* 短信模板编码
|
||||
*/
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.jeecg.common.util;
|
||||
|
||||
import org.jeecg.config.StaticConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -57,6 +58,12 @@ public class DySmsHelper {
|
||||
//可自助调整超时时间
|
||||
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
|
||||
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
|
||||
|
||||
//update-begin-author:taoyan date:20200811 for:配置类数据获取
|
||||
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
|
||||
setAccessKeyId(staticConfig.getAccessKeyId());
|
||||
setAccessKeySecret(staticConfig.getAccessKeySecret());
|
||||
//update-end-author:taoyan date:20200811 for:配置类数据获取
|
||||
|
||||
//初始化acsClient,暂不支持region化
|
||||
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
|
||||
|
||||
@ -3,8 +3,8 @@ package org.jeecg.common.util;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
|
||||
@ -35,32 +35,32 @@ public class TokenUtils {
|
||||
/**
|
||||
* 验证Token
|
||||
*/
|
||||
public static boolean verifyToken(HttpServletRequest request, ISysBaseAPI sysBaseAPI, RedisUtil redisUtil) {
|
||||
public static boolean verifyToken(HttpServletRequest request, CommonAPI commonAPI, RedisUtil redisUtil) {
|
||||
log.info(" -- url --" + request.getRequestURL());
|
||||
String token = getTokenByRequest(request);
|
||||
|
||||
if (StringUtils.isBlank(token)) {
|
||||
throw new AuthenticationException("token不能为空!");
|
||||
throw new AuthenticationException("Token不能为空!");
|
||||
}
|
||||
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("token非法无效!");
|
||||
throw new AuthenticationException("Token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
LoginUser user = sysBaseAPI.getUserByName(username);
|
||||
LoginUser user = commonAPI.getUserByName(username);
|
||||
if (user == null) {
|
||||
throw new AuthenticationException("用户不存在!");
|
||||
}
|
||||
// 判断用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new AuthenticationException("账号已被锁定,请联系管理员!");
|
||||
throw new AuthenticationException("账号已锁定,请联系管理员!");
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
|
||||
throw new AuthenticationException("Token失效,请重新登录!");
|
||||
throw new AuthenticationException("Token失效,请重新登录");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -95,4 +95,34 @@ public class TokenUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证Token
|
||||
*/
|
||||
public static boolean verifyToken(String token, CommonAPI commonAPI, RedisUtil redisUtil) {
|
||||
if (StringUtils.isBlank(token)) {
|
||||
throw new AuthenticationException("token不能为空!");
|
||||
}
|
||||
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
LoginUser user = commonAPI.getUserByName(username);
|
||||
if (user == null) {
|
||||
throw new AuthenticationException("用户不存在!");
|
||||
}
|
||||
// 判断用户状态
|
||||
if (user.getStatus() != 1) {
|
||||
throw new AuthenticationException("账号已被锁定,请联系管理员!");
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
|
||||
throw new AuthenticationException("Token失效,请重新登录!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package org.jeecg.common.util.dynamic.db;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.common.system.api.ISysBaseAPI;
|
||||
import org.jeecg.common.system.vo.DynamicDataSourceModel;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
@ -37,8 +37,8 @@ public class DataSourceCachePool {
|
||||
if (getRedisTemplate().hasKey(redisCacheKey)) {
|
||||
return (DynamicDataSourceModel) getRedisTemplate().opsForValue().get(redisCacheKey);
|
||||
}
|
||||
ISysBaseAPI sysBaseAPI = SpringContextUtils.getBean(ISysBaseAPI.class);
|
||||
DynamicDataSourceModel dbSource = sysBaseAPI.getDynamicDbSourceByCode(dbKey);
|
||||
CommonAPI commonAPI = SpringContextUtils.getBean(CommonAPI.class);
|
||||
DynamicDataSourceModel dbSource = commonAPI.getDynamicDbSourceByCode(dbKey);
|
||||
if (dbSource != null) {
|
||||
getRedisTemplate().opsForValue().set(redisCacheKey, dbSource);
|
||||
}
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
package org.jeecg.common.util.jsonschema;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.jeecg.common.system.vo.DictModel;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jeecg.common.system.vo.DictModel;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
/**
|
||||
* 验证通用属性
|
||||
*/
|
||||
@ -161,7 +159,7 @@ public abstract class CommonProperty implements Serializable{
|
||||
JSONObject ui = JSONObject.parseObject(str);
|
||||
json.put("ui", ui);
|
||||
}
|
||||
if (StringUtils.isNotBlank(defVal)) {
|
||||
if (defVal!=null && defVal.length()>0) {
|
||||
json.put("defVal", defVal);
|
||||
}
|
||||
return json;
|
||||
|
||||
@ -593,7 +593,7 @@ public class oConvertUtils {
|
||||
* @return
|
||||
*/
|
||||
public static<F,T> List<T> entityListToModelList(List<F> fromList, Class<T> tClass){
|
||||
if(fromList.isEmpty() || fromList == null){
|
||||
if(fromList == null || fromList.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
List<T> tList = new ArrayList<>();
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.jeecgframework.core.util.ApplicationContextUtil;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @Author: Scott
|
||||
* @Date: 2018/2/7
|
||||
* @description: autopoi 配置类
|
||||
*/
|
||||
|
||||
@Configuration
|
||||
public class AutoPoiConfig {
|
||||
|
||||
/**
|
||||
* excel注解字典参数支持(导入导出字典值,自动翻译)
|
||||
* 举例: @Excel(name = "性别", width = 15, dicCode = "sex")
|
||||
* 1、导出的时候会根据字典配置,把值1,2翻译成:男、女;
|
||||
* 2、导入的时候,会把男、女翻译成1,2存进数据库;
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public ApplicationContextUtil applicationContextUtil() {
|
||||
return new org.jeecgframework.core.util.ApplicationContextUtil();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* 跨域配置加载条件
|
||||
*/
|
||||
public class CorsFilterCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
Object object = context.getEnvironment().getProperty(CommonConstant.CLOUD_SERVER_KEY);
|
||||
//如果没有服务注册发现的配置 说明是单体应用 则加载跨域配置 返回true
|
||||
if(object==null){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.SimpleClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* 优雅的http请求方式RestTemplate
|
||||
* @Return:
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
|
||||
return new RestTemplate(factory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
|
||||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
|
||||
factory.setReadTimeout(5000);//ms
|
||||
factory.setConnectTimeout(15000);//ms
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设置静态参数初始化
|
||||
*/
|
||||
@Component
|
||||
@Data
|
||||
public class StaticConfig {
|
||||
|
||||
@Value("${jeecg.sms.accessKeyId}")
|
||||
private String accessKeyId;
|
||||
|
||||
@Value("${jeecg.sms.accessKeySecret}")
|
||||
private String accessKeySecret;
|
||||
|
||||
@Value(value = "${spring.mail.username}")
|
||||
private String emailFrom;
|
||||
|
||||
|
||||
/*@Bean
|
||||
public void initStatic() {
|
||||
DySmsHelper.setAccessKeyId(accessKeyId);
|
||||
DySmsHelper.setAccessKeySecret(accessKeySecret);
|
||||
EmailSendMsgHandle.setEmailFrom(emailFrom);
|
||||
}*/
|
||||
|
||||
}
|
||||
@ -0,0 +1,135 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.ParameterBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.schema.ModelRef;
|
||||
import springfox.documentation.service.*;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @Author scott
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@EnableSwaggerBootstrapUI
|
||||
@ConditionalOnProperty(name = "swagger.enable", havingValue = "true")
|
||||
public class Swagger2Config implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
*
|
||||
* 显示swagger-ui.html文档展示页,还必须注入swagger资源:
|
||||
*
|
||||
* @param registry
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
|
||||
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
|
||||
}
|
||||
|
||||
/**
|
||||
* swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
|
||||
*
|
||||
* @return Docket
|
||||
*/
|
||||
@Bean
|
||||
public Docket createRestApi() {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
//此包路径下的类,才生成接口文档
|
||||
.apis(RequestHandlerSelectors.basePackage("org.jeecg.modules"))
|
||||
//加了ApiOperation注解的类,才生成接口文档
|
||||
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||
.paths(PathSelectors.any())
|
||||
.build()
|
||||
.securitySchemes(Collections.singletonList(securityScheme()))
|
||||
.securityContexts(securityContexts());
|
||||
//.globalOperationParameters(setHeaderToken());
|
||||
}
|
||||
|
||||
/***
|
||||
* oauth2配置
|
||||
* 需要增加swagger授权回调地址
|
||||
* http://localhost:8888/webjars/springfox-swagger-ui/o2c.html
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
SecurityScheme securityScheme() {
|
||||
return new ApiKey(CommonConstant.X_ACCESS_TOKEN, CommonConstant.X_ACCESS_TOKEN, "header");
|
||||
}
|
||||
/**
|
||||
* JWT token
|
||||
* @return
|
||||
*/
|
||||
private List<Parameter> setHeaderToken() {
|
||||
ParameterBuilder tokenPar = new ParameterBuilder();
|
||||
List<Parameter> pars = new ArrayList<>();
|
||||
tokenPar.name(CommonConstant.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
|
||||
pars.add(tokenPar.build());
|
||||
return pars;
|
||||
}
|
||||
|
||||
/**
|
||||
* api文档的详细信息函数,注意这里的注解引用的是哪个
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
// //大标题
|
||||
.title("Jeecg-Boot 后台服务API接口文档")
|
||||
// 版本号
|
||||
.version("1.0")
|
||||
// .termsOfServiceUrl("NO terms of service")
|
||||
// 描述
|
||||
.description("后台API接口")
|
||||
// 作者
|
||||
.contact("JEECG团队")
|
||||
.license("The Apache License, Version 2.0")
|
||||
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增 securityContexts 保持登录状态
|
||||
*/
|
||||
private List<SecurityContext> securityContexts() {
|
||||
return new ArrayList(
|
||||
Collections.singleton(SecurityContext.builder()
|
||||
.securityReferences(defaultAuth())
|
||||
.forPaths(PathSelectors.regex("^(?!auth).*$"))
|
||||
.build())
|
||||
);
|
||||
}
|
||||
|
||||
private List<SecurityReference> defaultAuth() {
|
||||
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
|
||||
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
|
||||
authorizationScopes[0] = authorizationScope;
|
||||
return new ArrayList(
|
||||
Collections.singleton(new SecurityReference(CommonConstant.X_ACCESS_TOKEN, authorizationScopes)));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* Spring Boot 2.0 解决跨域问题
|
||||
*
|
||||
* @Author qinfeng
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Value("${jeecg.path.upload}")
|
||||
private String upLoadPath;
|
||||
@Value("${jeecg.path.webapp}")
|
||||
private String webAppPath;
|
||||
@Value("${spring.resource.static-locations}")
|
||||
private String staticLocations;
|
||||
|
||||
/**
|
||||
* 静态资源的配置 - 使得可以从磁盘中读取 Html、图片、视频、音频等
|
||||
*/
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations("file:" + upLoadPath + "//", "file:" + webAppPath + "//")
|
||||
.addResourceLocations(staticLocations.split(","));
|
||||
}
|
||||
|
||||
/**
|
||||
* 方案一: 默认访问根路径跳转 doc.html页面 (swagger文档页面)
|
||||
* 方案二: 访问根路径改成跳转 index.html页面 (简化部署方案: 可以把前端打包直接放到项目的 webapp,上面的配置)
|
||||
*/
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/").setViewName("doc.html");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Conditional(CorsFilterCondition.class)
|
||||
public CorsFilter corsFilter() {
|
||||
final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
|
||||
final CorsConfiguration corsConfiguration = new CorsConfiguration();
|
||||
//是否允许请求带有验证信息
|
||||
corsConfiguration.setAllowCredentials(true);
|
||||
// 允许访问的客户端域名
|
||||
corsConfiguration.addAllowedOrigin("*");
|
||||
// 允许服务端访问的客户端请求头
|
||||
corsConfiguration.addAllowedHeader("*");
|
||||
// 允许访问的方法名,GET POST等
|
||||
corsConfiguration.addAllowedMethod("*");
|
||||
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
|
||||
return new CorsFilter(urlBasedCorsConfigurationSource);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package org.jeecg.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||
|
||||
@Configuration
|
||||
public class WebSocketConfig {
|
||||
/**
|
||||
* 注入ServerEndpointExporter,
|
||||
* 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
|
||||
*/
|
||||
@Bean
|
||||
public ServerEndpointExporter serverEndpointExporter() {
|
||||
return new ServerEndpointExporter();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,130 @@
|
||||
package org.jeecg.config.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
|
||||
import net.sf.jsqlparser.expression.BinaryExpression;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.Parenthesis;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.*;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.select.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 复写租户条件
|
||||
*/
|
||||
public class JeecgTenantParser extends TenantSqlParser {
|
||||
|
||||
/**
|
||||
* @param expression
|
||||
* @param table
|
||||
* @return
|
||||
*/
|
||||
protected Expression processTableAlias(Expression expression, Table table) {
|
||||
String tableAliasName;
|
||||
if (table.getAlias() == null) {
|
||||
tableAliasName = table.getName();
|
||||
} else {
|
||||
tableAliasName = table.getAlias().getName();
|
||||
}
|
||||
|
||||
// in
|
||||
if (expression instanceof InExpression) {
|
||||
InExpression in = (InExpression) expression;
|
||||
if (in.getLeftExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) in.getLeftExpression(), tableAliasName);
|
||||
}
|
||||
|
||||
// 比较操作
|
||||
} else if (expression instanceof BinaryExpression) {
|
||||
BinaryExpression compare = (BinaryExpression) expression;
|
||||
if (compare.getLeftExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) compare.getLeftExpression(), tableAliasName);
|
||||
} else if (compare.getRightExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) compare.getRightExpression(), tableAliasName);
|
||||
}
|
||||
|
||||
// between
|
||||
} else if (expression instanceof Between) {
|
||||
Between between = (Between) expression;
|
||||
if (between.getLeftExpression() instanceof Column) {
|
||||
setTableAliasNameForColumn((Column) between.getLeftExpression(), tableAliasName);
|
||||
}
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
private void setTableAliasNameForColumn(Column column, String tableAliasName) {
|
||||
column.setColumnName(tableAliasName + "." + column.getColumnName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认是按 tenant_id=1 按等于条件追加
|
||||
*
|
||||
* @param currentExpression 现有的条件:比如你原来的sql查询条件
|
||||
* @param table
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected Expression builderExpression(Expression currentExpression, Table table) {
|
||||
final Expression tenantExpression = this.getTenantHandler().getTenantId(true);
|
||||
Expression appendExpression;
|
||||
if (!(tenantExpression instanceof SupportsOldOracleJoinSyntax)) {
|
||||
appendExpression = new EqualsTo();
|
||||
((EqualsTo) appendExpression).setLeftExpression(this.getAliasColumn(table));
|
||||
((EqualsTo) appendExpression).setRightExpression(tenantExpression);
|
||||
} else {
|
||||
appendExpression = processTableAlias(tenantExpression, table);
|
||||
}
|
||||
if (currentExpression == null) {
|
||||
return appendExpression;
|
||||
}
|
||||
if (currentExpression instanceof BinaryExpression) {
|
||||
BinaryExpression binaryExpression = (BinaryExpression) currentExpression;
|
||||
if (binaryExpression.getLeftExpression() instanceof FromItem) {
|
||||
processFromItem((FromItem) binaryExpression.getLeftExpression());
|
||||
}
|
||||
if (binaryExpression.getRightExpression() instanceof FromItem) {
|
||||
processFromItem((FromItem) binaryExpression.getRightExpression());
|
||||
}
|
||||
} else if (currentExpression instanceof InExpression) {
|
||||
InExpression inExp = (InExpression) currentExpression;
|
||||
ItemsList rightItems = inExp.getRightItemsList();
|
||||
if (rightItems instanceof SubSelect) {
|
||||
processSelectBody(((SubSelect) rightItems).getSelectBody());
|
||||
}
|
||||
}
|
||||
if (currentExpression instanceof OrExpression) {
|
||||
return new AndExpression(new Parenthesis(currentExpression), appendExpression);
|
||||
} else {
|
||||
return new AndExpression(currentExpression, appendExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processPlainSelect(PlainSelect plainSelect, boolean addColumn) {
|
||||
FromItem fromItem = plainSelect.getFromItem();
|
||||
if (fromItem instanceof Table) {
|
||||
Table fromTable = (Table) fromItem;
|
||||
if (!this.getTenantHandler().doTableFilter(fromTable.getName())) {
|
||||
plainSelect.setWhere(builderExpression(plainSelect.getWhere(), fromTable));
|
||||
if (addColumn) {
|
||||
plainSelect.getSelectItems().add(new SelectExpressionItem(new Column(this.getTenantHandler().getTenantIdColumn())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
processFromItem(fromItem);
|
||||
}
|
||||
List<Join> joins = plainSelect.getJoins();
|
||||
if (joins != null && joins.size() > 0) {
|
||||
joins.forEach(j -> {
|
||||
processJoin(j);
|
||||
processFromItem(j.getRightItem());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,161 @@
|
||||
package org.jeecg.config.mybatis;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.binding.MapperMethod.ParamMap;
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间
|
||||
* @Author scott
|
||||
* @Date 2019-01-19
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
|
||||
public class MybatisInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
|
||||
String sqlId = mappedStatement.getId();
|
||||
log.debug("------sqlId------" + sqlId);
|
||||
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
|
||||
Object parameter = invocation.getArgs()[1];
|
||||
log.debug("------sqlCommandType------" + sqlCommandType);
|
||||
|
||||
if (parameter == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
if (SqlCommandType.INSERT == sqlCommandType) {
|
||||
LoginUser sysUser = this.getLoginUser();
|
||||
Field[] fields = oConvertUtils.getAllFields(parameter);
|
||||
for (Field field : fields) {
|
||||
log.debug("------field.name------" + field.getName());
|
||||
try {
|
||||
if ("createBy".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object local_createBy = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (local_createBy == null || local_createBy.equals("")) {
|
||||
if (sysUser != null) {
|
||||
// 登录人账号
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, sysUser.getUsername());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 注入创建时间
|
||||
if ("createTime".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object local_createDate = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (local_createDate == null || local_createDate.equals("")) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, new Date());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
//注入部门编码
|
||||
if ("sysOrgCode".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
Object local_sysOrgCode = field.get(parameter);
|
||||
field.setAccessible(false);
|
||||
if (local_sysOrgCode == null || local_sysOrgCode.equals("")) {
|
||||
// 获取登录用户信息
|
||||
if (sysUser != null) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, sysUser.getOrgCode());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SqlCommandType.UPDATE == sqlCommandType) {
|
||||
LoginUser sysUser = this.getLoginUser();
|
||||
Field[] fields = null;
|
||||
if (parameter instanceof ParamMap) {
|
||||
ParamMap<?> p = (ParamMap<?>) parameter;
|
||||
//update-begin-author:scott date:20190729 for:批量更新报错issues/IZA3Q--
|
||||
if (p.containsKey("et")) {
|
||||
parameter = p.get("et");
|
||||
} else {
|
||||
parameter = p.get("param1");
|
||||
}
|
||||
//update-end-author:scott date:20190729 for:批量更新报错issues/IZA3Q-
|
||||
|
||||
//update-begin-author:scott date:20190729 for:更新指定字段时报错 issues/#516-
|
||||
if (parameter == null) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
//update-end-author:scott date:20190729 for:更新指定字段时报错 issues/#516-
|
||||
|
||||
fields = oConvertUtils.getAllFields(parameter);
|
||||
} else {
|
||||
fields = oConvertUtils.getAllFields(parameter);
|
||||
}
|
||||
|
||||
for (Field field : fields) {
|
||||
log.debug("------field.name------" + field.getName());
|
||||
try {
|
||||
if ("updateBy".equals(field.getName())) {
|
||||
//获取登录用户信息
|
||||
if (sysUser != null) {
|
||||
// 登录账号
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, sysUser.getUsername());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
if ("updateTime".equals(field.getName())) {
|
||||
field.setAccessible(true);
|
||||
field.set(parameter, new Date());
|
||||
field.setAccessible(false);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
//update-begin--Author:scott Date:20191213 for:关于使用Quzrtz 开启线程任务, #465
|
||||
private LoginUser getLoginUser() {
|
||||
LoginUser sysUser = null;
|
||||
try {
|
||||
sysUser = SecurityUtils.getSubject().getPrincipal() != null ? (LoginUser) SecurityUtils.getSubject().getPrincipal() : null;
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
sysUser = null;
|
||||
}
|
||||
return sysUser;
|
||||
}
|
||||
//update-end--Author:scott Date:20191213 for:关于使用Quzrtz 开启线程任务, #465
|
||||
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
package org.jeecg.config.mybatis;
|
||||
|
||||
import com.baomidou.mybatisplus.core.parser.ISqlParser;
|
||||
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 单数据源配置(jeecg.datasource.open = false时生效)
|
||||
* @Author zhoujf
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@MapperScan(value={"org.jeecg.modules.**.mapper*"})
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
/**
|
||||
* tenant_id 字段名
|
||||
*/
|
||||
public static final String tenant_field = "tenant_id";
|
||||
|
||||
/**
|
||||
* 有哪些表需要做多租户 这些表需要添加一个字段 ,字段名和tenant_field对应的值一样
|
||||
*/
|
||||
private static final List<String> tenantTable = new ArrayList<String>();
|
||||
/**
|
||||
* ddl 关键字 判断不走多租户的sql过滤
|
||||
*/
|
||||
private static final List<String> DDL_KEYWORD = new ArrayList<String>();
|
||||
static {
|
||||
tenantTable.add("jee_bug_danbiao");
|
||||
DDL_KEYWORD.add("alter");
|
||||
}
|
||||
|
||||
/**
|
||||
* 多租户属于 SQL 解析部分,依赖 MP 分页插件
|
||||
*/
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
PaginationInterceptor paginationInterceptor = new PaginationInterceptor().setLimit(-1);
|
||||
//多租户配置 配置后每次执行sql会走一遍他的转化器 如果不需要多租户功能 可以将其注释
|
||||
tenantConfig(paginationInterceptor);
|
||||
return paginationInterceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多租户的配置
|
||||
* @param paginationInterceptor
|
||||
*/
|
||||
private void tenantConfig(PaginationInterceptor paginationInterceptor){
|
||||
/*
|
||||
* 【测试多租户】 SQL 解析处理拦截器<br>
|
||||
* 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )<br>
|
||||
*/
|
||||
List<ISqlParser> sqlParserList = new ArrayList<>();
|
||||
TenantSqlParser tenantSqlParser = new JeecgTenantParser();
|
||||
tenantSqlParser.setTenantHandler(new TenantHandler() {
|
||||
|
||||
@Override
|
||||
public Expression getTenantId(boolean select) {
|
||||
String tenant_id = TenantContext.getTenant();
|
||||
return new LongValue(tenant_id);
|
||||
}
|
||||
@Override
|
||||
public String getTenantIdColumn() {
|
||||
return tenant_field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doTableFilter(String tableName) {
|
||||
//true则不加租户条件查询 false则加
|
||||
// return excludeTable.contains(tableName);
|
||||
if(tenantTable.contains(tableName)){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Expression in(String ids){
|
||||
final InExpression inExpression = new InExpression();
|
||||
inExpression.setLeftExpression(new Column(getTenantIdColumn()));
|
||||
final ExpressionList itemsList = new ExpressionList();
|
||||
final List<Expression> inValues = new ArrayList<>(2);
|
||||
for(String id:ids.split(",")){
|
||||
inValues.add(new LongValue(id));
|
||||
}
|
||||
itemsList.setExpressions(inValues);
|
||||
inExpression.setRightItemsList(itemsList);
|
||||
return inExpression;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
sqlParserList.add(tenantSqlParser);
|
||||
paginationInterceptor.setSqlParserList(sqlParserList);
|
||||
paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
|
||||
@Override
|
||||
public boolean doFilter(MetaObject metaObject) {
|
||||
String sql = (String) metaObject.getValue(PluginUtils.DELEGATE_BOUNDSQL_SQL);
|
||||
for(String tableName: tenantTable){
|
||||
String sql_lowercase = sql.toLowerCase();
|
||||
if(sql_lowercase.indexOf(tableName.toLowerCase())>=0){
|
||||
for(String key: DDL_KEYWORD){
|
||||
if(sql_lowercase.indexOf(key)>=0){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/*if ("mapper路径.方法名".equals(ms.getId())) {
|
||||
//使用这种判断也可以避免走此过滤器
|
||||
return true;
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// /**
|
||||
// * mybatis-plus SQL执行效率插件【生产环境可以关闭】
|
||||
// */
|
||||
// @Bean
|
||||
// public PerformanceInterceptor performanceInterceptor() {
|
||||
// return new PerformanceInterceptor();
|
||||
// }
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package org.jeecg.config.mybatis;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 多租户 tenant_id存储器
|
||||
*/
|
||||
@Slf4j
|
||||
public class TenantContext {
|
||||
|
||||
private static ThreadLocal<String> currentTenant = new ThreadLocal<>();
|
||||
|
||||
public static void setTenant(String tenant) {
|
||||
log.debug(" setting tenant to " + tenant);
|
||||
currentTenant.set(tenant);
|
||||
}
|
||||
|
||||
public static String getTenant() {
|
||||
return currentTenant.get();
|
||||
}
|
||||
|
||||
public static void clear(){
|
||||
currentTenant.remove();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package org.jeecg.config.oss;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.util.MinioUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Minio文件上传配置文件
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class MinioConfig {
|
||||
@Value(value = "${jeecg.minio.minio_url}")
|
||||
private String minioUrl;
|
||||
@Value(value = "${jeecg.minio.minio_name}")
|
||||
private String minioName;
|
||||
@Value(value = "${jeecg.minio.minio_pass}")
|
||||
private String minioPass;
|
||||
@Value(value = "${jeecg.minio.bucketName}")
|
||||
private String bucketName;
|
||||
|
||||
@Bean
|
||||
public void initMinio(){
|
||||
if(!minioUrl.startsWith("http")){
|
||||
minioUrl = "http://" + minioUrl;
|
||||
}
|
||||
if(!minioUrl.endsWith("/")){
|
||||
minioUrl = minioUrl.concat("/");
|
||||
}
|
||||
MinioUtil.setMinioUrl(minioUrl);
|
||||
MinioUtil.setMinioName(minioName);
|
||||
MinioUtil.setMinioPass(minioPass);
|
||||
MinioUtil.setBucketName(bucketName);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package org.jeecg.config.oss;
|
||||
|
||||
import org.jeecg.common.util.oss.OssBootUtil;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 云存储 配置
|
||||
*/
|
||||
@Configuration
|
||||
public class OssConfiguration {
|
||||
|
||||
@Value("${jeecg.oss.endpoint}")
|
||||
private String endpoint;
|
||||
@Value("${jeecg.oss.accessKey}")
|
||||
private String accessKeyId;
|
||||
@Value("${jeecg.oss.secretKey}")
|
||||
private String accessKeySecret;
|
||||
@Value("${jeecg.oss.bucketName}")
|
||||
private String bucketName;
|
||||
@Value("${jeecg.oss.staticDomain}")
|
||||
private String staticDomain;
|
||||
|
||||
|
||||
@Bean
|
||||
public void initOssBootConfiguration() {
|
||||
OssBootUtil.setEndPoint(endpoint);
|
||||
OssBootUtil.setAccessKeyId(accessKeyId);
|
||||
OssBootUtil.setAccessKeySecret(accessKeySecret);
|
||||
OssBootUtil.setBucketName(bucketName);
|
||||
OssBootUtil.setStaticDomain(staticDomain);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package org.jeecg.config.redis;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
|
||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Duration;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
||||
/**
|
||||
* 开启缓存支持
|
||||
* @Return:
|
||||
*/
|
||||
@Slf4j
|
||||
@EnableCaching
|
||||
@Configuration
|
||||
public class RedisConfig extends CachingConfigurerSupport {
|
||||
|
||||
@Resource
|
||||
private LettuceConnectionFactory lettuceConnectionFactory;
|
||||
|
||||
// /**
|
||||
// * @description 自定义的缓存key的生成策略 若想使用这个key
|
||||
// * 只需要讲注解上keyGenerator的值设置为keyGenerator即可</br>
|
||||
// * @return 自定义策略生成的key
|
||||
// */
|
||||
// @Override
|
||||
// @Bean
|
||||
// public KeyGenerator keyGenerator() {
|
||||
// return new KeyGenerator() {
|
||||
// @Override
|
||||
// public Object generate(Object target, Method method, Object... params) {
|
||||
// StringBuilder sb = new StringBuilder();
|
||||
// sb.append(target.getClass().getName());
|
||||
// sb.append(method.getDeclaringClass().getName());
|
||||
// Arrays.stream(params).map(Object::toString).forEach(sb::append);
|
||||
// return sb.toString();
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
/**
|
||||
* RedisTemplate配置
|
||||
*
|
||||
* @param lettuceConnectionFactory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
|
||||
log.info(" --- redis config init --- ");
|
||||
// 设置序列化
|
||||
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
|
||||
om.enableDefaultTyping(DefaultTyping.NON_FINAL);
|
||||
jackson2JsonRedisSerializer.setObjectMapper(om);
|
||||
// 配置redisTemplate
|
||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
|
||||
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
|
||||
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
|
||||
redisTemplate.setKeySerializer(stringSerializer);// key序列化
|
||||
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
|
||||
redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
|
||||
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
|
||||
redisTemplate.afterPropertiesSet();
|
||||
return redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存配置管理器
|
||||
*
|
||||
* @param factory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public CacheManager cacheManager(LettuceConnectionFactory factory) {
|
||||
// 配置序列化(缓存默认有效期 6小时)
|
||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(6));
|
||||
RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
|
||||
|
||||
// 以锁写入的方式创建RedisCacheWriter对象
|
||||
//RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
|
||||
// 创建默认缓存配置对象
|
||||
/* 默认配置,设置缓存有效期 1小时*/
|
||||
//RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(1));
|
||||
/* 自定义配置test:demo 的超时时间为 5分钟*/
|
||||
RedisCacheManager cacheManager = RedisCacheManager.builder(RedisCacheWriter.lockingRedisCacheWriter(factory)).cacheDefaults(redisCacheConfiguration)
|
||||
.withInitialCacheConfigurations(singletonMap(CacheConstant.TEST_DEMO_CACHE, RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues()))
|
||||
.transactionAware().build();
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package org.jeecg.config.shiro;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
|
||||
/**
|
||||
* @Author Scott
|
||||
* @create 2018-07-12 15:19
|
||||
* @desc
|
||||
**/
|
||||
public class JwtToken implements AuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String token;
|
||||
|
||||
public JwtToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,309 @@
|
||||
package org.jeecg.config.shiro;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
|
||||
import org.apache.shiro.mgt.DefaultSubjectDAO;
|
||||
import org.apache.shiro.mgt.SecurityManager;
|
||||
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
|
||||
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
|
||||
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
|
||||
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
|
||||
import org.crazycake.shiro.IRedisManager;
|
||||
import org.crazycake.shiro.RedisCacheManager;
|
||||
import org.crazycake.shiro.RedisClusterManager;
|
||||
import org.crazycake.shiro.RedisManager;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.config.shiro.filters.JwtFilter;
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import redis.clients.jedis.HostAndPort;
|
||||
import redis.clients.jedis.JedisCluster;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.Filter;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author: Scott
|
||||
* @date: 2018/2/7
|
||||
* @description: shiro 配置类
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class ShiroConfig {
|
||||
|
||||
@Value("${jeecg.shiro.excludeUrls}")
|
||||
private String excludeUrls;
|
||||
@Resource
|
||||
LettuceConnectionFactory lettuceConnectionFactory;
|
||||
@Autowired
|
||||
private Environment env;
|
||||
|
||||
|
||||
/**
|
||||
* Filter Chain定义说明
|
||||
*
|
||||
* 1、一个URL可以配置多个Filter,使用逗号分隔
|
||||
* 2、当设置多个过滤器时,全部验证通过,才视为通过
|
||||
* 3、部分过滤器可指定参数,如perms,roles
|
||||
*/
|
||||
@Bean("shiroFilter")
|
||||
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
|
||||
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
|
||||
shiroFilterFactoryBean.setSecurityManager(securityManager);
|
||||
// 拦截器
|
||||
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
|
||||
if(oConvertUtils.isNotEmpty(excludeUrls)){
|
||||
String[] permissionUrl = excludeUrls.split(",");
|
||||
for(String url : permissionUrl){
|
||||
filterChainDefinitionMap.put(url,"anon");
|
||||
}
|
||||
}
|
||||
|
||||
//cas验证登录
|
||||
filterChainDefinitionMap.put("/cas/client/validateLogin", "anon");
|
||||
// 配置不会被拦截的链接 顺序判断
|
||||
filterChainDefinitionMap.put("/sys/randomImage/**", "anon"); //登录验证码接口排除
|
||||
filterChainDefinitionMap.put("/sys/checkCaptcha", "anon"); //登录验证码接口排除
|
||||
filterChainDefinitionMap.put("/sys/login", "anon"); //登录接口排除
|
||||
filterChainDefinitionMap.put("/sys/mLogin", "anon"); //登录接口排除
|
||||
filterChainDefinitionMap.put("/sys/logout", "anon"); //登出接口排除
|
||||
filterChainDefinitionMap.put("/thirdLogin/**", "anon"); //第三方登录
|
||||
filterChainDefinitionMap.put("/sys/getEncryptedString", "anon"); //获取加密串
|
||||
filterChainDefinitionMap.put("/sys/sms", "anon");//短信验证码
|
||||
filterChainDefinitionMap.put("/sys/phoneLogin", "anon");//手机登录
|
||||
filterChainDefinitionMap.put("/sys/user/checkOnlyUser", "anon");//校验用户是否存在
|
||||
filterChainDefinitionMap.put("/sys/user/register", "anon");//用户注册
|
||||
filterChainDefinitionMap.put("/sys/user/querySysUser", "anon");//根据手机号获取用户信息
|
||||
filterChainDefinitionMap.put("/sys/user/phoneVerification", "anon");//用户忘记密码验证手机号
|
||||
filterChainDefinitionMap.put("/sys/user/passwordChange", "anon");//用户更改密码
|
||||
filterChainDefinitionMap.put("/auth/2step-code", "anon");//登录验证码
|
||||
filterChainDefinitionMap.put("/sys/common/static/**", "anon");//图片预览 &下载文件不限制token
|
||||
//filterChainDefinitionMap.put("/sys/common/view/**", "anon");//图片预览不限制token
|
||||
//filterChainDefinitionMap.put("/sys/common/download/**", "anon");//文件下载不限制token
|
||||
filterChainDefinitionMap.put("/sys/common/pdf/**", "anon");//pdf预览
|
||||
filterChainDefinitionMap.put("/generic/**", "anon");//pdf预览需要文件
|
||||
filterChainDefinitionMap.put("/", "anon");
|
||||
filterChainDefinitionMap.put("/doc.html", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.js", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.css", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.html", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.svg", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.pdf", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.jpg", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.png", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.ico", "anon");
|
||||
|
||||
// update-begin--Author:sunjianlei Date:20190813 for:排除字体格式的后缀
|
||||
filterChainDefinitionMap.put("/**/*.ttf", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.woff2", "anon");
|
||||
// update-begin--Author:sunjianlei Date:20190813 for:排除字体格式的后缀
|
||||
|
||||
filterChainDefinitionMap.put("/druid/**", "anon");
|
||||
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
|
||||
filterChainDefinitionMap.put("/swagger**/**", "anon");
|
||||
filterChainDefinitionMap.put("/webjars/**", "anon");
|
||||
filterChainDefinitionMap.put("/v2/**", "anon");
|
||||
|
||||
// update-begin--Author:sunjianlei Date:20190813 for:表单设计器不要一次性全排除
|
||||
filterChainDefinitionMap.put("/desform/index/**", "anon");
|
||||
filterChainDefinitionMap.put("/desform/ext/**", "anon");
|
||||
filterChainDefinitionMap.put("/desform/add/**", "anon");
|
||||
filterChainDefinitionMap.put("/desform/view/**", "anon");
|
||||
filterChainDefinitionMap.put("/desform/edit/**", "anon");
|
||||
filterChainDefinitionMap.put("/desform/detail/**", "anon");
|
||||
// update-end--Author:sunjianlei Date:20190813 for:表单设计器不要一次性全排除
|
||||
|
||||
//报表设计器排除
|
||||
filterChainDefinitionMap.put("/design/report/**", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.js.map", "anon");
|
||||
filterChainDefinitionMap.put("/**/*.css.map", "anon");
|
||||
|
||||
//在线文档管理
|
||||
filterChainDefinitionMap.put("/filemanage/**", "anon");
|
||||
//在线聊天排除
|
||||
filterChainDefinitionMap.put("/oa/im/**", "anon");
|
||||
filterChainDefinitionMap.put("/im/dist/**", "anon");
|
||||
|
||||
//大屏设计器排除
|
||||
filterChainDefinitionMap.put("/jmreport/bigscreen/**", "anon");
|
||||
filterChainDefinitionMap.put("/test/bigScreen/**", "anon");
|
||||
filterChainDefinitionMap.put("/bigscreen/**", "anon");
|
||||
|
||||
//测试示例
|
||||
//filterChainDefinitionMap.put("/test/jeecgDemo/html", "anon"); //模板页面
|
||||
//filterChainDefinitionMap.put("/test/jeecgDemo/redis/**", "anon"); //redis测试
|
||||
|
||||
//流程模块组件请求
|
||||
// filterChainDefinitionMap.put("/act/process/**", "anon");
|
||||
// filterChainDefinitionMap.put("/act/task/**", "anon");
|
||||
// filterChainDefinitionMap.put("/act/model/**", "anon");
|
||||
// 流程图请求地址
|
||||
filterChainDefinitionMap.put("/act/task/traceImage", "anon");
|
||||
filterChainDefinitionMap.put("/act/process/processPic", "anon");
|
||||
filterChainDefinitionMap.put("/act/process/downProcessXml", "anon");
|
||||
filterChainDefinitionMap.put("/act/process/resource", "anon");
|
||||
filterChainDefinitionMap.put("/service/editor/**", "anon");
|
||||
filterChainDefinitionMap.put("/service/model/**", "anon");
|
||||
filterChainDefinitionMap.put("/service/model/**/save", "anon");
|
||||
filterChainDefinitionMap.put("/editor-app/**", "anon");
|
||||
filterChainDefinitionMap.put("/diagram-viewer/**", "anon");
|
||||
filterChainDefinitionMap.put("/modeler.html", "anon");
|
||||
filterChainDefinitionMap.put("/designer", "anon");
|
||||
filterChainDefinitionMap.put("/designer/**", "anon");
|
||||
filterChainDefinitionMap.put("/plug-in/**", "anon");
|
||||
|
||||
//排除Online请求
|
||||
filterChainDefinitionMap.put("/auto/cgform/**", "anon");
|
||||
//FineReport报表
|
||||
filterChainDefinitionMap.put("/ReportServer**", "anon");
|
||||
|
||||
//websocket排除
|
||||
filterChainDefinitionMap.put("/websocket/**", "anon");
|
||||
filterChainDefinitionMap.put("/newsWebsocket/**", "anon");
|
||||
|
||||
//我的聊天排除
|
||||
filterChainDefinitionMap.put("/oa/im/**","anon");
|
||||
filterChainDefinitionMap.put("/im/dist/**","anon");
|
||||
filterChainDefinitionMap.put("/eoaSocket/**","anon");
|
||||
|
||||
// VXE Table WebSocket 排除(前端vxe组件无痕刷新功能)
|
||||
filterChainDefinitionMap.put("/vxeSocket/**", "anon");
|
||||
|
||||
//wps
|
||||
filterChainDefinitionMap.put("/v1/**","anon");
|
||||
//公文路径部分拦截
|
||||
filterChainDefinitionMap.put("/officialdoc/oaOfficialdocTemp/editByFiledId","anon");
|
||||
filterChainDefinitionMap.put("/officialdoc/oaOfficialdocOrgancode/getTemp","anon");
|
||||
filterChainDefinitionMap.put("/officialdoc/oaOfficialdocTemp/getTempByFileId","anon");
|
||||
filterChainDefinitionMap.put("/officialdoc/oaOfficialdocTemp/updateNameByFileId","anon");
|
||||
//ureport报表拦截排除
|
||||
filterChainDefinitionMap.put("/ureport/**","anon");
|
||||
//插件商城排除
|
||||
filterChainDefinitionMap.put("/pluginMall/**","anon");
|
||||
// 添加自己的过滤器并且取名为jwt
|
||||
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
|
||||
//如果cloudServer为空 则说明是单体 需要加载跨域配置
|
||||
Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY);
|
||||
filterMap.put("jwt", new JwtFilter(cloudServer==null));
|
||||
shiroFilterFactoryBean.setFilters(filterMap);
|
||||
// <!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边
|
||||
filterChainDefinitionMap.put("/**", "jwt");
|
||||
|
||||
// 未授权界面返回JSON
|
||||
shiroFilterFactoryBean.setUnauthorizedUrl("/sys/common/403");
|
||||
shiroFilterFactoryBean.setLoginUrl("/sys/common/403");
|
||||
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
|
||||
return shiroFilterFactoryBean;
|
||||
}
|
||||
|
||||
@Bean("securityManager")
|
||||
public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
|
||||
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
|
||||
securityManager.setRealm(myRealm);
|
||||
|
||||
/*
|
||||
* 关闭shiro自带的session,详情见文档
|
||||
* http://shiro.apache.org/session-management.html#SessionManagement-
|
||||
* StatelessApplications%28Sessionless%29
|
||||
*/
|
||||
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
|
||||
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
|
||||
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
|
||||
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
|
||||
securityManager.setSubjectDAO(subjectDAO);
|
||||
//自定义缓存实现,使用redis
|
||||
securityManager.setCacheManager(redisCacheManager());
|
||||
return securityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下面的代码是添加注解支持
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@DependsOn("lifecycleBeanPostProcessor")
|
||||
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
|
||||
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
|
||||
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
|
||||
/**
|
||||
* 解决重复代理问题 github#994
|
||||
* 添加前缀判断 不匹配 任何Advisor
|
||||
*/
|
||||
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
|
||||
defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
|
||||
return defaultAdvisorAutoProxyCreator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
|
||||
return new LifecycleBeanPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
|
||||
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
|
||||
advisor.setSecurityManager(securityManager);
|
||||
return advisor;
|
||||
}
|
||||
|
||||
/**
|
||||
* cacheManager 缓存 redis实现
|
||||
* 使用的是shiro-redis开源插件
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public RedisCacheManager redisCacheManager() {
|
||||
log.info("===============(1)创建缓存管理器RedisCacheManager");
|
||||
RedisCacheManager redisCacheManager = new RedisCacheManager();
|
||||
redisCacheManager.setRedisManager(redisManager());
|
||||
//redis中针对不同用户缓存(此处的id需要对应user实体中的id字段,用于唯一标识)
|
||||
redisCacheManager.setPrincipalIdFieldName("id");
|
||||
//用户权限信息缓存时间
|
||||
redisCacheManager.setExpire(200000);
|
||||
return redisCacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置shiro redisManager
|
||||
* 使用的是shiro-redis开源插件
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public IRedisManager redisManager() {
|
||||
log.info("===============(2)创建RedisManager,连接Redis..");
|
||||
IRedisManager manager;
|
||||
// redis 单机支持,在集群为空,或者集群无机器时候使用 add by jzyadmin@163.com
|
||||
if (lettuceConnectionFactory.getClusterConfiguration() == null || lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().isEmpty()) {
|
||||
RedisManager redisManager = new RedisManager();
|
||||
redisManager.setHost(lettuceConnectionFactory.getHostName());
|
||||
redisManager.setPort(lettuceConnectionFactory.getPort());
|
||||
redisManager.setTimeout(0);
|
||||
if (!StringUtils.isEmpty(lettuceConnectionFactory.getPassword())) {
|
||||
redisManager.setPassword(lettuceConnectionFactory.getPassword());
|
||||
}
|
||||
manager = redisManager;
|
||||
}else{
|
||||
// redis 集群支持,优先使用集群配置 add by jzyadmin@163.com
|
||||
RedisClusterManager redisManager = new RedisClusterManager();
|
||||
Set<HostAndPort> portSet = new HashSet<>();
|
||||
lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().forEach(node -> portSet.add(new HostAndPort(node.getHost() , node.getPort())));
|
||||
JedisCluster jedisCluster = new JedisCluster(portSet);
|
||||
redisManager.setJedisCluster(jedisCluster);
|
||||
manager = redisManager;
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
package org.jeecg.config.shiro;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.jeecg.common.api.CommonAPI;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.system.util.JwtUtil;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @Description: 用户登录鉴权和获取用户授权
|
||||
* @Author: Scott
|
||||
* @Date: 2019-4-23 8:13
|
||||
* @Version: 1.1
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ShiroRealm extends AuthorizingRealm {
|
||||
@Lazy
|
||||
@Resource
|
||||
private CommonAPI commonAPI;
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
/**
|
||||
* 必须重写此方法,不然Shiro会报错
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(AuthenticationToken token) {
|
||||
return token instanceof JwtToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* 权限信息认证(包括角色以及权限)是用户访问controller的时候才进行验证(redis存储的此处权限信息)
|
||||
* 触发检测用户权限时才会调用此方法,例如checkRole,checkPermission
|
||||
*
|
||||
* @param principals 身份信息
|
||||
* @return AuthorizationInfo 权限信息
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||
log.info("===============Shiro权限认证开始============ [ roles、permissions]==========");
|
||||
String username = null;
|
||||
if (principals != null) {
|
||||
LoginUser sysUser = (LoginUser) principals.getPrimaryPrincipal();
|
||||
username = sysUser.getUsername();
|
||||
}
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
|
||||
|
||||
// 设置用户拥有的角色集合,比如“admin,test”
|
||||
Set<String> roleSet = commonAPI.queryUserRoles(username);
|
||||
System.out.println(roleSet.toString());
|
||||
info.setRoles(roleSet);
|
||||
|
||||
// 设置用户拥有的权限集合,比如“sys:role:add,sys:user:add”
|
||||
Set<String> permissionSet = commonAPI.queryUserAuths(username);
|
||||
info.addStringPermissions(permissionSet);
|
||||
System.out.println(permissionSet);
|
||||
log.info("===============Shiro权限认证成功==============");
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息认证是在用户进行登录的时候进行验证(不存redis)
|
||||
* 也就是说验证用户输入的账号和密码是否正确,错误抛出异常
|
||||
*
|
||||
* @param auth 用户登录的账号密码信息
|
||||
* @return 返回封装了用户信息的 AuthenticationInfo 实例
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
|
||||
log.debug("===============Shiro身份认证开始============doGetAuthenticationInfo==========");
|
||||
String token = (String) auth.getCredentials();
|
||||
if (token == null) {
|
||||
log.info("————————身份认证失败——————————IP地址: "+ oConvertUtils.getIpAddrByRequest(SpringContextUtils.getHttpServletRequest()));
|
||||
throw new AuthenticationException("token为空!");
|
||||
}
|
||||
// 校验token有效性
|
||||
LoginUser loginUser = this.checkUserTokenIsEffect(token);
|
||||
return new SimpleAuthenticationInfo(loginUser, token, getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token的有效性
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
public LoginUser checkUserTokenIsEffect(String token) throws AuthenticationException {
|
||||
// 解密获得username,用于和数据库进行对比
|
||||
String username = JwtUtil.getUsername(token);
|
||||
if (username == null) {
|
||||
throw new AuthenticationException("token非法无效!");
|
||||
}
|
||||
|
||||
// 查询用户信息
|
||||
log.debug("———校验token是否有效————checkUserTokenIsEffect——————— "+ token);
|
||||
LoginUser loginUser = (LoginUser) redisUtil.get(CacheConstant.SYS_USERS_CACHE_JWT+":"+token);
|
||||
//TODO 当前写法导致两个小时操作中token过期
|
||||
//如果redis缓存用户信息为空,则通过接口获取用户信息,避免超过两个小时操作中token过期
|
||||
if(loginUser==null){
|
||||
loginUser = commonAPI.getUserByName(username);
|
||||
}
|
||||
if (loginUser == null) {
|
||||
throw new AuthenticationException("用户不存在!");
|
||||
}
|
||||
// 判断用户状态
|
||||
if (loginUser.getStatus() != 1) {
|
||||
throw new AuthenticationException("账号已被锁定,请联系管理员!");
|
||||
}
|
||||
// 校验token是否超时失效 & 或者账号密码是否错误
|
||||
if (!jwtTokenRefresh(token, username, loginUser.getPassword())) {
|
||||
throw new AuthenticationException("Token失效,请重新登录!");
|
||||
}
|
||||
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* JWTToken刷新生命周期 (实现: 用户在线操作不掉线功能)
|
||||
* 1、登录成功后将用户的JWT生成的Token作为k、v存储到cache缓存里面(这时候k、v值一样),缓存有效期设置为Jwt有效时间的2倍
|
||||
* 2、当该用户再次请求时,通过JWTFilter层层校验之后会进入到doGetAuthenticationInfo进行身份验证
|
||||
* 3、当该用户这次请求jwt生成的token值已经超时,但该token对应cache中的k还是存在,则表示该用户一直在操作只是JWT的token失效了,程序会给token对应的k映射的v值重新生成JWTToken并覆盖v值,该缓存生命周期重新计算
|
||||
* 4、当该用户这次请求jwt在生成的token值已经超时,并在cache中不存在对应的k,则表示该用户账户空闲超时,返回用户信息已失效,请重新登录。
|
||||
* 注意: 前端请求Header中设置Authorization保持不变,校验有效性以缓存中的token为准。
|
||||
* 用户过期时间 = Jwt有效时间 * 2。
|
||||
*
|
||||
* @param userName
|
||||
* @param passWord
|
||||
* @return
|
||||
*/
|
||||
public boolean jwtTokenRefresh(String token, String userName, String passWord) {
|
||||
String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
|
||||
if (oConvertUtils.isNotEmpty(cacheToken)) {
|
||||
// 校验token有效性
|
||||
if (!JwtUtil.verify(cacheToken, userName, passWord)) {
|
||||
String newAuthorization = JwtUtil.sign(userName, passWord);
|
||||
// 设置超时时间
|
||||
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
|
||||
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME *2 / 1000);
|
||||
log.info("——————————用户在线操作,更新token保证不掉线—————————jwtTokenRefresh——————— "+ token);
|
||||
}
|
||||
//update-begin--Author:scott Date:20191005 for:解决每次请求,都重写redis中 token缓存问题
|
||||
// else {
|
||||
// // 设置超时时间
|
||||
// redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);
|
||||
// redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
|
||||
// }
|
||||
//update-end--Author:scott Date:20191005 for:解决每次请求,都重写redis中 token缓存问题
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除当前用户的权限认证缓存
|
||||
*
|
||||
* @param principals 权限信息
|
||||
*/
|
||||
@Override
|
||||
public void clearCache(PrincipalCollection principals) {
|
||||
super.clearCache(principals);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package org.jeecg.config.shiro.filters;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.config.mybatis.TenantContext;
|
||||
import org.jeecg.config.shiro.JwtToken;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @Description: 鉴权登录拦截器
|
||||
* @Author: Scott
|
||||
* @Date: 2018/10/7
|
||||
**/
|
||||
@Slf4j
|
||||
public class JwtFilter extends BasicHttpAuthenticationFilter {
|
||||
|
||||
private boolean allowOrigin = true;
|
||||
|
||||
public JwtFilter(){}
|
||||
public JwtFilter(boolean allowOrigin){
|
||||
this.allowOrigin = allowOrigin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行登录认证
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param mappedValue
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
|
||||
try {
|
||||
executeLogin(request, response);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
throw new AuthenticationException("Token失效,请重新登录", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
|
||||
|
||||
JwtToken jwtToken = new JwtToken(token);
|
||||
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
|
||||
getSubject(request, response).login(jwtToken);
|
||||
// 如果没有抛出异常则代表登入成功,返回true
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对跨域提供支持
|
||||
*/
|
||||
@Override
|
||||
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
|
||||
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
|
||||
if(allowOrigin){
|
||||
httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
|
||||
httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
|
||||
httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
|
||||
//update-begin-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题
|
||||
// 是否允许发送Cookie,默认Cookie不包括在CORS请求之中。设为true时,表示服务器允许Cookie包含在请求中。
|
||||
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
|
||||
//update-end-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题
|
||||
}
|
||||
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
|
||||
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
|
||||
httpServletResponse.setStatus(HttpStatus.OK.value());
|
||||
return false;
|
||||
}
|
||||
//update-begin-author:taoyan date:20200708 for:多租户用到
|
||||
String tenant_id = httpServletRequest.getHeader(CommonConstant.TENANT_ID);
|
||||
TenantContext.setTenant(tenant_id);
|
||||
//update-end-author:taoyan date:20200708 for:多租户用到
|
||||
return super.preHandle(request, response);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,67 @@
|
||||
package org.jeecg.config.shiro.filters;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.web.filter.AccessControlFilter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @Author Scott
|
||||
* @create 2019-02-01 15:56
|
||||
* @desc 鉴权请求URL访问权限拦截器
|
||||
*/
|
||||
@Slf4j
|
||||
public class ResourceCheckFilter extends AccessControlFilter {
|
||||
|
||||
private String errorUrl;
|
||||
|
||||
public String getErrorUrl() {
|
||||
return errorUrl;
|
||||
}
|
||||
|
||||
public void setErrorUrl(String errorUrl) {
|
||||
this.errorUrl = errorUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* 表示是否允许访问 ,如果允许访问返回true,否则false;
|
||||
*
|
||||
* @param servletRequest
|
||||
* @param servletResponse
|
||||
* @param o 表示写在拦截器中括号里面的字符串 mappedValue 就是 [urls] 配置中拦截器参数部分
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
|
||||
Subject subject = getSubject(servletRequest, servletResponse);
|
||||
String url = getPathWithinApplication(servletRequest);
|
||||
log.info("当前用户正在访问的 url => " + url);
|
||||
return subject.isPermitted(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* onAccessDenied:表示当访问拒绝时是否已经处理了; 如果返回 true 表示需要继续处理; 如果返回 false
|
||||
* 表示该拦截器实例已经处理了,将直接返回即可。
|
||||
*
|
||||
* @param servletRequest
|
||||
* @param servletResponse
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@Override
|
||||
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
|
||||
log.info("当 isAccessAllowed 返回 false 的时候,才会执行 method onAccessDenied ");
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
response.sendRedirect(request.getContextPath() + this.errorUrl);
|
||||
|
||||
// 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package org.jeecg.modules.base.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.SqlParser;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.jeecg.common.api.dto.LogDTO;
|
||||
|
||||
public interface BaseCommonMapper {
|
||||
|
||||
/**
|
||||
* 保存日志
|
||||
* @param dto
|
||||
*/
|
||||
@SqlParser(filter=true)
|
||||
void saveLog(@Param("dto")LogDTO dto);
|
||||
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="org.jeecg.modules.base.mapper.BaseCommonMapper">
|
||||
|
||||
<!-- 保存日志11 -->
|
||||
<insert id="saveLog" parameterType="Object">
|
||||
insert into sys_log (id, log_type, log_content, method, operate_type, request_param, ip, userid, username, cost_time, create_time)
|
||||
values(
|
||||
#{dto.id,jdbcType=VARCHAR},
|
||||
#{dto.logType,jdbcType=INTEGER},
|
||||
#{dto.logContent,jdbcType=VARCHAR},
|
||||
#{dto.method,jdbcType=VARCHAR},
|
||||
#{dto.operateType,jdbcType=INTEGER},
|
||||
#{dto.requestParam,jdbcType=VARCHAR},
|
||||
#{dto.ip,jdbcType=VARCHAR},
|
||||
#{dto.userid,jdbcType=VARCHAR},
|
||||
#{dto.username,jdbcType=VARCHAR},
|
||||
#{dto.costTime,jdbcType=BIGINT},
|
||||
#{dto.createTime,jdbcType=TIMESTAMP}
|
||||
)
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
@ -0,0 +1,34 @@
|
||||
package org.jeecg.modules.base.service;
|
||||
|
||||
import org.jeecg.common.api.dto.LogDTO;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
|
||||
/**
|
||||
* common接口
|
||||
*/
|
||||
public interface BaseCommonService {
|
||||
|
||||
/**
|
||||
* 保存日志
|
||||
* @param logDTO
|
||||
*/
|
||||
void addLog(LogDTO logDTO);
|
||||
|
||||
/**
|
||||
* 保存日志
|
||||
* @param LogContent
|
||||
* @param logType
|
||||
* @param operateType
|
||||
* @param user
|
||||
*/
|
||||
void addLog(String LogContent, Integer logType, Integer operateType, LoginUser user);
|
||||
|
||||
/**
|
||||
* 保存日志
|
||||
* @param LogContent
|
||||
* @param logType
|
||||
* @param operateType
|
||||
*/
|
||||
void addLog(String LogContent, Integer logType, Integer operateType);
|
||||
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
package org.jeecg.modules.base.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.jeecg.common.api.dto.LogDTO;
|
||||
import org.jeecg.common.constant.CacheConstant;
|
||||
import org.jeecg.modules.base.mapper.BaseCommonMapper;
|
||||
import org.jeecg.modules.base.service.BaseCommonService;
|
||||
import org.jeecg.common.system.vo.LoginUser;
|
||||
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
|
||||
import org.jeecg.common.system.vo.SysUserCacheInfo;
|
||||
import org.jeecg.common.util.IPUtils;
|
||||
import org.jeecg.common.util.SpringContextUtils;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class BaseCommonServiceImpl implements BaseCommonService {
|
||||
|
||||
@Resource
|
||||
private BaseCommonMapper baseCommonMapper;
|
||||
|
||||
@Override
|
||||
public void addLog(LogDTO logDTO) {
|
||||
if(oConvertUtils.isEmpty(logDTO.getId())){
|
||||
logDTO.setId(String.valueOf(IdWorker.getId()));
|
||||
}
|
||||
baseCommonMapper.saveLog(logDTO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLog(String logContent, Integer logType, Integer operatetype, LoginUser user) {
|
||||
LogDTO sysLog = new LogDTO();
|
||||
sysLog.setId(String.valueOf(IdWorker.getId()));
|
||||
//注解上的描述,操作日志内容
|
||||
sysLog.setLogContent(logContent);
|
||||
sysLog.setLogType(logType);
|
||||
sysLog.setOperateType(operatetype);
|
||||
try {
|
||||
//获取request
|
||||
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
|
||||
//设置IP地址
|
||||
sysLog.setIp(IPUtils.getIpAddr(request));
|
||||
} catch (Exception e) {
|
||||
sysLog.setIp("127.0.0.1");
|
||||
}
|
||||
//获取登录用户信息
|
||||
if(user==null){
|
||||
try {
|
||||
user = (LoginUser) SecurityUtils.getSubject().getPrincipal();
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if(user!=null){
|
||||
sysLog.setUserid(user.getUsername());
|
||||
sysLog.setUsername(user.getRealname());
|
||||
}
|
||||
sysLog.setCreateTime(new Date());
|
||||
//保存系统日志
|
||||
baseCommonMapper.saveLog(sysLog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLog(String logContent, Integer logType, Integer operateType) {
|
||||
addLog(logContent, logType, operateType, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user