init
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
package io.v.nutz.web.commons.base;
|
||||
|
||||
import io.v.nutz.base.enums.Env;
|
||||
import io.v.nutz.task.services.TaskPlatformService;
|
||||
import io.v.nutz.sys.models.Sys_config;
|
||||
import io.v.nutz.sys.models.Sys_gx;
|
||||
import io.v.nutz.sys.models.Sys_route;
|
||||
import io.v.nutz.sys.models.Sys_task;
|
||||
import io.v.nutz.sys.services.SysConfigService;
|
||||
import io.v.nutz.sys.services.SysRouteService;
|
||||
import io.v.nutz.sys.services.SysTaskService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2016/12/19.
|
||||
*/
|
||||
@IocBean(create = "init")
|
||||
@Slf4j
|
||||
public class Globals {
|
||||
//项目路径
|
||||
public static String AppRoot = "";
|
||||
//pdf Path
|
||||
public static String PdfBase;
|
||||
|
||||
static {
|
||||
try {
|
||||
PdfBase = new File("").getCanonicalPath() + "/pdf/";
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//环境
|
||||
public static Env Environment = Env.dev;
|
||||
//系统登录地址
|
||||
public static String LoginUrl = "/platform/login";
|
||||
/*手机登录地址*/
|
||||
public static String LoginUrlMobile = "/mobile/login";
|
||||
//项目目录
|
||||
public static String AppBase = "";
|
||||
//项目名称
|
||||
public static String AppName = "";
|
||||
//项目短名称
|
||||
public static String AppShrotName = "";
|
||||
//项目域名 (设置一个默认的 为优先级考虑)
|
||||
public static String AppDomain = "https://zhgh.zufe.edu.cn";
|
||||
//cas地址
|
||||
public static String CasAddress = "https://cas.zufe.edu.cn/cas";
|
||||
//文件访问域名
|
||||
public static String AppFileDomain = "";
|
||||
//文件上传路径
|
||||
public static String AppUploadBase = "";
|
||||
//学校代码
|
||||
public static String schoolCode = "zufe";
|
||||
//系统自定义参数
|
||||
public static NutMap MyConfig = NutMap.NEW();
|
||||
//自定义路由
|
||||
public static NutMap RouteMap = NutMap.NEW();
|
||||
//微信map
|
||||
public static NutMap WxMap = NutMap.NEW();
|
||||
//高校名称
|
||||
public static String schoolName = "浙江财经大学";
|
||||
|
||||
public static String BrandActivityName = "";
|
||||
|
||||
@Inject
|
||||
private SysConfigService sysConfigService;
|
||||
@Inject
|
||||
private SysRouteService sysRouteService;
|
||||
@Inject
|
||||
private TaskPlatformService taskPlatformService;
|
||||
@Inject
|
||||
private SysTaskService sysTaskService;
|
||||
|
||||
@Inject
|
||||
private PropertiesProxy conf;
|
||||
|
||||
public void init() {
|
||||
initSysConfig(sysConfigService);
|
||||
initRoute(sysRouteService);
|
||||
initTask(sysTaskService);
|
||||
initEnv(conf);
|
||||
}
|
||||
|
||||
public static void initEnv(PropertiesProxy conf) {
|
||||
String env = conf.get("v.environment", "dev");
|
||||
switch (env) {
|
||||
case "dev":
|
||||
Globals.Environment = Env.dev;
|
||||
break;
|
||||
case "prod":
|
||||
Globals.Environment = Env.prod;
|
||||
break;
|
||||
default:
|
||||
Globals.Environment = Env.dev;
|
||||
break;
|
||||
}
|
||||
Globals.LoginUrl = conf.get("v.loginUrl", LoginUrl);
|
||||
Globals.LoginUrlMobile = conf.get("v.loginUrlMobile", LoginUrlMobile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param e
|
||||
* @return 是否为 ${e} 环境
|
||||
*/
|
||||
public static boolean isEnv(Env e) {
|
||||
return e.equals(Globals.Environment);
|
||||
}
|
||||
|
||||
public void initTask(SysTaskService sysTaskService) {
|
||||
taskPlatformService.clear();
|
||||
List<Sys_task> taskList = sysTaskService.query();
|
||||
for (Sys_task sysTask : taskList) {
|
||||
try {
|
||||
if (!sysTask.isDisabled())//不存在则新建
|
||||
taskPlatformService.add(sysTask.getId(), sysTask.getId(), sysTask.getJobClass(), sysTask.getCron()
|
||||
, sysTask.getNote(), sysTask.getData());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void initSysConfig(SysConfigService sysConfigService) {
|
||||
Globals.MyConfig.clear();
|
||||
List<Sys_config> configList = sysConfigService.query();
|
||||
for (Sys_config sysConfig : configList) {
|
||||
switch (Strings.sNull(sysConfig.getConfigKey())) {
|
||||
case "AppName":
|
||||
Globals.AppName = sysConfig.getConfigValue();
|
||||
break;
|
||||
case "AppShrotName":
|
||||
Globals.AppShrotName = sysConfig.getConfigValue();
|
||||
break;
|
||||
case "AppDomain":
|
||||
Globals.AppDomain = sysConfig.getConfigValue();
|
||||
break;
|
||||
case "AppFileDomain":
|
||||
Globals.AppFileDomain = sysConfig.getConfigValue();
|
||||
break;
|
||||
case "AppUploadBase":
|
||||
Globals.AppUploadBase = sysConfig.getConfigValue();
|
||||
break;
|
||||
case "BrandActivityName":
|
||||
Globals.BrandActivityName = sysConfig.getConfigValue();
|
||||
break;
|
||||
default:
|
||||
Globals.MyConfig.put(sysConfig.getConfigKey(), sysConfig.getConfigValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
Sys_gx sysGx = sysConfigService.dao().fetch(Sys_gx.class);
|
||||
if (sysGx != null) {
|
||||
Globals.schoolName = sysGx.getGxname();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void initRoute(SysRouteService sysRouteService) {
|
||||
Globals.RouteMap.clear();
|
||||
List<Sys_route> routeList = sysRouteService.query(Cnd.where("disabled", "=", false));
|
||||
for (Sys_route route : routeList) {
|
||||
Globals.RouteMap.put(route.getUrl(), route);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initWx() {
|
||||
Globals.WxMap.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
package io.v.nutz.web.commons.controller;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
|
||||
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
|
||||
import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;
|
||||
import io.v.nutz.base.utils.WordUtil;
|
||||
import io.v.nutz.sys.models.Sys_file;
|
||||
import io.v.nutz.sys.models.Sys_log;
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import io.v.nutz.web.commons.slog.SLogService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.coobird.thumbnailator.Thumbnails;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.nutz.boot.starter.ftp.FtpService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.entity.Record;
|
||||
import org.nutz.ioc.IocException;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.mvc.annotation.*;
|
||||
import org.nutz.mvc.upload.TempFile;
|
||||
import org.nutz.mvc.upload.UploadAdaptor;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @FileName io.v.nutz.web.commons.controller.FilePreviewController
|
||||
* @Description: TODO
|
||||
* @Author zxc
|
||||
* @Date 2022/5/19:19:36
|
||||
* @Version V1.0
|
||||
**/
|
||||
|
||||
@IocBean
|
||||
@At("/file_server")
|
||||
@Slf4j
|
||||
public class FileServerController {
|
||||
|
||||
@Inject
|
||||
private PropertiesProxy conf;
|
||||
|
||||
private static final List<String> IMGTYPES = new ArrayList<>() {{
|
||||
add("JPG");
|
||||
add("JPEG");
|
||||
add("PNG");
|
||||
add("BMP");
|
||||
add("SVG");
|
||||
}};
|
||||
|
||||
private static final List<String> OFFICETYPES = new ArrayList<>() {{
|
||||
add("DOC");
|
||||
add("DOCX");
|
||||
add("XLS");
|
||||
add("XLSX");
|
||||
add("PPT");
|
||||
}};
|
||||
|
||||
private static final String PDFTYPE = "PDF";
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private FtpService ftpService;
|
||||
|
||||
@Inject
|
||||
private SLogService sLogService;
|
||||
|
||||
private static int calculateSize(int srcWidth, int srcHeight) {
|
||||
srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
|
||||
srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
|
||||
int longSide = Math.max(srcWidth, srcHeight);
|
||||
int shortSide = Math.min(srcWidth, srcHeight);
|
||||
float scale = ((float) shortSide / longSide);
|
||||
if (scale <= 1 && scale > 0.5625) {
|
||||
if (longSide < 1664) {
|
||||
return 1;
|
||||
} else if (longSide < 4990) {
|
||||
return 2;
|
||||
} else if (longSide > 4990 && longSide < 10240) {
|
||||
return 4;
|
||||
} else {
|
||||
return longSide / 1280;
|
||||
}
|
||||
} else if (scale <= 0.5625 && scale > 0.5) {
|
||||
return longSide / 1280 == 0 ? 1 : longSide / 1280;
|
||||
} else {
|
||||
return (int) Math.ceil(longSide / (1280.0 / scale));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件流预览
|
||||
*
|
||||
* @param id id
|
||||
* @param response 响应
|
||||
*/
|
||||
@At("/fileStreamPreview")
|
||||
@Ok("void")
|
||||
@RequiresAuthentication
|
||||
public void fileStreamPreview(String id, HttpServletResponse response) {
|
||||
Record sys_file = dao.fetch("sys_file", Cnd.where("id", "=", id).or("filepath", "=", id));
|
||||
if (sys_file != null) {
|
||||
try {
|
||||
String fullFileName = sys_file.getString("filename");
|
||||
String filepath = sys_file.getString("filepath");
|
||||
String suffix = FileUtil.extName(fullFileName).toUpperCase();
|
||||
String prefix = FileUtil.getPrefix(fullFileName);
|
||||
if (IMGTYPES.contains(suffix)) {
|
||||
response.setContentType("image/jpg");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fullFileName.getBytes("utf-8"), "ISO8859-1"));
|
||||
ftpService.download(filepath, response.getOutputStream());
|
||||
} else if (PDFTYPE.equals(suffix)) {
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fullFileName.getBytes("utf-8"), "ISO8859-1"));
|
||||
response.setContentType("application/pdf");
|
||||
ftpService.download(filepath, response.getOutputStream());
|
||||
} else if (OFFICETYPES.contains(suffix)) {
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + new String((prefix + ".pdf").getBytes("utf-8"), "ISO8859-1"));
|
||||
response.setContentType("application/pdf");
|
||||
String pdfPath = generatePDF(filepath, id + ".pdf");
|
||||
FileCopyUtils.copy(new FileInputStream(pdfPath), response.getOutputStream());
|
||||
Files.delete(Path.of(pdfPath));
|
||||
} else {
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fullFileName.getBytes("utf-8"), "ISO8859-1"));
|
||||
ftpService.download(filepath, response.getOutputStream());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("View document exception, document id is [{}]", id, e);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成pdf
|
||||
*
|
||||
* @param sourcePath 源路径
|
||||
* @param targetPath 目标路径
|
||||
* @return {@link String}
|
||||
*/
|
||||
private String generatePDF(String sourcePath, String targetPath) {
|
||||
try {
|
||||
//文件扩展名
|
||||
String extName = FileUtil.extName(sourcePath);
|
||||
//将ftp文件下载到本地的临时文件路径
|
||||
Path localSourcePath = Files.createTempFile(null, "." + extName);
|
||||
FileOutputStream localOutPutStream = new FileOutputStream(localSourcePath.toFile());
|
||||
ftpService.download(sourcePath, localOutPutStream);
|
||||
|
||||
//要转换的pdf本地临时路径
|
||||
Path localPdfPath = Files.createTempFile(null, ".pdf");
|
||||
this.convert(localSourcePath.toFile(), localPdfPath.toFile());
|
||||
|
||||
//删除临时文件(docx,xlsx)
|
||||
Files.deleteIfExists(localSourcePath);
|
||||
return localPdfPath.toString();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void convert(File sourceFile, File targetFile) throws IOException {
|
||||
try {
|
||||
if (sourceFile == null || targetFile == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
if (sourceFile.exists() && sourceFile.length() == 0L) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
String localIp = StrUtil.blankToDefault(conf.get("v.ip"), "127.0.0.1");
|
||||
OpenOfficeConnection connection = new SocketOpenOfficeConnection(localIp, 8100);
|
||||
connection.connect();
|
||||
//此处如果是使用远程的openoffice服务 需要使用StreamOpenOfficeDocumentConverter
|
||||
// DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
|
||||
StreamOpenOfficeDocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection);
|
||||
converter.convert(sourceFile, targetFile);
|
||||
connection.disconnect();
|
||||
} catch (IocException var4) {
|
||||
log.info(var4.getMessage(), var4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @param request 请求
|
||||
* @return {@link Object}
|
||||
*/
|
||||
@At
|
||||
@POST
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
@RequiresAuthentication
|
||||
@Ok("json")
|
||||
public Object uploadFile(TempFile file, Integer source, @Param(value = "folderPath", required = false) String folderPath, HttpServletRequest request) {
|
||||
if (Lang.isEmpty(file)) {
|
||||
return Result.error("文件不能为空");
|
||||
}
|
||||
Sys_user user = (Sys_user) ShiroUtil.getPrincipal();
|
||||
Sys_log sysLog = new Sys_log();
|
||||
sysLog.setType("info");
|
||||
sysLog.setTag("上传文件");
|
||||
sysLog.setSrc(this.getClass().getName() + "#uploadFile");
|
||||
sysLog.setIp(Lang.getIP(request));
|
||||
sysLog.setCreatedBy(user.getId());
|
||||
sysLog.setUsername(user.getUsername());
|
||||
sysLog.setLoginname(user.getLoginname());
|
||||
sysLog.setParam(file.getSubmittedFileName());
|
||||
|
||||
try {
|
||||
Sys_file sysFile = saveFile(file, source, folderPath);
|
||||
sysLog.setMsg("上传成功");
|
||||
sysLog.setResult(Json.toJson(sysFile));
|
||||
//去掉真实路径
|
||||
return Result.success(sysFile);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
sysLog.setMsg("上传失败");
|
||||
sysLog.setResult(e.getMessage());
|
||||
return Result.error("上传失败");
|
||||
} finally {
|
||||
sLogService.async(sysLog);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param id 文件id
|
||||
* @param request 请求
|
||||
*/
|
||||
@At
|
||||
@POST
|
||||
@RequiresAuthentication
|
||||
@Ok("json")
|
||||
public Object deleteFile(String id, HttpServletRequest request) {
|
||||
if (StrUtil.isBlank(id)) {
|
||||
return Result.error("文件id不能为空");
|
||||
}
|
||||
Sys_file sysFile = dao.fetch(Sys_file.class, Cnd.where("id", "=", id).or("filepath", "=", id));
|
||||
if (Lang.isEmpty(sysFile)) {
|
||||
return Result.error("文件不存在");
|
||||
}
|
||||
boolean delete = ftpService.delete(sysFile.getFilepath());
|
||||
Sys_user user = (Sys_user) ShiroUtil.getPrincipal();
|
||||
Sys_log sysLog = new Sys_log();
|
||||
sysLog.setType("info");
|
||||
sysLog.setTag("删除文件");
|
||||
sysLog.setSrc(this.getClass().getName() + "#deleteFile");
|
||||
sysLog.setIp(Lang.getIP(request));
|
||||
sysLog.setCreatedBy(user.getId());
|
||||
sysLog.setUsername(user.getUsername());
|
||||
sysLog.setLoginname(user.getLoginname());
|
||||
sysLog.setParam(id);
|
||||
if (delete) {
|
||||
sysLog.setResult("删除成功");
|
||||
return Result.success("删除成功");
|
||||
} else {
|
||||
sysLog.setResult("删除失败");
|
||||
return Result.error("删除失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param file
|
||||
* @param source
|
||||
* @param folderPath
|
||||
* @return {@link Sys_file}
|
||||
* @throws Exception
|
||||
*/
|
||||
private Sys_file saveFile(TempFile file, Integer source, String folderPath) throws Exception {
|
||||
// 获取当前上传时间
|
||||
LocalDateTime uploadTime = LocalDateTime.now();
|
||||
|
||||
if (StrUtil.isBlank(folderPath)) {
|
||||
// 生成年/月/日的文件夹结构
|
||||
folderPath = String.format("/%04d/%02d/%02d/", uploadTime.getYear(), uploadTime.getMonthValue(), uploadTime.getDayOfMonth());
|
||||
}
|
||||
|
||||
//文件id
|
||||
String fileID = R.UU64();
|
||||
//文件后缀名
|
||||
String fileSuffixName = FilenameUtils.getExtension(file.getSubmittedFileName());
|
||||
//生成的文件路径
|
||||
String generatedFileName = fileID + "." + fileSuffixName;
|
||||
String generatedFilePath = folderPath + generatedFileName;
|
||||
boolean uploadRes;
|
||||
if (IMGTYPES.contains(fileSuffixName.toUpperCase())) {
|
||||
try {
|
||||
BufferedImage bufferedImage = ImageIO.read(file.getFile());
|
||||
int size = calculateSize(bufferedImage.getWidth(), bufferedImage.getHeight());
|
||||
BufferedImage asBufferedImage = Thumbnails.of(bufferedImage).outputFormat("jpg").size(bufferedImage.getWidth() / size, bufferedImage.getHeight() / size).outputQuality(0.6f).asBufferedImage();
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
ImageIO.write(asBufferedImage, fileSuffixName, output);
|
||||
byte[] buff = output.toByteArray();
|
||||
InputStream is = new ByteArrayInputStream(buff);
|
||||
uploadRes = ftpService.upload(folderPath, generatedFileName, is);
|
||||
is.close();
|
||||
} catch (Exception e) {
|
||||
// 压缩失败,记录异常信息
|
||||
log.error("Image compression failed. File: {}, Error: {}", file.getSubmittedFileName(), e.getMessage());
|
||||
//压缩失败上传原图
|
||||
uploadRes = ftpService.upload(folderPath, generatedFileName, file.getInputStream());
|
||||
}
|
||||
} else {
|
||||
uploadRes = ftpService.upload(folderPath, generatedFileName, file.getInputStream());
|
||||
}
|
||||
|
||||
// uploadRes = ftpService.upload(folderPath, generatedFileName, file.getInputStream());
|
||||
|
||||
if (uploadRes) {
|
||||
//上传成功记录到文件表
|
||||
Sys_file sys_file = new Sys_file();
|
||||
sys_file.setFilename(file.getSubmittedFileName());
|
||||
sys_file.setFilepath(generatedFilePath);
|
||||
sys_file.setSource(source);
|
||||
dao.insert(sys_file);
|
||||
return sys_file;
|
||||
} else {
|
||||
throw new RuntimeException("上传文件失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载
|
||||
*
|
||||
* @param id
|
||||
* @param filename
|
||||
* @param response
|
||||
*/
|
||||
@At
|
||||
@Ok("void")
|
||||
@RequiresAuthentication
|
||||
public void download(String id, String filename, HttpServletResponse response) {
|
||||
try {
|
||||
if (StrUtil.isBlank(filename)) {
|
||||
Sys_file sysFile = dao.fetch(Sys_file.class, Cnd.where("id", "=", id).or("filepath", "=", id));
|
||||
if (null == sysFile) {
|
||||
log.error("下载文件不存在:id={}", id);
|
||||
} else {
|
||||
filename = sysFile.getFilename();
|
||||
String filepath = sysFile.getFilepath();
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("utf-8"), "ISO8859-1"));
|
||||
ftpService.download(filepath, response.getOutputStream());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@POST
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
@RequiresAuthentication
|
||||
@Ok("json")
|
||||
public Object word2Html(@Param("file") TempFile tempFile) {
|
||||
try {
|
||||
String submittedFileName = tempFile.getSubmittedFileName().toLowerCase();
|
||||
if (!submittedFileName.endsWith("doc") && !submittedFileName.endsWith("docx")) {
|
||||
return Result.error("请上传doc或docx的文件");
|
||||
}
|
||||
String html = WordUtil.checkConvert2Html(tempFile.getFile());
|
||||
return Result.success().addData(html);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package io.v.nutz.web.commons.controller;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.sys.models.Sys_file;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.nutz.boot.starter.ftp.FtpService;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* @author 赵欣雨
|
||||
* @date 2020/8/15 10:27
|
||||
*/
|
||||
@IocBean
|
||||
@At("/signature")
|
||||
public class SignatureController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private RedisService redisService;
|
||||
|
||||
@Inject
|
||||
private FtpService ftpService;
|
||||
|
||||
@At("")
|
||||
@Ok("re")
|
||||
@RequiresAuthentication
|
||||
public String signature() {
|
||||
return "beetl:/platform/common/signature.html";
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动端提交签名数据存到redis里
|
||||
*
|
||||
* @param prefix 前缀
|
||||
* @param id 唯一id
|
||||
* @param base64
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@Ok("json")
|
||||
@RequiresAuthentication
|
||||
public Object doSub(String prefix, String id, String base64, Boolean is_value_base64) {
|
||||
if (is_value_base64) {
|
||||
redisService.setex(prefix + ":" + id, 60 * 10, base64);
|
||||
} else {
|
||||
String today = DateUtil.today();
|
||||
String[] split = today.split("-");
|
||||
String todayPath = "/signature/" + split[0] + "/" + split[1] + "/" + split[2];
|
||||
String fileName = R.UU32() + ".png";
|
||||
String fullPath = todayPath + "/" + fileName;
|
||||
|
||||
String replaceData = base64.replaceAll("data:image/png;base64,", "");
|
||||
|
||||
try (InputStream is = new ByteArrayInputStream(Base64.decode(replaceData))) {
|
||||
ftpService.upload(todayPath, fileName, is);
|
||||
redisService.setex(prefix + ":" + id, 60 * 10, fullPath);
|
||||
} catch (Exception e) {
|
||||
return Result.error("签字数据上传失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取base64
|
||||
*
|
||||
* @param prefix 前缀
|
||||
* @param id 唯一id
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@Ok("json")
|
||||
@RequiresAuthentication
|
||||
public Object getBase64(String prefix, String id) {
|
||||
return redisService.get(prefix + ":" + id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 签字base64转图片链接
|
||||
*
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@Ok("json")
|
||||
@RequiresAuthentication
|
||||
public Result uploadSignData(String data) {
|
||||
String today = DateUtil.today();
|
||||
String[] split = today.split("-");
|
||||
String todayPath = "/signature/" + split[0] + "/" + split[1] + "/" + split[2];
|
||||
String fileName = R.UU32() + ".png";
|
||||
String fullPath = todayPath + "/" + fileName;
|
||||
|
||||
String replaceData = data.replaceAll("data:image/png;base64,", "");
|
||||
|
||||
try (InputStream is = new ByteArrayInputStream(Base64.decode(replaceData))) {
|
||||
boolean uploadRes = ftpService.upload(todayPath, fileName, is);
|
||||
if (uploadRes) {
|
||||
//上传成功记录到文件表
|
||||
Sys_file sys_file = new Sys_file();
|
||||
sys_file.setFilename(fileName);
|
||||
sys_file.setFilepath(fullPath);
|
||||
sys_file.setSource(null);
|
||||
dao.insert(sys_file);
|
||||
} else {
|
||||
throw new RuntimeException("上传文件失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error("签字数据上传失败");
|
||||
}
|
||||
|
||||
return Result.success().addData(fullPath);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@RequiresAuthentication
|
||||
public void getSignData(String path, HttpServletResponse response) {
|
||||
try {
|
||||
response.setContentType("image/jpg");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=1.png");
|
||||
ftpService.download(path, response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,823 @@
|
||||
package io.v.nutz.web.commons.controller;
|
||||
|
||||
import cn.hutool.core.lang.ClassScanner;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.base.annontation.SelectEnum;
|
||||
import io.v.nutz.base.annontation.ViReturn;
|
||||
import io.v.nutz.base.enums.AuditTypeEnum;
|
||||
import io.v.nutz.base.service.BaseService;
|
||||
import io.v.nutz.base.utils.Roles;
|
||||
import io.v.nutz.base.utils.Vi;
|
||||
import io.v.nutz.base.service.ViService;
|
||||
import io.v.nutz.base.utils.ViResource;
|
||||
import io.v.nutz.sys.models.Sys_union;
|
||||
import io.v.nutz.sys.models.Sys_unit;
|
||||
import io.v.nutz.sys.services.*;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import io.v.nutz.zhgh.activity.models.ActivityBasicUnit;
|
||||
import io.v.nutz.zhgh.jdh.model.zzjg.Jdh_dbt;
|
||||
import io.v.nutz.sys.models.SysUnionGroup;
|
||||
import io.v.nutz.sys.models.Sys_user_role;
|
||||
import io.v.nutz.zhgh.jdh.model.zzjg.Jdh_dbt_zcdw;
|
||||
import io.v.nutz.zhgh.workersCongress.model.workersCongressSession;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.shiro.authz.annotation.RequiresAuthentication;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.entity.Record;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.dao.util.cri.Static;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @Author: V
|
||||
* @DateTime: 2020/8/14 8:45
|
||||
* @Description: ...
|
||||
*/
|
||||
@IocBean
|
||||
@At("/platform/vi/common")
|
||||
@Ok("json:full")
|
||||
@Slf4j
|
||||
public class ViCommonCon {
|
||||
|
||||
public static final String DICT_JC_ID = "d5ef321774874cdbbfca9c6b8e3403f6"; //届次
|
||||
public static final String DICT_JCGBJS_ID = "66145d71bb7d4bb38ead4115d40be220"; //基层干部
|
||||
public static final String DICT_CLUBTYPE_ID = "1f0e6322b28d4b718ff07bd65876fadc"; //协会类型
|
||||
public static final String DICT_SPYJS_ID = "ee99a74af42e4fe88ab7b1b414f792c1"; //审批员
|
||||
public static final String DICT_UNION_ID = "25dd6e291d6442e8ab475c603ae7f16d"; //工会
|
||||
public static final String DICT_CLUB_ID = "c8708f460bcc4fb6951cb3d613cb0296"; //协会
|
||||
public static final String DICT_SYSTEM_ID = "9abd06610cdb4f7b886fcd4e8c88b5ce"; //系统名称
|
||||
public static final String DICT_FILETYPE_ID = "3e99034eb1814891ba4ca7c7be429067"; //文件类型
|
||||
|
||||
private static String FGH_ADMIN_ROLE_ID = "5d342e614e9a48288d50154bcdcc75d3";
|
||||
|
||||
@Inject
|
||||
private SysDictService sysDictService;
|
||||
|
||||
@Inject
|
||||
private SysWelfareUnitService sysWelfareUnitService;
|
||||
|
||||
@Inject
|
||||
private SysUnitService sysUnitService;
|
||||
|
||||
@Inject
|
||||
private SysUnionService sysUnionService;
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
|
||||
@Inject
|
||||
private SysClubService sysClubService;
|
||||
|
||||
@Inject
|
||||
private SysDqService sysDqService;
|
||||
|
||||
@Inject("Jdh_dbt")
|
||||
private ViService<Jdh_dbt> dbtService;
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
|
||||
|
||||
/**
|
||||
* 将enum的转成list供前台select调用
|
||||
* 需带 SelectEnum 注解
|
||||
*
|
||||
* @param enumName
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
@RequiresAuthentication
|
||||
public Object enumOptions(String enumName) {
|
||||
Set<Class<?>> selectEnumClasses = ClassScanner.scanPackageByAnnotation("io.v.nutz", SelectEnum.class);
|
||||
Iterator<Class<?>> selectEnumClassesIterator = selectEnumClasses.iterator();
|
||||
|
||||
if (ViResource.selectEnums.containsKey(enumName)) {
|
||||
return ViResource.selectEnums.get(enumName);
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查用户
|
||||
*
|
||||
* @param ids
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object queryUserByIds(String[] ids) {
|
||||
Sql sql = Sqls.create("select id,loginname,username,unitname,unionname,sex from user where id in (@id)");
|
||||
sql.setParam("id", ids);
|
||||
return sysUserService.listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据id查用户
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object queryUserById(String id) {
|
||||
Sql sql = Sqls.create("""
|
||||
select
|
||||
id,
|
||||
loginname,
|
||||
username,
|
||||
unitname,
|
||||
unionname,
|
||||
sex,
|
||||
userState,
|
||||
political,
|
||||
unitid,
|
||||
unionid,
|
||||
birthday,
|
||||
jobTitle
|
||||
position
|
||||
from
|
||||
user
|
||||
where id = @id
|
||||
""");
|
||||
sql.setParam("id", id);
|
||||
return sysUserService.listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取校区
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getCampus() {
|
||||
return sysUnitService.list(Sqls.create("""
|
||||
SELECT
|
||||
dq_id campus_id,
|
||||
dq_name campus_name\s
|
||||
FROM
|
||||
sys_dq
|
||||
"""));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code获取字典选项
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object dictOptions(String code) {
|
||||
return sysDictService.getSubListByCode(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getSystemNameByDict() {
|
||||
return sysDictService.query(Cnd.where("parentId", "=", DICT_SYSTEM_ID).and("disabled", "=", false).asc("location"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取届次信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getJcByDict() {
|
||||
return sysDictService.query(Cnd.where("parentId", "=", DICT_JC_ID).and("disabled", "=", false).asc("location"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getFileTypeByDict() {
|
||||
return sysDictService.query(Cnd.where("parentId", "=", DICT_FILETYPE_ID).and("disabled", "=", false).asc("location"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取基层干部角色信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getJcgbJsByDict() {
|
||||
return sysDictService.list(Sqls.create("SELECT\n" +
|
||||
"\tdict.id,\n" +
|
||||
"\tdict.`name`,\n" +
|
||||
"\tdict.`code`,\n" +
|
||||
"\trole.id roleid \n" +
|
||||
"FROM\n" +
|
||||
"\t`sys_dict` dict\n" +
|
||||
"\tLEFT JOIN sys_role role ON dict.`code` = role.`code` \n" +
|
||||
"WHERE\n" +
|
||||
"\tdict.parentId = @pid \n" +
|
||||
"\tAND dict.disabled = FALSE \n" +
|
||||
"ORDER BY\n" +
|
||||
"\tdict.location ASC").setParam("pid", DICT_JCGBJS_ID));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取审批员角色信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getSpyJsByDict() {
|
||||
return sysDictService.list(Sqls.create("SELECT\n" +
|
||||
"\tdict.id,\n" +
|
||||
"\tdict.`name`,\n" +
|
||||
"\tdict.`code`,\n" +
|
||||
"\trole.id roleid \n" +
|
||||
"FROM\n" +
|
||||
"\t`sys_dict` dict\n" +
|
||||
"\tLEFT JOIN sys_role role ON dict.`code` = role.`code` \n" +
|
||||
"WHERE\n" +
|
||||
"\tdict.parentId = @pid \n" +
|
||||
"\tAND dict.disabled = FALSE \n" +
|
||||
"ORDER BY\n" +
|
||||
"\tdict.location ASC").setParam("pid", DICT_SPYJS_ID));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 所有福利单位
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object welfareUnits() {
|
||||
return sysWelfareUnitService.query(Cnd.orderBy().asc("welfare_unit_code"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取福利单位的二级单位
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object unitsByWelfareUnit(@Param(value = "welfareUnitId", required = false) String welfareUnitId) {
|
||||
if (Strings.isBlank(welfareUnitId)) {
|
||||
return sysUnitService.query(Cnd.orderBy().asc("id"));
|
||||
}
|
||||
return sysUnitService.query(Cnd.where("welfare_unit_id", "=", welfareUnitId).asc("id"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代表团
|
||||
*
|
||||
* @param jdhId
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getDbt(String jdhId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
dbt.*
|
||||
FROM
|
||||
jdh_dbt dbt
|
||||
LEFT JOIN sys_dict dict ON dict.`name` = dbt.dbtname
|
||||
WHERE
|
||||
dbt.jdhid = @jdhid
|
||||
ORDER BY
|
||||
dbt.`code`
|
||||
""").setParam("jdhid", jdhId);
|
||||
return dbtService.list(sql);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 工会
|
||||
*
|
||||
* @param delegationId 代表团id
|
||||
* @param isPermission 是否按权限查询
|
||||
* @return 获取院级工会
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object unions(@Param(value = "delegationId", required = false) String delegationId, boolean isPermission) {
|
||||
if (Strings.isBlank(delegationId)) {
|
||||
Cnd cndX = Cnd.NEW();
|
||||
if (isPermission) {
|
||||
cndX.and("id", "=", Vi.getUnionId());
|
||||
}
|
||||
cndX.asc("unioncode");
|
||||
return sysUnionService.query(cndX);
|
||||
}
|
||||
List<Jdh_dbt_zcdw> zcdws = dao.query(Jdh_dbt_zcdw.class, Cnd.where("dbtid", "=", delegationId));
|
||||
List<String> unitIds = zcdws.stream().map(Jdh_dbt_zcdw::getDwid).collect(Collectors.toList());
|
||||
List<Sys_unit> units = dao.query(Sys_unit.class, Cnd.where("id", "in", unitIds));
|
||||
Set<String> unionIds = units.stream().map(v -> v.getUnionid()).collect(Collectors.toSet());
|
||||
List<Sys_union> unions = dao.query(Sys_union.class, Cnd.where("id", "in", unionIds));
|
||||
return unions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取基层工会
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object unionList(@Param(value = "unionId", required = false) String unionId) {
|
||||
if (Strings.isBlank(unionId)) {
|
||||
return sysUnionService.query(Cnd.orderBy().asc("unioncode"));
|
||||
}
|
||||
Cnd cnd = Cnd.NEW();
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
dbt.id dbtid,
|
||||
dbt.dbtname,
|
||||
uni.*
|
||||
FROM
|
||||
sys_union uni
|
||||
LEFT JOIN jdh_dbt_zcfgh zc ON zc.fghid = uni.id
|
||||
LEFT JOIN jdh_dbt dbt ON zc.dbtid = dbt.id $condition
|
||||
""");
|
||||
if (!ShiroUtil.hasAnyRoles("sysadmin")) {
|
||||
cnd.and("uni.id", "=", unionId);
|
||||
}
|
||||
cnd.groupBy("uni.id");
|
||||
cnd.asc("uni.unioncode");
|
||||
|
||||
sql.setCondition(cnd);
|
||||
return sysUnionService.listEntity(sql);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 获取协会
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object clubs() {
|
||||
return sysClubService.query(Cnd.where("state", "=", 930).asc("code"));
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getGroup(@Param(value = "jdhId", required = false) String jdhId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
g.*
|
||||
FROM
|
||||
jdh_group g
|
||||
LEFT JOIN sys_dict dict ON dict.`name` = g.groupName
|
||||
WHERE
|
||||
g.jdhId = @jdhid
|
||||
ORDER BY
|
||||
dict.`code`
|
||||
""").setParam("jdhid", jdhId);
|
||||
return dbtService.listMap(sql);
|
||||
}
|
||||
|
||||
@At
|
||||
public Object getUnit(String unionid) {
|
||||
try {
|
||||
if (Strings.isBlank(unionid)) {
|
||||
return Result.success().addData(baseService.dao().query("unit_union", Cnd.where("unitlevel", "=", 2)));
|
||||
}
|
||||
List<Record> unitList = baseService.dao().query("unit_union", Cnd.where("unionid", "=", unionid));
|
||||
return Result.success().addData(unitList);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前人所管理的协会
|
||||
*
|
||||
* @return {@link Object}
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getClubsByRole() {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
club.id as stid,
|
||||
role.userId,
|
||||
club.`name`,
|
||||
club.foundTime,
|
||||
club.dues_standard,
|
||||
club.`code`
|
||||
FROM
|
||||
sys_club club
|
||||
LEFT JOIN `sys_user_role` role ON club.id = role.stid
|
||||
$condition
|
||||
""");
|
||||
if (!ShiroUtil.hasRole("sysadmin")) {
|
||||
cnd.and("role.roleId", "in", Lang.array(Roles.club01, Roles.club02, Roles.club03, Roles.club04));
|
||||
cnd.and("role.userId", "=", ShiroUtil.getUserId());
|
||||
}
|
||||
cnd.and("club.isjs", "=", false);
|
||||
cnd.and("club.state", "=", 930);
|
||||
cnd.groupBy("club.id");
|
||||
cnd.asc("club.`code`");
|
||||
sql.setCondition(cnd);
|
||||
return sysUserService.listMap(sql);
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getClubsByUser() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
id as clubid,
|
||||
name as clubName
|
||||
FROM
|
||||
sys_club
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (!ShiroUtil.hasAnyRoles("sysadmin")) {
|
||||
cnd.and(new Static(" id in (select clubid from sys_club_user where userid = '%s')"
|
||||
.formatted(ShiroUtil.getPrincipalProperty("id"))));
|
||||
}
|
||||
cnd.and("state", "=", 930);
|
||||
sql.setCondition(cnd);
|
||||
List list = baseService.listMap(sql);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协会类型
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getClubTypeByDict() {
|
||||
return sysDictService.query(Cnd.where("parentId", "=", DICT_CLUBTYPE_ID).and("disabled", "=", false).asc("location"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 获取单位
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object units(@Param(value = "unionId", required = false) String unionId) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("unitlevel", "=", 2);
|
||||
cnd.andEX("unionId", "=", unionId);
|
||||
if (ShiroUtil.hasAnyRoles("sysadmin,SchoolUnionAdmin")) {
|
||||
|
||||
} else if (ShiroUtil.hasRole("H04")) {
|
||||
cnd.and("unionid", "=", Vi.getUnionId());
|
||||
} else if (ShiroUtil.hasRole("ghxzzz")) {
|
||||
cnd.and("id", "=", Vi.getUnit().getId());
|
||||
}
|
||||
cnd.asc("unitcode");
|
||||
|
||||
if (Strings.isBlank(unionId)) {
|
||||
return sysUnitService.dao().query("unit_union", cnd);
|
||||
}
|
||||
return sysUnitService.dao().query("unit_union", cnd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 根据多个工会获取单位
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getUnitsByUnions(@Param(value = "unionId", required = false) String[] unionId) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("unitlevel", "=", 2);
|
||||
cnd.andEX("unionId", "in", unionId);
|
||||
if (ShiroUtil.hasAnyRoles("sysadmin,SchoolUnionAdmin")) {
|
||||
|
||||
} else if (ShiroUtil.hasRole("H04")) {
|
||||
cnd.and("unionid", "=", Vi.getUnionId());
|
||||
} else if (ShiroUtil.hasRole("ghxzzz")) {
|
||||
cnd.and("id", "=", Vi.getUnit().getId());
|
||||
}
|
||||
cnd.asc("unitcode");
|
||||
|
||||
if (unionId != null && unionId.length > 0) {
|
||||
return sysUnitService.dao().query("unit_union", cnd);
|
||||
}
|
||||
return sysUnitService.dao().query("unit_union", cnd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 获取组成科室by二级单位id or工会小组id
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object threeUnitsByUnionGroupOrUnitId(@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "groupId", required = false) String groupId) {
|
||||
if (Strings.isNotBlank(groupId)) {
|
||||
return sysUnitService.dao().query("unit_union", Cnd.where("unionGroupId", "=", groupId).and("unitlevel", "=", 3).asc("id"));
|
||||
}
|
||||
if (Strings.isNotBlank(unitId)) {
|
||||
return sysUnitService.dao().query("unit_union", Cnd.where("parentId", "=", unitId).and("unitlevel", "=", 3).asc("id"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 获取组成科室by多个二级单位id or多个工会小组id
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getThreeUnitsByUnionGroupsOrUnitIds(@Param(value = "unitId", required = false) String[] unitId,
|
||||
@Param(value = "groupId", required = false) String[] groupId) {
|
||||
if (groupId != null && groupId.length > 0) {
|
||||
return sysUnitService.dao().query("unit_union", Cnd.where("unionGroupId", "in", groupId).and("unitlevel", "=", 3).asc("id"));
|
||||
}
|
||||
if (unitId != null && unitId.length > 0) {
|
||||
return sysUnitService.dao().query("unit_union", Cnd.where("parentId", "in", unitId).and("unitlevel", "=", 3).asc("id"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 根据工会id查工会小组
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object unionGroupsByUnionId(@Param(value = "unionId", required = false) String unionId) {
|
||||
if (!ShiroUtil.hasAnyRoles("sysadmin,SchoolUnionAdmin,flwyh01,H04")) {
|
||||
List<Sys_user_role> userRole = sysUserService.dao().query(Sys_user_role.class, Cnd.where("roleId", "=", Roles.ghxzzz)
|
||||
.and("userId", "=", ShiroUtil.getUserId()));
|
||||
List<String> ids = userRole.stream().map(Sys_user_role::getUnionGroupId).collect(Collectors.toList());
|
||||
if (Lang.isEmpty(ids)) {
|
||||
return null;
|
||||
}
|
||||
return sysUnitService.dao().query(SysUnionGroup.class, Cnd.where("id", "in", ids).asc("groupCode"));
|
||||
}
|
||||
if (Strings.isNotBlank(unionId)) {
|
||||
return sysUnitService.dao().query(SysUnionGroup.class, Cnd.where("unionId", "=", unionId).asc("groupCode"));
|
||||
}
|
||||
return sysUnitService.dao().query(SysUnionGroup.class, Cnd.NEW().asc("groupCode"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return 根据多个工会id查工会小组
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getUnionGroupsByUnions(@Param(value = "unionId", required = false) String[] unionId) {
|
||||
if (!ShiroUtil.hasAnyRoles("sysadmin,SchoolUnionAdmin,flwyh01,H04")) {
|
||||
List<Sys_user_role> userRole = sysUserService.dao().query(Sys_user_role.class, Cnd.where("roleId", "=", Roles.ghxzzz)
|
||||
.and("userId", "=", ShiroUtil.getUserId()));
|
||||
List<String> ids = userRole.stream().map(Sys_user_role::getUnionGroupId).collect(Collectors.toList());
|
||||
if (Lang.isEmpty(ids)) {
|
||||
return null;
|
||||
}
|
||||
return sysUnitService.dao().query(SysUnionGroup.class, Cnd.where("id", "in", ids).asc("groupCode"));
|
||||
}
|
||||
if (unionId != null && unionId.length > 0) {
|
||||
return sysUnitService.dao().query(SysUnionGroup.class, Cnd.where("unionId", "in", unionId).asc("groupCode"));
|
||||
}
|
||||
return sysUnitService.dao().query(SysUnionGroup.class, Cnd.NEW().asc("groupCode"));
|
||||
}
|
||||
|
||||
|
||||
//协会负责人登录获取成员所在的单位
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getClubUnits() {
|
||||
Sql sql = Sqls.create("""
|
||||
select
|
||||
*
|
||||
from
|
||||
sys_unit
|
||||
where
|
||||
id in (select u.unitid from sys_club_user c left join `user` u on c.userid=u.id where clubid in (select stid from sys_user_role where userId = @userId and roleId = @roleId) group by u.unitid)
|
||||
""").setParam("userId", ShiroUtil.getPrincipalProperty("id")).setParam("roleId", Roles.club01);
|
||||
List<NutMap> nutMaps = sysUnitService.listMap(sql);
|
||||
return nutMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 获取单位
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object unionUnits() {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("unitlevel", "=", 2);
|
||||
if (ShiroUtil.hasAnyRoles("sysadmin,SchoolUnionAdmin")) {
|
||||
|
||||
} else if (ShiroUtil.hasRole("H04")) {
|
||||
cnd.and("unionid", "=", Vi.getUnionId());
|
||||
} else if (ShiroUtil.hasRole("ghxzzz")) {
|
||||
cnd.and("id", "=", Vi.getUnit().getId());
|
||||
}
|
||||
cnd.asc("unitcode");
|
||||
return sysUnitService.dao().query("unit_union", cnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询用户
|
||||
*
|
||||
* @param query
|
||||
* @param sex
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object searchUser(@Param(value = "query", required = false) String query,
|
||||
@Param(value = "unionId", required = false) String unionId,
|
||||
@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "sex", required = false) String sex,
|
||||
@Param(value = "member", required = false) Boolean member) {
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.loginname,
|
||||
u.mobile,
|
||||
u.email,
|
||||
u.sex,
|
||||
unit.`id` unitid,
|
||||
unit.`name` unitname,
|
||||
un.unionname
|
||||
FROM
|
||||
sys_user u
|
||||
LEFT JOIN sys_unit unit ON u.unitid = unit.id
|
||||
LEFT JOIN sys_union un ON unit.unionid = un.id
|
||||
$condition
|
||||
""");
|
||||
|
||||
Cnd cnd = Cnd.NEW();
|
||||
|
||||
if (Strings.isNotBlank(query)) {
|
||||
SqlExpressionGroup group = new SqlExpressionGroup();
|
||||
group.orLike("u.username", query);
|
||||
group.orLike("u.loginname", query);
|
||||
cnd.and(group);
|
||||
}
|
||||
|
||||
cnd.andEX("unit.unionid", "=", unionId);
|
||||
cnd.andEX("u.unitid", "=", unitId);
|
||||
cnd.andEX("u.sex", "=", sex);
|
||||
if (member) {
|
||||
cnd.and("u.member", "=", member);
|
||||
}
|
||||
sql.setCondition(cnd);
|
||||
return sysUserService.listPage(1, 50, sql);
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取工代会
|
||||
*
|
||||
* @param open
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getGdh(Boolean open) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("startState", "=", open).desc("year");
|
||||
List<workersCongressSession> query = dao.query(workersCongressSession.class, cnd);
|
||||
return Result.success(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据code查看字典选项信息
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object dictState(String code) {
|
||||
return baseService.dao().fetch("sys_dict", Cnd.where("code", "=", code));
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getActivityUnions() {
|
||||
return sysUnionService.query();
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getActivityUnits(@Param(value = "unionId", required = false) String unionId) {
|
||||
if (Strings.isBlank(unionId)) {
|
||||
return sysUnitService.dao().query(ActivityBasicUnit.class, Cnd.where("unitlevel", "=", 2).asc("id"));
|
||||
}
|
||||
return sysUnitService.dao().query(ActivityBasicUnit.class, Cnd.where("unionid", "=", unionId).and("unitlevel", "=", 2).asc("id"));
|
||||
}
|
||||
|
||||
@At
|
||||
public Object getUnion() {
|
||||
try {
|
||||
List<Record> unionList;
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (ShiroUtil.hasRole("sysadmin") || ShiroUtil.hasRole("SchoolUnionAdmin")) {
|
||||
unionList = baseService.dao().query("sys_union", cnd);
|
||||
} else if (ShiroUtil.hasRole("H04")) {
|
||||
cnd.and(new Static("id in (select unionid from sys_user_role where roleid = '" + FGH_ADMIN_ROLE_ID + "' and userid = '" + ShiroUtil.getPrincipalProperty("id") + "')"));
|
||||
unionList = baseService.dao().query("sys_union", cnd);
|
||||
} else {
|
||||
return Result.success().addData(new ArrayList<Record>());
|
||||
}
|
||||
|
||||
return Result.success().addData(unionList);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@Ok("json:full")
|
||||
@RequiresAuthentication
|
||||
public Object getUnionLimit(@Param(value = "activityScopeId", required = false) String activityScopeId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
gh.id,
|
||||
gh.unionname,
|
||||
gh.unioncode,
|
||||
(select count(1) from `user` where unionid = gh.id $cnd) as teacherCount,
|
||||
NULL as ratio,
|
||||
NULL as limitCount
|
||||
FROM
|
||||
sys_union gh
|
||||
order by gh.unioncode
|
||||
""");
|
||||
if (StrUtil.isNotBlank(activityScopeId)) {
|
||||
sql.setVar("cnd", "AND id in (select userId from activity_user_scope where groupId = '" + activityScopeId + "')");
|
||||
}
|
||||
return baseService.listMap(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载模板文件
|
||||
*
|
||||
* @param filePath
|
||||
* @param fileName
|
||||
* @param response
|
||||
*/
|
||||
@At(value = "/platform/basics/downloadTemplate", top = true)
|
||||
@Ok("void")
|
||||
public void downloadTemplate(String filePath, String fileName, HttpServletResponse response) {
|
||||
String TEMPLATE_PATH = "templates";
|
||||
try {
|
||||
response.setHeader("Content-Disposition", "attachment;filename="
|
||||
.concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
|
||||
|
||||
if (filePath.startsWith("/")) {
|
||||
filePath = filePath.substring(1);
|
||||
}
|
||||
if (filePath.contains(".")) {
|
||||
// return;
|
||||
response.sendRedirect("/platform/home/403");
|
||||
}
|
||||
if("club/addClubUser".equals(filePath)) {
|
||||
filePath += ".xlsx";
|
||||
}
|
||||
InputStream fin = Thread.currentThread().getContextClassLoader().getResourceAsStream(TEMPLATE_PATH + "/" + filePath);
|
||||
assert fin != null;
|
||||
IOUtils.copy(fin, response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package io.v.nutz.web.commons.controller.userFilter.constant;
|
||||
|
||||
import io.v.nutz.base.annontation.SelectEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@SelectEnum
|
||||
public enum userFilterEnum {
|
||||
|
||||
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
package io.v.nutz.web.commons.controller.userFilter.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.base.annontation.ViReturn;
|
||||
import io.v.nutz.zhgh.data.model.MatchCondition;
|
||||
import io.v.nutz.zhgh.data.model.MatchConditionStructure;
|
||||
import io.v.nutz.base.query.PageForm;
|
||||
import io.v.nutz.web.commons.controller.userFilter.service.UserFilterService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.POST;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户高级查询
|
||||
*/
|
||||
@IocBean
|
||||
@At("/platform/userFilter")
|
||||
@Slf4j
|
||||
public class userFilterController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private UserFilterService userFilterService;
|
||||
|
||||
@At
|
||||
@POST
|
||||
@Ok("json:full")
|
||||
@ViReturn
|
||||
public Object findUserByCnd(PageForm pageForm, @Param("cnd") String json_conditionStructure) {
|
||||
MatchConditionStructure conditionStructure = null;
|
||||
try {
|
||||
conditionStructure = Json.fromJson(MatchConditionStructure.class, json_conditionStructure);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return Result.error("参数错误");
|
||||
}
|
||||
if (Lang.isEmpty(conditionStructure)) {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
List<MatchCondition> conditionList = conditionStructure.getConditions();
|
||||
|
||||
boolean hasNullField = conditionList.stream().anyMatch(v -> StrUtil.isBlank(v.getField()));
|
||||
if (hasNullField) {
|
||||
return Result.error("请把数据填完整再查询");
|
||||
}
|
||||
|
||||
if ((conditionList.size() == 1)) {
|
||||
boolean b = conditionList.stream().allMatch(v -> StrUtil.isBlank(v.getField())
|
||||
|| StrUtil.isBlank(v.getValue()) || v.getOperational().getValue() == null);
|
||||
if (b) {
|
||||
return Result.error("请选择查询条件");
|
||||
}
|
||||
}
|
||||
|
||||
if (pageForm.getPageNumber() == null) {
|
||||
return userFilterService.findUserByCnd(conditionStructure);
|
||||
} else {
|
||||
return userFilterService.findUserByCnd(pageForm, conditionStructure);
|
||||
}
|
||||
// return null;
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package io.v.nutz.web.commons.controller.userFilter.service;
|
||||
|
||||
import cn.wizzer.framework.page.Pagination;
|
||||
import io.v.nutz.zhgh.data.model.MatchConditionStructure;
|
||||
import io.v.nutz.base.query.PageForm;
|
||||
import io.v.nutz.base.service.ViService;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserFilterService extends ViService {
|
||||
|
||||
Pagination findUserByCnd(PageForm pageForm, MatchConditionStructure matchConditionStructure);
|
||||
|
||||
List<NutMap> findUserByCnd(MatchConditionStructure matchConditionStructure);
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
package io.v.nutz.web.commons.controller.userFilter.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.page.Pagination;
|
||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||
import io.v.nutz.zhgh.data.constant.MatchMethod;
|
||||
import io.v.nutz.zhgh.data.model.MatchCondition;
|
||||
import io.v.nutz.zhgh.data.model.MatchConditionStructure;
|
||||
import io.v.nutz.base.query.PageForm;
|
||||
import io.v.nutz.web.commons.controller.userFilter.service.UserFilterService;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class UserFilterServiceImpl extends ViServiceImpl implements UserFilterService {
|
||||
public UserFilterServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造执行sql语句
|
||||
*
|
||||
* @param matchConditionStructure
|
||||
* @return
|
||||
*/
|
||||
private Sql generateSql(MatchConditionStructure matchConditionStructure) {
|
||||
MatchMethod method = matchConditionStructure.getMethod();
|
||||
List<MatchCondition> conditionList = matchConditionStructure.getConditions();
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
u.id,
|
||||
u.username,
|
||||
u.loginname,
|
||||
u.sex,
|
||||
u.unitname,
|
||||
u.unionname,
|
||||
sur.roleid,
|
||||
sur.jdhid,
|
||||
sur.dbtid
|
||||
FROM
|
||||
sys_user_role sur
|
||||
RIGHT JOIN `user` u ON u.id = sur.userId
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
cnd.and("1", "=", 1);
|
||||
SqlExpressionGroup seg = new SqlExpressionGroup();
|
||||
// seg.and("1", "=", 1);
|
||||
for (MatchCondition condition : conditionList) {
|
||||
if (StrUtil.isNotBlank(condition.getField())) {
|
||||
// String value = ConditionalOperational.EQ.getValue();
|
||||
String value = condition.getOperational().getValue();
|
||||
if (method.equals(MatchMethod.AND)) {
|
||||
seg.andEX(condition.getField(), value, condition.getValue());
|
||||
} else {
|
||||
seg.orEX(condition.getField(), value, condition.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
cnd.groupBy("u.id");
|
||||
cnd.desc("u.unitid").desc("u.unionid").desc("u.sex");
|
||||
cnd.and(seg);
|
||||
sql.setCondition(cnd);
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination findUserByCnd(PageForm pageForm, MatchConditionStructure matchConditionStructure) {
|
||||
return list(pageForm, generateSql(matchConditionStructure));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> findUserByCnd(MatchConditionStructure matchConditionStructure) {
|
||||
return list(generateSql(matchConditionStructure));
|
||||
}
|
||||
}
|
||||
+481
@@ -0,0 +1,481 @@
|
||||
package io.v.nutz.web.commons.controller.userpartupdate.controller;
|
||||
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.v.nutz.web.commons.controller.userpartupdate.models.UserPartUp;
|
||||
import io.v.nutz.web.commons.controller.userpartupdate.service.UserPatUpService;
|
||||
import io.v.nutz.zhgh.activity.models.ActivityUserCnd;
|
||||
import io.v.nutz.base.annontation.ViReturn;
|
||||
import io.v.nutz.base.service.BaseService;
|
||||
import io.v.nutz.base.query.PageForm;
|
||||
import io.v.nutz.sys.models.User;
|
||||
import io.v.nutz.base.utils.ViTool;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.dao.util.cri.SqlExpressionGroup;
|
||||
import org.nutz.dao.util.cri.Static;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.annotation.POST;
|
||||
import org.nutz.mvc.annotation.Param;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@At("/platform/user/part/update")
|
||||
@Ok("json:full")
|
||||
@IocBean
|
||||
public class UserPartUpdateController {
|
||||
|
||||
@Inject
|
||||
private Dao dao;
|
||||
|
||||
@Inject
|
||||
private BaseService baseService;
|
||||
|
||||
@Inject
|
||||
private UserPatUpService userPartUpService;
|
||||
|
||||
|
||||
/**
|
||||
* 查询可以设置活动的人员
|
||||
*
|
||||
* @param pageForm 页面形式
|
||||
* @param unionId 工会id
|
||||
* @param unitId 单位id
|
||||
* @param personTypes 人类型
|
||||
* @param userStates 用户状态
|
||||
* @param memberTypes 成员类型
|
||||
* @param sexTypes 性类型
|
||||
* @param age 年龄
|
||||
* @param teacherMeetingId 教师会议id
|
||||
* @param roleIds 角色id
|
||||
* @param userId 用户id
|
||||
* @param clubId 俱乐部id
|
||||
* @param reverseSelection 逆向选择
|
||||
* @return {@link Object}
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
@POST
|
||||
public Object userPartData(PageForm pageForm,
|
||||
@Param(value = "unionId", required = false) String unionId,
|
||||
@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "personTypes", required = false) String[] personTypes,
|
||||
@Param(value = "userStates", required = false) String[] userStates,
|
||||
@Param(value = "memberTypes", required = false) String[] memberTypes,
|
||||
@Param(value = "sexTypes", required = false) String[] sexTypes,
|
||||
@Param(value = "age", required = false) String[] age,
|
||||
@Param(value = "teacherMeetingId", required = false) String teacherMeetingId,
|
||||
@Param(value = "roleIds", required = false) String[] roleIds,
|
||||
@Param(value = "userId", required = false) String[] userId,
|
||||
@Param(value = "clubId", required = false) String clubId,
|
||||
@Param(value = "reverseSelection") boolean reverseSelection,
|
||||
@Param(value = "activityGroupId", required = false) Integer activityGroupId,
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
DISTINCT(u.id) as id,
|
||||
u.loginname AS loginName,
|
||||
u.username AS userName,
|
||||
u.sex,
|
||||
u.birthday,
|
||||
u.mobile,
|
||||
u.personType,
|
||||
u.userState,
|
||||
u.unitname AS unitName,
|
||||
u.unionname AS unionName,
|
||||
TIMESTAMPDIFF(YEAR, u.birthday, CURDATE()) AS age
|
||||
FROM
|
||||
`user` u
|
||||
LEFT JOIN sys_user_role sur on sur.userid = u.id
|
||||
LEFT JOIN sys_club_user clubuser ON clubuser.userid=u.id
|
||||
$condition
|
||||
""");
|
||||
try {
|
||||
Cnd cnd = getCnd(pageForm, unionId, unitId, personTypes, userStates, memberTypes, sexTypes, age, teacherMeetingId, roleIds, userId, clubId, reverseSelection, activityGroupId, activityUserCndStr);
|
||||
sql.setCondition(cnd);
|
||||
return userPartUpService.list(pageForm, sql);
|
||||
} catch (NumberFormatException e) {
|
||||
return cn.wizzer.framework.base.Result.error(e.getMessage() + "请输入数字类型的值");
|
||||
} catch (Exception e) {
|
||||
return cn.wizzer.framework.base.Result.error(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
@POST
|
||||
public Object pageData(PageForm pageForm,
|
||||
@Param(value = "unionId", required = false) String unionId,
|
||||
@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "personType", required = false) String personType,
|
||||
@Param(value = "userState", required = false) String userState,
|
||||
@Param(value = "groupId", required = false) Integer groupId,
|
||||
@Param(value = "activityUnionId", required = false) String activityUnionId,
|
||||
@Param(value = "activityUnitId", required = false) String activityUnitId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
aus.id,
|
||||
u.username AS userName,
|
||||
u.loginname AS loginName,
|
||||
u.sex,
|
||||
u.birthday,
|
||||
u.mobile,
|
||||
u.personType,
|
||||
u.userState,
|
||||
u.unitname AS unitName,
|
||||
u.unionname AS unionName,
|
||||
aus.groupId,
|
||||
aus.groupName,
|
||||
actun.unionname AS activityUnionName
|
||||
FROM
|
||||
user_part aus
|
||||
LEFT JOIN `user` u ON u.id = aus.userId
|
||||
LEFT JOIN activity_basic_unit actit ON actit.id=u.unitid
|
||||
LEFT JOIN activity_basic_union actun ON actit.unionid=actun.id
|
||||
$condition
|
||||
""");
|
||||
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (StrUtil.isNotBlank(pageForm.getSearchName()) && StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
|
||||
cnd.and(Cnd.likeEX(pageForm.getSearchName(), pageForm.getSearchKeyword()));
|
||||
}
|
||||
cnd.andEX("actit.unionid", "=", activityUnionId);
|
||||
cnd.andEX("actit.id", "=", activityUnitId);
|
||||
cnd.andEX("aus.groupId", "=", groupId);
|
||||
cnd.andEX("u.unionid", "=", unionId);
|
||||
cnd.andEX("u.unitid", "=", unitId);
|
||||
cnd.andEX("u.personType", "=", personType);
|
||||
cnd.andEX("u.userState", "=", userState);
|
||||
if (!io.v.nutz.web.commons.utils.ShiroUtil.hasAnyRoles("sysadmin,SchoolUnionAdmin")) {
|
||||
cnd.andEX("aus.creator", "=", io.v.nutz.web.commons.utils.ShiroUtil.getPrincipalProperty("id"));
|
||||
}
|
||||
|
||||
sql.setCondition(cnd);
|
||||
return userPartUpService.list(pageForm, sql);
|
||||
}
|
||||
|
||||
private Cnd getCnd(PageForm pageForm,
|
||||
String unionId,
|
||||
String unitId,
|
||||
String[] personTypes,
|
||||
String[] userStates,
|
||||
String[] memberTypes,
|
||||
String[] sexTypes,
|
||||
String[] age,
|
||||
String teacherMeetingId,
|
||||
String[] roleIds,
|
||||
String[] userId,
|
||||
String clubId,
|
||||
boolean reverseSelection,
|
||||
Integer activityGroupId,
|
||||
String activityUserCndStr) {
|
||||
String IN_OR_NIN_OP = reverseSelection ? "NOT IN" : "IN";
|
||||
String EQ_OR_NEQ_OP = reverseSelection ? "!=" : "=";
|
||||
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
|
||||
cnd.orderBy(pageForm.getPageOrderName(), pageForm.getPageOrderBy().equalsIgnoreCase("ascending") ? "ASC" : "DESC");
|
||||
}
|
||||
|
||||
if (activityGroupId != null) {
|
||||
Sql sqlx = Sqls.createf("SELECT userId FROM user_part where groupId = '%s'", activityGroupId);
|
||||
cnd.and("u.id", reverseSelection ? "IN" : "NOT IN", sqlx);
|
||||
}
|
||||
|
||||
cnd.andEX("u.id", IN_OR_NIN_OP, userId);
|
||||
|
||||
if (Lang.isNotEmpty(memberTypes)) {
|
||||
if (ArrayUtils.contains(memberTypes, "工会会员")) {
|
||||
cnd.and("u.member", EQ_OR_NEQ_OP, 1);
|
||||
}
|
||||
if (ArrayUtils.contains(memberTypes, "福利会员")) {
|
||||
cnd.and("u.welfareMember", EQ_OR_NEQ_OP, 1);
|
||||
}
|
||||
if (ArrayUtils.contains(memberTypes, "基金会员")) {
|
||||
cnd.and(new Static(" u.loginname " + IN_OR_NIN_OP + " (select loginname from sick_fund_member)"));
|
||||
}
|
||||
}
|
||||
|
||||
if (!Lang.isEmptyArray(age)) {
|
||||
if (!age[1].equals("0")) {
|
||||
if (reverseSelection) {
|
||||
cnd.andNot("TIMESTAMPDIFF(YEAR, u.birthday, CURDATE())", "between", age);
|
||||
} else {
|
||||
cnd.and("TIMESTAMPDIFF(YEAR, u.birthday, CURDATE())", "between", age);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
SqlExpressionGroup sqlExpressionGroup = ActivityUserCnd.formatSql("u", activityUserCndStr, reverseSelection);
|
||||
if (!sqlExpressionGroup.isEmpty()) {
|
||||
cnd.and(sqlExpressionGroup);
|
||||
}
|
||||
|
||||
cnd.andEX("u.unionid", EQ_OR_NEQ_OP, unionId);
|
||||
cnd.andEX("u.unitid", EQ_OR_NEQ_OP, unitId);
|
||||
cnd.andEX("u.personType", IN_OR_NIN_OP, personTypes);
|
||||
cnd.andEX("u.userState", IN_OR_NIN_OP, userStates);
|
||||
cnd.andEX("u.sex", IN_OR_NIN_OP, sexTypes);
|
||||
cnd.andEX("sur.jdhid", EQ_OR_NEQ_OP, teacherMeetingId);
|
||||
cnd.andEX("sur.roleid", IN_OR_NIN_OP, roleIds);
|
||||
cnd.andEX("clubuser.clubid", EQ_OR_NEQ_OP, clubId);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return cnd;
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public Object cleanPart(@Param(value = "id", required = false) String id,
|
||||
@Param(value = "groupId", required = false) Integer groupId,
|
||||
@Param(value = "searchKeyword", required = false) String searchKeyword,
|
||||
@Param(value = "searchName", required = false) String searchName,
|
||||
@Param(value = "unionId", required = false) String unionId,
|
||||
@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "activityUnionId", required = false) String activityUnionId,
|
||||
@Param(value = "activityUnitId", required = false) String activityUnitId,
|
||||
@Param(value = "personType", required = false) String personType,
|
||||
@Param(value = "userState", required = false) String userState) {
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (StrUtil.isNotBlank(id)) {
|
||||
cnd.and("id", "=", id);
|
||||
userPartUpService.dao().clear(UserPartUp.class, cnd);
|
||||
} else {
|
||||
List<User> users = userPartUpService.dao().query(User.class, Cnd.NEW());
|
||||
if (StrUtil.isNotBlank(searchKeyword) && StrUtil.isNotBlank(searchName)) {
|
||||
users = users.stream().filter(v -> v.getLoginname().equals(searchKeyword) || v.getUsername().equals(searchKeyword)).collect(Collectors.toList());
|
||||
}
|
||||
if (StrUtil.isNotBlank(unionId)) {
|
||||
users = users.stream().filter(v -> StrUtil.isNotBlank(v.getUnionid()) && v.getUnionid().equals(unionId)).collect(Collectors.toList());
|
||||
}
|
||||
if (StrUtil.isNotBlank(unitId)) {
|
||||
users = users.stream().filter(v -> StrUtil.isNotBlank(v.getUnitid()) && v.getUnitid().equals(unitId)).collect(Collectors.toList());
|
||||
}
|
||||
if (StrUtil.isNotBlank(personType)) {
|
||||
users = users.stream().filter(v -> StrUtil.isNotBlank(v.getPersonType()) && v.getPersonType().equals(personType)).collect(Collectors.toList());
|
||||
}
|
||||
if (StrUtil.isNotBlank(userState)) {
|
||||
users = users.stream().filter(v -> StrUtil.isNotBlank(v.getUserState()) && v.getUserState().equals(userState)).collect(Collectors.toList());
|
||||
}
|
||||
if (StrUtil.isNotBlank(activityUnitId)) {
|
||||
users = users.stream().filter(v -> StrUtil.isNotBlank(v.getActivityUnitId()) && v.getActivityUnitId().equals(activityUnitId)).collect(Collectors.toList());
|
||||
}
|
||||
if (StrUtil.isNotBlank(activityUnionId)) {
|
||||
users = users.stream().filter(v -> StrUtil.isNotBlank(v.getActivityUnionId()) && v.getActivityUnionId().equals(activityUnionId)).collect(Collectors.toList());
|
||||
}
|
||||
List<String> ids = users.stream().map(User::getId).collect(Collectors.toList());
|
||||
cnd.andEX("userId", "in", ids);
|
||||
cnd.andEX("groupId", "=", groupId);
|
||||
if (Lang.isEmpty(ids)) {
|
||||
return null;
|
||||
}
|
||||
userPartUpService.dao().clear(UserPartUp.class, cnd);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getPartCount() {
|
||||
return dao.count(UserPartUp.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置更新人员
|
||||
*
|
||||
* @param unionId 工会id
|
||||
* @param unitId 单位id
|
||||
* @param personTypes 人员类型
|
||||
* @param userStates 在职状态
|
||||
* @return Result
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
@POST
|
||||
public Object doSetUpUser(PageForm pageForm,
|
||||
@Param(value = "unionId", required = false) String unionId,
|
||||
@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "personTypes", required = false) String[] personTypes,
|
||||
@Param(value = "userStates", required = false) String[] userStates,
|
||||
@Param(value = "memberTypes", required = false) String[] memberTypes,
|
||||
@Param(value = "sexTypes", required = false) String[] sexTypes,
|
||||
@Param(value = "age", required = false) String[] age,
|
||||
@Param(value = "activityGroupId", required = false) Integer activityGroupId,
|
||||
@Param(value = "setGroupType", required = false) Integer setGroupType,
|
||||
@Param(value = "setGroupId", required = false) Integer setGroupId,
|
||||
@Param(value = "setGroupName", required = false) String setGroupName,
|
||||
@Param(value = "teacherMeetingId", required = false) String teacherMeetingId,
|
||||
@Param(value = "roleIds", required = false) String[] roleIds,
|
||||
@Param(value = "userId", required = false) String[] userId,
|
||||
@Param(value = "clubId", required = false) String clubId,
|
||||
@Param(value = "reverseSelection") boolean reverseSelection,
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT DISTINCT
|
||||
( u.id ) AS userId,
|
||||
u.loginname
|
||||
FROM
|
||||
`user` u
|
||||
LEFT JOIN sys_user_role sur ON sur.userid = u.id
|
||||
LEFT JOIN sys_club_user clubuser ON clubuser.userid=u.id
|
||||
$condition
|
||||
""");
|
||||
|
||||
Cnd cnd = getCnd(pageForm, unionId, unitId, personTypes, userStates, memberTypes, sexTypes, age, teacherMeetingId, roleIds, userId, clubId, reverseSelection, activityGroupId, activityUserCndStr);
|
||||
|
||||
sql.setCondition(cnd);
|
||||
sql.setCallback(Sqls.callback.entities());
|
||||
sql.setEntity(dao.getEntity(UserPartUp.class));
|
||||
dao.execute(sql);
|
||||
|
||||
List<UserPartUp> list = sql.getList(UserPartUp.class);
|
||||
|
||||
NutMap groupMap = null;
|
||||
int maxCount = 0;
|
||||
|
||||
if (setGroupType == 1) {
|
||||
groupMap = userPartUpService.fetch(Sqls.create("select groupName from user_part where groupId = @groupId").setParam("groupId", setGroupId));
|
||||
} else if (setGroupType == 2) {
|
||||
maxCount = userPartUpService.count(Sqls.create("select max(groupId) from user_part"));
|
||||
}
|
||||
|
||||
for (UserPartUp userScope : list) {
|
||||
if (setGroupType == 1) {
|
||||
userScope.setGroupId(setGroupId);
|
||||
userScope.setGroupName(groupMap.getString("groupName"));
|
||||
} else if (setGroupType == 2) {
|
||||
userScope.setGroupId(maxCount + 1);
|
||||
userScope.setGroupName(setGroupName);
|
||||
}
|
||||
userScope.setCreator((String) ShiroUtil.getPrincipalProperty("id"));
|
||||
}
|
||||
if (list.size() < 500) {
|
||||
dao.insert(list);
|
||||
return setGroupType == 1 ? setGroupId : maxCount + 1;
|
||||
}
|
||||
//多线程插入
|
||||
userPartUpService.largeDataInsert(list);
|
||||
return setGroupType == 1 ? setGroupId : maxCount + 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取组别
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@At
|
||||
@ViReturn
|
||||
public Object getUserPart() {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
groupId,
|
||||
groupName
|
||||
FROM
|
||||
user_part
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.NEW();
|
||||
if (!ShiroUtil.hasAnyRoles("sysadmin,H06,H10")) {
|
||||
if (ShiroUtil.hasAnyRoles(new String[]{"H04", "club01"})) {
|
||||
cnd.and("creator", "=", ShiroUtil.getPrincipalProperty("id"));
|
||||
}
|
||||
}
|
||||
cnd.and("groupId", "IS NOT", null);
|
||||
cnd.and("groupName", "IS NOT", null);
|
||||
cnd.groupBy("groupId");
|
||||
sql.setCondition(cnd);
|
||||
return userPartUpService.listMap(sql);
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@ViReturn
|
||||
public void doExportUser(PageForm pageForm,
|
||||
@Param(value = "props", required = false) String props,
|
||||
@Param(value = "unionId", required = false) String unionId,
|
||||
@Param(value = "unitId", required = false) String unitId,
|
||||
@Param(value = "personTypes", required = false) String[] personTypes,
|
||||
@Param(value = "userStates", required = false) String[] userStates,
|
||||
@Param(value = "memberTypes", required = false) String[] memberTypes,
|
||||
@Param(value = "sexTypes", required = false) String[] sexTypes,
|
||||
@Param(value = "age", required = false) String[] age,
|
||||
@Param(value = "teacherMeetingId", required = false) String teacherMeetingId,
|
||||
@Param(value = "roleIds", required = false) String[] roleIds,
|
||||
@Param(value = "userId", required = false) String[] userId,
|
||||
@Param(value = "clubId", required = false) String clubId,
|
||||
@Param(value = "reverseSelection") boolean reverseSelection,
|
||||
@Param(value = "activityGroupId", required = false) Integer activityGroupId,
|
||||
@Param(value = "activityUserCnd", required = false) String activityUserCndStr, HttpServletResponse response) {
|
||||
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT
|
||||
DISTINCT(u.id) as id,
|
||||
u.loginname AS loginName,
|
||||
u.username AS userName,
|
||||
u.sex,
|
||||
u.birthday,
|
||||
u.mobile,
|
||||
u.personType,
|
||||
u.userState,
|
||||
u.unitname AS unitName,
|
||||
u.unionname AS unionName,
|
||||
TIMESTAMPDIFF(YEAR, u.birthday, CURDATE()) AS age
|
||||
FROM
|
||||
`user` u
|
||||
LEFT JOIN sys_user_role sur on sur.userid = u.id
|
||||
LEFT JOIN sys_club_user clubuser ON clubuser.userid=u.id
|
||||
$condition
|
||||
""");
|
||||
try {
|
||||
Cnd cnd = getCnd(pageForm, unionId, unitId, personTypes, userStates, memberTypes, sexTypes, age, teacherMeetingId, roleIds, userId, clubId, reverseSelection, activityGroupId, activityUserCndStr);
|
||||
|
||||
sql.setCondition(cnd);
|
||||
|
||||
List<NutMap> map = userPartUpService.listMap(sql);
|
||||
|
||||
List<ExcelExportEntity> entityList = new ArrayList<>();
|
||||
ExcelExportEntity no = new ExcelExportEntity("序号", "no", 10);
|
||||
no.setFormat("isAddIndex");
|
||||
entityList.add(no);
|
||||
Map<String, String> propMap = Json.fromJson(Map.class, props);
|
||||
propMap.forEach((k, v) -> {
|
||||
entityList.add(new ExcelExportEntity(v, k, 40));
|
||||
});
|
||||
|
||||
ViTool.excelResponse(response, "人员名单.xls");
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), entityList, map);
|
||||
workbook.write(response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.v.nutz.web.commons.controller.userpartupdate.models;
|
||||
|
||||
import lombok.Data;
|
||||
import org.nutz.dao.entity.annotation.*;
|
||||
import org.nutz.dao.interceptor.annotation.PrevInsert;
|
||||
|
||||
@Data
|
||||
@TableIndexes({
|
||||
@Index(name = "INDEX_USER_PART_ID", fields = {"id"}, unique = false),
|
||||
@Index(name = "INDEX_USER_PART_USER_ID", fields = {"userId"}, unique = false),
|
||||
})
|
||||
@Table("user_part")
|
||||
public class UserPartUp{
|
||||
|
||||
@Name
|
||||
@Comment("id")
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@PrevInsert(uu32 = true)
|
||||
private String id;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.INT)
|
||||
@Comment("组别id")
|
||||
private Integer groupId;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 100)
|
||||
@Comment("组别名称")
|
||||
private String groupName;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("userid")
|
||||
private String userId;
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("工号")
|
||||
private String loginname;
|
||||
|
||||
|
||||
@Column
|
||||
@ColDefine(type = ColType.VARCHAR, width = 32)
|
||||
@Comment("创建人")
|
||||
private String creator;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package io.v.nutz.web.commons.controller.userpartupdate.service;
|
||||
|
||||
import io.v.nutz.base.service.ViService;
|
||||
import io.v.nutz.web.commons.controller.userpartupdate.models.UserPartUp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface UserPatUpService extends ViService<UserPartUp> {
|
||||
|
||||
/**
|
||||
* 大量数据插入
|
||||
*/
|
||||
void largeDataInsert(List<UserPartUp> list);
|
||||
|
||||
void renewUserState();
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
package io.v.nutz.web.commons.controller.userpartupdate.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||
import io.v.nutz.base.utils.DateUtil;
|
||||
import io.v.nutz.web.commons.controller.userpartupdate.models.UserPartUp;
|
||||
import io.v.nutz.web.commons.controller.userpartupdate.service.UserPatUpService;
|
||||
import io.v.nutz.sys.models.Sys_dict;
|
||||
import io.v.nutz.sys.services.SysDictService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.aop.interceptor.ioc.TransAop;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
import org.nutz.dao.impl.NutTxDao;
|
||||
import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.aop.Aop;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@Slf4j
|
||||
public class UserPartUpServiceImpl extends ViServiceImpl<UserPartUp> implements UserPatUpService {
|
||||
public UserPartUpServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
|
||||
@Inject
|
||||
private SysDictService sysDictService;
|
||||
|
||||
@Override
|
||||
public void largeDataInsert(List<UserPartUp> list) {
|
||||
log.info("开始时间" + DateUtil.getDateTime());
|
||||
List<List<UserPartUp>> splitList = CollectionUtil.split(list, 500);
|
||||
NutTxDao nutTxDao = new NutTxDao(dao()).setDebug(true);
|
||||
nutTxDao.beginRC();
|
||||
ExecutorService executorService = Executors.newWorkStealingPool();
|
||||
|
||||
List<Callable<Object>> taskList = new ArrayList<>();
|
||||
|
||||
for (List<UserPartUp> everyList : splitList) {
|
||||
taskList.add(() -> nutTxDao.insert(everyList).size());
|
||||
}
|
||||
|
||||
List<Future<Object>> futureList = null;
|
||||
|
||||
AtomicInteger atomicInteger = new AtomicInteger();
|
||||
|
||||
try {
|
||||
futureList = executorService.invokeAll(taskList);
|
||||
for (Future<Object> future : futureList) {
|
||||
atomicInteger.getAndAdd((Integer) future.get());
|
||||
}
|
||||
if (atomicInteger.get() != list.size()) {
|
||||
throw new RuntimeException("插入数据与期望数据条数不符");
|
||||
}
|
||||
nutTxDao.commit();
|
||||
} catch (Exception e) {
|
||||
nutTxDao.rollback();
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
executorService.shutdown();
|
||||
}
|
||||
log.info("结束时间" + DateUtil.getDateTime());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Aop(TransAop.READ_COMMITTED)
|
||||
public void renewUserState() {
|
||||
//在职状态
|
||||
Sql userStateSql = Sqls.create("select userState from `user` where userState is not null and userState !='' group by userState");
|
||||
List<String> userStates = sysDictService.listMap(userStateSql).stream().map(v -> v.getString("userState")).collect(Collectors.toList());
|
||||
List<Sys_dict> userStateList = sysDictService.getSubListByCode("UserState");
|
||||
|
||||
//dict中的在职状态,在user表中没有,这一部分删除
|
||||
List<Sys_dict> deleteUserState = userStateList.stream().filter(v -> !userStates.contains(v.getName())).collect(Collectors.toList());
|
||||
List<String> deleteUserStateIds = deleteUserState.stream().map(Sys_dict::getId).collect(Collectors.toList());
|
||||
|
||||
if (Lang.isNotEmpty(deleteUserStateIds)) {
|
||||
sysDictService.delete(deleteUserStateIds);
|
||||
}
|
||||
|
||||
//获取删除dict后的在职状态
|
||||
List<Sys_dict> deleteRenewUserStateList = sysDictService.getSubListByCode("UserState");
|
||||
Sys_dict userState = deleteRenewUserStateList.stream().findFirst().orElse(null);
|
||||
assert userState != null;
|
||||
//获取在职状态名
|
||||
List<String> dictNameList = deleteRenewUserStateList.stream().map(Sys_dict::getName).collect(Collectors.toList());
|
||||
//如果user表中有在职状态, 删除userState后的dict表中没有,就需要新增
|
||||
List<String> insertUserStateList = userStates.stream().filter(v -> !dictNameList.contains(v)).collect(Collectors.toList());
|
||||
|
||||
insertUserStateList = insertUserStateList.stream().distinct().collect(Collectors.toList());
|
||||
if (Lang.isNotEmpty(insertUserStateList)) {
|
||||
for (String item : insertUserStateList) {
|
||||
Sys_dict dict = new Sys_dict();
|
||||
dict.setCode(item);
|
||||
dict.setName(item);
|
||||
sysDictService.save(dict, userState.getParentId());
|
||||
}
|
||||
}
|
||||
|
||||
//人员类型
|
||||
Sql personTypeSql = Sqls.create("select personType from `user` where personType is not null and personType !='' group by personType");
|
||||
List<String> personTypes = sysDictService.listMap(personTypeSql).stream().map(v->v.getString("personType")).collect(Collectors.toList());
|
||||
List<Sys_dict> personTypeList = sysDictService.getSubListByCode("UserType");
|
||||
|
||||
//dict中的personType,在user表中没有,这一部分删除
|
||||
List<Sys_dict> deletePersonType = personTypeList.stream().filter(v -> !personTypes.contains(v.getName())).collect(Collectors.toList());
|
||||
List<String> deletePersonTypes = deletePersonType.stream().map(Sys_dict::getId).collect(Collectors.toList());
|
||||
|
||||
if (Lang.isNotEmpty(deletePersonTypes)){
|
||||
sysDictService.delete(deletePersonTypes);
|
||||
}
|
||||
|
||||
//获取删除dict后的personType
|
||||
List<Sys_dict> deleteRenewPersonTypeList = sysDictService.getSubListByCode("UserType");
|
||||
Sys_dict personType = deleteRenewPersonTypeList.stream().findFirst().orElse(null);
|
||||
assert personType != null;
|
||||
//获取personType
|
||||
List<String> dictPersonTypeNameList = deleteRenewPersonTypeList.stream().map(Sys_dict::getName).collect(Collectors.toList());
|
||||
//如果user表中有personType, 删除personType后的dict表中没有,就需要新增
|
||||
List<String> insertPersonTypeList = personTypes.stream().filter(v -> !dictPersonTypeNameList.contains(v)).collect(Collectors.toList());
|
||||
|
||||
insertPersonTypeList = insertPersonTypeList.stream().distinct().collect(Collectors.toList());
|
||||
if (Lang.isNotEmpty(insertPersonTypeList)) {
|
||||
for (String item : insertPersonTypeList) {
|
||||
Sys_dict dict = new Sys_dict();
|
||||
dict.setCode(item);
|
||||
dict.setName(item);
|
||||
sysDictService.save(dict, personType.getParentId());
|
||||
}
|
||||
}
|
||||
|
||||
sysDictService.clearCache();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.v.nutz.web.commons.exception;
|
||||
|
||||
/**
|
||||
* @author wizzer@qq.com
|
||||
*/
|
||||
public class CaptchaException extends Exception{
|
||||
public CaptchaException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.v.nutz.web.commons.exception;
|
||||
|
||||
/**
|
||||
* @author wizzer@qq.com
|
||||
*/
|
||||
public class SmsException extends Exception{
|
||||
public SmsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package io.v.nutz.web.commons.ext.beetl;
|
||||
|
||||
import org.beetl.core.Format;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2018/1/23.
|
||||
*/
|
||||
public class FileSizeFormat implements Format {
|
||||
public Object format(Object data, String pattern) {
|
||||
return Strings.formatSizeForReadBy1024(Long.valueOf(Strings.sNull(data)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.v.nutz.web.commons.ext.beetl;
|
||||
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import org.beetl.core.Context;
|
||||
import org.beetl.core.Function;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
/**
|
||||
* @Author: 1V
|
||||
* @DateTime: 2020/11/25 16:40
|
||||
* @Description: TODO
|
||||
*/
|
||||
public class GetSysConfig implements Function {
|
||||
@Override
|
||||
public Object call(Object[] objects, Context context) {
|
||||
String parm = (String) objects[0];
|
||||
|
||||
Object result = "";
|
||||
|
||||
switch (Strings.sNull(parm)) {
|
||||
case "AppName":
|
||||
result = Globals.AppName;
|
||||
break;
|
||||
case "AppShortName":
|
||||
result = Globals.AppShrotName;
|
||||
break;
|
||||
case "AppDomain":
|
||||
result = Globals.AppDomain;
|
||||
break;
|
||||
case "AppFileDomain":
|
||||
result = Globals.AppFileDomain;
|
||||
break;
|
||||
case "AppUploadBase":
|
||||
result = Globals.AppUploadBase;
|
||||
break;
|
||||
default:
|
||||
result = Globals.MyConfig.get(parm);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.v.nutz.web.commons.ext.beetl;
|
||||
|
||||
import org.beetl.core.Format;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2018/2/6.
|
||||
*/
|
||||
public class Html2TxtFormat implements Format {
|
||||
|
||||
public Object format(Object data, String pattern) {
|
||||
if (data == null) {
|
||||
return "";
|
||||
}
|
||||
Document document= Jsoup.parse(Strings.sNull(data));
|
||||
String s = document.text();
|
||||
if (pattern != null && s.length() > Integer.valueOf(pattern)) {
|
||||
return s.substring(0, Integer.valueOf(pattern));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.v.nutz.web.commons.ext.beetl;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/2/8.
|
||||
*/
|
||||
|
||||
import org.beetl.core.Format;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
public class HtmlEscapeFormat implements Format {
|
||||
|
||||
public Object format(Object data, String pattern) {
|
||||
return Strings.escapeHtml(String.valueOf(data == null ? "" : data));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package io.v.nutz.web.commons.ext.beetl;
|
||||
|
||||
import org.beetl.core.Context;
|
||||
import org.beetl.core.Function;
|
||||
|
||||
public class IncludeJs implements Function {
|
||||
public IncludeJs() {
|
||||
}
|
||||
|
||||
public Object call(Object[] objects, Context context) {
|
||||
String path = (String)objects[0];
|
||||
return String.format("<script type=\"text/javascript\" src=\"%s\"></script>", path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.v.nutz.web.commons.ext.beetl;
|
||||
|
||||
import org.beetl.core.Format;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2018/2/6.
|
||||
*/
|
||||
public class StrlenFormat implements Format {
|
||||
|
||||
public Object format(Object data, String pattern) {
|
||||
if (data == null) {
|
||||
return "";
|
||||
}
|
||||
String s = Strings.sNull(data);
|
||||
if (pattern != null && s.length() > Integer.valueOf(pattern)) {
|
||||
return s.substring(0, Integer.valueOf(pattern));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.v.nutz.web.commons.ext.handler;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 错误页拦截器,登陆后台显示友好提示
|
||||
*/
|
||||
@IocBean
|
||||
public class WkErrorPageHandler extends ErrorPageErrorHandler {
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
if (response.getStatus() == 403 || response.getStatus() == 404 || response.getStatus() == 500) {
|
||||
try {
|
||||
if (isAjax(request)) {
|
||||
response.getWriter().write(Json.toJson(new NutMap("code", "-1").setv("msg", response.getStatus() + " error")));
|
||||
return;
|
||||
} else {
|
||||
request.setAttribute("original_request_uri", request.getRequestURI());
|
||||
RequestDispatcher rd = request.getRequestDispatcher("/platform/home/" + response.getStatus());
|
||||
rd.forward(request, response);
|
||||
return;
|
||||
}
|
||||
} catch (ServletException e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
super.handle(target, baseRequest, request, response);
|
||||
}
|
||||
|
||||
private boolean isAjax(ServletRequest req) {
|
||||
String value = ((HttpServletRequest) req).getHeader("X-Requested-With");
|
||||
return value != null && "XMLHttpRequest".equalsIgnoreCase(value.trim());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package io.v.nutz.web.commons.ext.pubsub;
|
||||
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import io.v.nutz.sys.services.SysConfigService;
|
||||
import io.v.nutz.sys.services.SysRouteService;
|
||||
import org.nutz.integration.jedis.pubsub.PubSub;
|
||||
import org.nutz.integration.jedis.pubsub.PubSubService;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
/**
|
||||
* 订阅发布用于更新所有实例的 Globals变量
|
||||
* Created by wizzer on 2018/3/18.
|
||||
*/
|
||||
@IocBean(create = "init")
|
||||
public class WebPubSub implements PubSub {
|
||||
private static final Log log = Logs.get();
|
||||
@Inject
|
||||
protected PubSubService pubSubService;
|
||||
@Inject
|
||||
protected SysConfigService sysConfigService;
|
||||
@Inject
|
||||
protected SysRouteService sysRouteService;
|
||||
|
||||
@Inject("refer:$ioc")
|
||||
protected Ioc ioc;
|
||||
|
||||
@Inject
|
||||
protected PropertiesProxy conf;
|
||||
|
||||
public void init() {
|
||||
pubSubService.reg("nutzwk:web:platform", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(String channel, String message) {
|
||||
log.debug("WebPubSub onMessage::" + message);
|
||||
switch (message) {
|
||||
case "sys_config":
|
||||
Globals.initSysConfig(sysConfigService);
|
||||
break;
|
||||
case "sys_route":
|
||||
Globals.initRoute(sysRouteService);
|
||||
break;
|
||||
case "sys_wx":
|
||||
Globals.initWx();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package io.v.nutz.web.commons.ext.sms;
|
||||
|
||||
import io.v.nutz.web.commons.exception.SmsException;
|
||||
import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
||||
import com.tencentcloudapi.common.profile.HttpProfile;
|
||||
import com.tencentcloudapi.sms.v20190711.SmsClient;
|
||||
import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
|
||||
import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
/**
|
||||
* 短信服务
|
||||
*
|
||||
* @author wizzer@qq.com
|
||||
*/
|
||||
@IocBean
|
||||
public class SmsService {
|
||||
private static final Log log = Logs.get();
|
||||
@Inject
|
||||
private PropertiesProxy conf;
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @param text 验证码
|
||||
* @return true发送成功
|
||||
* @throws SmsException
|
||||
*/
|
||||
public boolean sendCode(String mobile, String text) throws SmsException {
|
||||
try {
|
||||
if (!conf.getBoolean("sms.enabled")) {
|
||||
return true;
|
||||
}
|
||||
Credential cred = new Credential(conf.get("sms.tencent.secret-id"), conf.get("sms.tencent.secret-key"));
|
||||
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint("sms.tencentcloudapi.com");
|
||||
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
|
||||
SmsClient client = new SmsClient(cred, "", clientProfile);
|
||||
|
||||
SendSmsRequest req = new SendSmsRequest();
|
||||
String[] phoneNumberSet1 = {"+86"+mobile};
|
||||
req.setPhoneNumberSet(phoneNumberSet1);
|
||||
String[] templateParamSet1 = {text};
|
||||
req.setTemplateParamSet(templateParamSet1);
|
||||
req.setTemplateID(conf.get("sms.tencent.tpl.code"));
|
||||
req.setSmsSdkAppid(conf.get("sms.tencent.appid"));
|
||||
req.setSign(conf.get("sms.tencent.sign"));
|
||||
SendSmsResponse resp = client.SendSms(req);
|
||||
log.debug(SendSmsResponse.toJsonString(resp));
|
||||
return true;
|
||||
} catch (TencentCloudSDKException tencentCloudSDKException) {
|
||||
throw new SmsException(tencentCloudSDKException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信通知
|
||||
*
|
||||
* @param mobile 手机号码(最多200)
|
||||
* @param param 模板参数值
|
||||
* @return
|
||||
* @throws SmsException
|
||||
*/
|
||||
public boolean sendMsg(String[] mobile, String[] param) throws SmsException {
|
||||
try {
|
||||
if (!conf.getBoolean("sms.enabled")) {
|
||||
return true;
|
||||
}
|
||||
Credential cred = new Credential(conf.get("sms.tencent.secret-id"), conf.get("sms.tencent.secret-key"));
|
||||
|
||||
HttpProfile httpProfile = new HttpProfile();
|
||||
httpProfile.setEndpoint("sms.tencentcloudapi.com");
|
||||
|
||||
ClientProfile clientProfile = new ClientProfile();
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
|
||||
SmsClient client = new SmsClient(cred, "", clientProfile);
|
||||
|
||||
SendSmsRequest req = new SendSmsRequest();
|
||||
req.setPhoneNumberSet(mobile);
|
||||
req.setTemplateParamSet(param);
|
||||
req.setTemplateID(conf.get("sms.tencent.tpl.msg"));
|
||||
req.setSmsSdkAppid(conf.get("sms.tencent.appid"));
|
||||
req.setSign(conf.get("sms.tencent.sign"));
|
||||
SendSmsResponse resp = client.SendSms(req);
|
||||
log.debug(SendSmsResponse.toJsonString(resp));
|
||||
return true;
|
||||
} catch (TencentCloudSDKException tencentCloudSDKException) {
|
||||
throw new SmsException(tencentCloudSDKException.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package io.v.nutz.web.commons.ext.validate;
|
||||
|
||||
import io.v.nutz.base.constant.RedisConstant;
|
||||
import io.v.nutz.web.commons.exception.CaptchaException;
|
||||
import io.v.nutz.web.commons.exception.SmsException;
|
||||
import io.v.nutz.web.commons.ext.sms.SmsService;
|
||||
import com.wf.captcha.ArithmeticCaptcha;
|
||||
import io.v.nutz.web.commons.shiro.exception.CaptchaEmptyException;
|
||||
import io.v.nutz.web.commons.shiro.exception.CaptchaIncorrectException;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.random.R;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author wizzer@qq.com
|
||||
*/
|
||||
@IocBean
|
||||
public class ValidateService {
|
||||
private static final Log log = Logs.get();
|
||||
@Inject
|
||||
private RedisService redisService;
|
||||
@Inject
|
||||
private SmsService smsService;
|
||||
|
||||
public NutMap getCode() {
|
||||
String uuid = UUID.randomUUID().toString().replace("-", "");
|
||||
ArithmeticCaptcha captcha = new ArithmeticCaptcha(120, 40);
|
||||
captcha.getArithmeticString(); // 获取运算的公式:3+2=?
|
||||
String text = captcha.text();
|
||||
redisService.setex(RedisConstant.REDIS_CAPTCHA_KEY + uuid, 180, text);
|
||||
return NutMap.NEW().addv("key", uuid).addv("codeUrl", captcha.toBase64());
|
||||
}
|
||||
|
||||
public void getSMSCode(String mobile) throws SmsException {
|
||||
String text = R.captchaNumber(4);
|
||||
|
||||
String codeFromRedis = redisService.get(RedisConstant.REDIS_SMSCODE_KEY + mobile + ":LOCK");
|
||||
if (Strings.isNotBlank(codeFromRedis)) {
|
||||
throw new SmsException("请1分钟之后再试");
|
||||
}
|
||||
|
||||
if (smsService.sendCode(mobile, text)) {
|
||||
log.debug("sms code:::" + text);
|
||||
redisService.setex(RedisConstant.REDIS_SMSCODE_KEY + mobile, 300, text);
|
||||
redisService.setex(RedisConstant.REDIS_SMSCODE_KEY + mobile + ":LOCK", 60, text);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkCode(String key, String code) {
|
||||
String codeFromRedis = redisService.get(RedisConstant.REDIS_CAPTCHA_KEY + key);
|
||||
if (Strings.isBlank(code)) {
|
||||
throw new CaptchaEmptyException("请输入验证码");
|
||||
}
|
||||
if (Strings.isEmpty(codeFromRedis)) {
|
||||
throw new CaptchaIncorrectException("验证码已过期");
|
||||
}
|
||||
if (!Strings.equalsIgnoreCase(code, codeFromRedis)) {
|
||||
throw new CaptchaIncorrectException("验证码不正确");
|
||||
}
|
||||
redisService.del(RedisConstant.REDIS_CAPTCHA_KEY + key);
|
||||
}
|
||||
|
||||
public void checkSMSCode(String mobile, String code) throws CaptchaException {
|
||||
String codeFromRedis = redisService.get(RedisConstant.REDIS_SMSCODE_KEY + mobile);
|
||||
|
||||
if (Strings.isBlank(code)) {
|
||||
throw new CaptchaException("请输入短信验证码");
|
||||
}
|
||||
if (Strings.isEmpty(codeFromRedis)) {
|
||||
throw new CaptchaException("短信验证码已过期");
|
||||
}
|
||||
if (!Strings.equalsIgnoreCase(code, codeFromRedis)) {
|
||||
throw new CaptchaException("短信验证码不正确");
|
||||
}
|
||||
redisService.del(RedisConstant.REDIS_SMSCODE_KEY + mobile);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.v.nutz.web.commons.ext.websocket;
|
||||
|
||||
import org.nutz.integration.jedis.JedisAgent;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.plugins.mvc.websocket.WsRoomProvider;
|
||||
import redis.clients.jedis.Jedis;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2018/7/5.
|
||||
*/
|
||||
public class WkJedisRoomProvider implements WsRoomProvider {
|
||||
private static final Log log = Logs.get();
|
||||
protected JedisAgent jedisAgent;
|
||||
protected int RedisKeySessionTTL;
|
||||
|
||||
public WkJedisRoomProvider(JedisAgent jedisAgent, int RedisKeySessionTTL) {
|
||||
this.jedisAgent = jedisAgent;
|
||||
this.RedisKeySessionTTL = RedisKeySessionTTL;
|
||||
}
|
||||
|
||||
public Set<String> wsids(String room) {
|
||||
try (Jedis jedis = jedisAgent.getResource()) {
|
||||
return jedis.smembers(room);
|
||||
}
|
||||
}
|
||||
|
||||
public void join(String room, String wsid) {
|
||||
try (Jedis jedis = jedisAgent.getResource()) {
|
||||
jedis.sadd(room, wsid);
|
||||
jedis.expire(room,RedisKeySessionTTL);//每次加入的时候时间有效期重置?
|
||||
}
|
||||
}
|
||||
|
||||
public void left(String room, String wsid) {
|
||||
try (Jedis jedis = jedisAgent.getResource()) {
|
||||
jedis.srem(room, wsid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package io.v.nutz.web.commons.ext.websocket;
|
||||
|
||||
import io.v.nutz.base.constant.RedisConstant;
|
||||
import org.nutz.integration.jedis.JedisAgent;
|
||||
import org.nutz.integration.jedis.pubsub.PubSub;
|
||||
import org.nutz.integration.jedis.pubsub.PubSubService;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.plugins.mvc.websocket.AbstractWsEndpoint;
|
||||
import org.nutz.plugins.mvc.websocket.NutWsConfigurator;
|
||||
import org.nutz.plugins.mvc.websocket.WsHandler;
|
||||
import redis.clients.jedis.*;
|
||||
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ServerEndpoint(value = "/websocket", configurator = NutWsConfigurator.class)
|
||||
@IocBean(create = "init") // 使用NutWsConfigurator的必备条件
|
||||
public class WkWebSocket extends AbstractWsEndpoint implements PubSub {
|
||||
protected static final Log log = Logs.get();
|
||||
@Inject
|
||||
protected PubSubService pubSubService;
|
||||
@Inject
|
||||
protected JedisAgent jedisAgent;
|
||||
@Inject("refer:$ioc")
|
||||
protected Ioc ioc;
|
||||
@Inject("java:$conf.getInt('shiro.session.cache.redis.ttl')")
|
||||
private int REDIS_KEY_SESSION_TTL;
|
||||
|
||||
public WsHandler createHandler(Session session, EndpointConfig config) {
|
||||
return ioc.get(WkWsHandler.class);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
roomProvider = new WkJedisRoomProvider(jedisAgent, REDIS_KEY_SESSION_TTL);
|
||||
if (jedisAgent.isClusterMode()) {
|
||||
JedisCluster jedisCluster = jedisAgent.getJedisClusterWrapper().getJedisCluster();
|
||||
List<String> keys=new ArrayList<>();
|
||||
for (JedisPool pool : jedisCluster.getClusterNodes().values()) {
|
||||
try (Jedis jedis = pool.getResource()) {
|
||||
ScanParams match = new ScanParams().match(RedisConstant.REDIS_KEY_WSROOM + "*");
|
||||
ScanResult<String> scan = null;
|
||||
do {
|
||||
scan = jedis.scan(scan == null ? ScanParams.SCAN_POINTER_START : scan.getStringCursor(), match);
|
||||
keys.addAll(scan.getResult());
|
||||
|
||||
} while (!scan.isCompleteIteration());
|
||||
}
|
||||
}
|
||||
try (Jedis jedis = jedisAgent.getResource()) {
|
||||
for (String key : keys) {
|
||||
switch (jedis.type(key)) {
|
||||
case "none":
|
||||
break;
|
||||
case "set":
|
||||
break;
|
||||
default:
|
||||
jedis.del(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try (Jedis jedis = jedisAgent.getResource()) {
|
||||
ScanParams match = new ScanParams().match(RedisConstant.REDIS_KEY_WSROOM + "*");
|
||||
ScanResult<String> scan = null;
|
||||
do {
|
||||
scan = jedis.scan(scan == null ? ScanParams.SCAN_POINTER_START : scan.getStringCursor(), match);
|
||||
for (String key : scan.getResult()) {
|
||||
switch (jedis.type(key)) {
|
||||
case "none":
|
||||
break;
|
||||
case "set":
|
||||
break;
|
||||
default:
|
||||
jedis.del(key);
|
||||
}
|
||||
}
|
||||
} while (!scan.isCompleteIteration());
|
||||
}
|
||||
}
|
||||
pubSubService.reg(RedisConstant.REDIS_KEY_WSROOM + "*", this);
|
||||
}
|
||||
|
||||
|
||||
public void onMessage(String channel, String message) {
|
||||
if (log.isDebugEnabled())
|
||||
log.debugf("GET PubSub channel=%s msg=%s", channel, message);
|
||||
each(channel, (index, session, length) -> session.getAsyncRemote().sendText(message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.v.nutz.web.commons.ext.websocket;
|
||||
|
||||
import io.v.nutz.base.constant.RedisConstant;
|
||||
import io.v.nutz.sys.services.SysMsgService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.plugins.mvc.websocket.handler.SimpleWsHandler;
|
||||
|
||||
@IocBean
|
||||
public class WkWsHandler extends SimpleWsHandler {
|
||||
private static final Log log = Logs.get();
|
||||
@Inject
|
||||
private SysMsgService sysMsgService;
|
||||
|
||||
@Override
|
||||
public void join(NutMap req) {
|
||||
join(req.getString("room"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void left(NutMap req) {
|
||||
left(req.getString("room"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void join(String room) {
|
||||
if (!Strings.isBlank(room)) {
|
||||
room = RedisConstant.REDIS_KEY_WSROOM + room;
|
||||
log.debugf("session(id=%s) join room(name=%s)", session.getId(), room);
|
||||
roomProvider.join(room, session.getId());
|
||||
sysMsgService.getMsg(room.split(":")[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void left(String room) {
|
||||
if (!Strings.isBlank(room)) {
|
||||
room = RedisConstant.REDIS_KEY_WSROOM + room;
|
||||
log.debugf("session(id=%s) left room(name=%s)", session.getId(), room);
|
||||
roomProvider.left(room, session.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void depose() {
|
||||
//覆盖原生写法,因为room= loginname + httpSessionId 和聊天室的机制不一样,不覆盖的话功能会异常
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package io.v.nutz.web.commons.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.base.utils.StringUtil;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import lombok.SneakyThrows;
|
||||
import org.beetl.ext.nutz.BeetlView;
|
||||
import org.beetl.ext.web.WebRender;
|
||||
import org.nutz.integration.shiro.NutShiro;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.json.JsonFormat;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionFilter;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.nutz.mvc.View;
|
||||
import org.nutz.mvc.view.ForwardView;
|
||||
import org.nutz.mvc.view.ServerRedirectView;
|
||||
import org.nutz.mvc.view.UTF8JsonView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* csrf过滤器
|
||||
*/
|
||||
public class CsrfFilter implements ActionFilter {
|
||||
|
||||
protected Boolean check;
|
||||
|
||||
protected String appDoMain = Globals.AppDomain;
|
||||
|
||||
@Override
|
||||
public View match(ActionContext actionContext) {
|
||||
/*if (check == null) {
|
||||
check = actionContext.getIoc().get(PropertiesProxy.class, "conf").getBoolean("website.csrf.enable", true);
|
||||
}
|
||||
if (!check) {
|
||||
return null;
|
||||
}
|
||||
HttpServletRequest request = actionContext.getRequest();
|
||||
String referer = request.getHeader("Referer");
|
||||
|
||||
boolean ajax = NutShiro.isAjax(request);
|
||||
|
||||
String domain = appDoMain.replace("http://", "").replace("https://", "");
|
||||
|
||||
//先判断referer
|
||||
if (StrUtil.isNotBlank(referer) && !referer.contains(domain)) {
|
||||
return new UTF8JsonView(JsonFormat.compact()).setData(Result.error("csrf拦截"));
|
||||
}
|
||||
|
||||
if (request.getMethod().equalsIgnoreCase("post") && StrUtil.isBlank(referer)) {
|
||||
return new UTF8JsonView(JsonFormat.compact()).setData(Result.error("csrf拦截"));
|
||||
}
|
||||
|
||||
if (NutShiro.isAjax(request)) {
|
||||
if (StrUtil.isNotBlank(referer) && !referer.contains(domain)) {
|
||||
return new UTF8JsonView(JsonFormat.compact()).setData(Result.error("csrf拦截"));
|
||||
}
|
||||
}*/
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.v.nutz.web.commons.filter;
|
||||
|
||||
import io.v.nutz.sys.models.Sys_route;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by Wizzer on 2016/7/31.
|
||||
*/
|
||||
public class RouteFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest req2 = (HttpServletRequest) req;
|
||||
HttpServletResponse res2 = (HttpServletResponse) res;
|
||||
res2.setCharacterEncoding("utf-8");
|
||||
req2.setCharacterEncoding("utf-8");
|
||||
Sys_route route = Globals.RouteMap.getAs(Strings.sNull(req2.getRequestURI()).replace(Globals.AppBase, ""), Sys_route.class);
|
||||
if (route != null) {
|
||||
if ("show".equals(route.getType())) {
|
||||
res2.sendRedirect(route.getToUrl());
|
||||
} else {
|
||||
req2.getRequestDispatcher(route.getToUrl()).forward(req2, res2);
|
||||
}
|
||||
} else chain.doFilter(req2, res2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package io.v.nutz.web.commons.filter;
|
||||
|
||||
import org.nutz.boot.AppContext;
|
||||
import org.nutz.boot.starter.WebFilterFace;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@IocBean
|
||||
public class RouteFilterStarter implements WebFilterFace {
|
||||
|
||||
|
||||
@Inject("refer:$ioc")
|
||||
protected Ioc ioc;
|
||||
|
||||
@Inject
|
||||
protected PropertiesProxy conf;
|
||||
|
||||
@Inject
|
||||
protected AppContext appContext;
|
||||
|
||||
public String getName() {
|
||||
return "routeFilterStarter";
|
||||
}
|
||||
|
||||
public String getPathSpec() {
|
||||
return "/*";
|
||||
}
|
||||
|
||||
public EnumSet<DispatcherType> getDispatches() {
|
||||
return EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE);
|
||||
}
|
||||
|
||||
@IocBean(name="routeFilter")
|
||||
public RouteFilter createRouteFilter() {
|
||||
return new RouteFilter();
|
||||
}
|
||||
|
||||
public Filter getFilter() {
|
||||
return ioc.get(RouteFilter.class, "routeFilter");
|
||||
}
|
||||
|
||||
public Map<String, String> getInitParameters() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return 11;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.v.nutz.web.commons.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.nutz.integration.shiro.NutShiro;
|
||||
import org.nutz.json.JsonFormat;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionFilter;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.nutz.mvc.View;
|
||||
import org.nutz.mvc.view.ServerRedirectView;
|
||||
import org.nutz.mvc.view.UTF8JsonView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ssrf过滤器
|
||||
*/
|
||||
@Slf4j
|
||||
public class SsrfFilter implements ActionFilter {
|
||||
|
||||
/**
|
||||
* 违规协议
|
||||
*/
|
||||
private static final List<String> errorPreFixes = List.of("file://", "ftp://", "dict://", "gopher://");
|
||||
|
||||
@Override
|
||||
public View match(ActionContext actionContext) {
|
||||
/*HttpServletRequest req = actionContext.getRequest();
|
||||
|
||||
Iterator<String[]> values = actionContext.getRequest().getParameterMap().values().iterator();
|
||||
|
||||
boolean isError = false;
|
||||
|
||||
String domain = Globals.AppDomain.replace("http://", "").replace("https://", "");
|
||||
|
||||
while (values.hasNext()) {
|
||||
String[] valueArray = values.next();
|
||||
for (String value : valueArray) {
|
||||
String v = value.toLowerCase();
|
||||
if (isHttp(v) && (!v.startsWith("http://" + domain) && !v.startsWith("https://" + domain))) {
|
||||
log.info("ssrf过滤--违规参数{}", v);
|
||||
isError = true;
|
||||
} else if (StrUtil.isNotBlank(v) && errorPreFixes.stream().anyMatch(e -> e.startsWith(v))) {
|
||||
log.info("ssrf过滤--违规参数{}", v);
|
||||
isError = true;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
if (NutShiro.isAjax(req)) {
|
||||
return new UTF8JsonView(JsonFormat.compact()).setData(Result.error("ssrf拦截"));
|
||||
} else {
|
||||
return new ServerRedirectView("/platform/home/403");
|
||||
}
|
||||
}*/
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* isHttp
|
||||
* @param url url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isHttp(String url) {
|
||||
return url.startsWith("http://") || url.startsWith("https://");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
import io.v.nutz.base.utils.DateUtil;
|
||||
import io.v.nutz.base.utils.StringUtil;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionInfo;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.nutz.mvc.NutConfig;
|
||||
import org.nutz.mvc.impl.processor.AbstractProcessor;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2016/6/22.
|
||||
*/
|
||||
public class GlobalsSettingProcessor extends AbstractProcessor {
|
||||
private static ShiroUtil ShiroUtil;
|
||||
private static DateUtil dateUtil;
|
||||
private static StringUtil stringUtil;
|
||||
|
||||
public void init(NutConfig config, ActionInfo ai) throws Throwable {
|
||||
if (ShiroUtil == null) {
|
||||
ShiroUtil = new ShiroUtil();
|
||||
}
|
||||
if (dateUtil == null) {
|
||||
dateUtil = new DateUtil();
|
||||
}
|
||||
if (stringUtil == null) {
|
||||
stringUtil = new StringUtil();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void process(ActionContext ac) throws Throwable {
|
||||
ac.getRequest().setAttribute("AppRoot", Globals.AppRoot);
|
||||
ac.getRequest().setAttribute("AppBase", Globals.AppBase);
|
||||
ac.getRequest().setAttribute("AppName", Globals.AppName);
|
||||
ac.getRequest().setAttribute("AppDomain", Globals.AppDomain);
|
||||
ac.getRequest().setAttribute("AppShrotName", Globals.AppShrotName);
|
||||
ac.getRequest().setAttribute("AppFileDomain", Globals.AppFileDomain);
|
||||
ac.getRequest().setAttribute("config", Globals.MyConfig);
|
||||
ac.getRequest().setAttribute("shiro", ShiroUtil);
|
||||
ac.getRequest().setAttribute("date", dateUtil);
|
||||
ac.getRequest().setAttribute("string", stringUtil);
|
||||
// 如果url中有语言属性则设置
|
||||
String lang = ac.getRequest().getParameter("lang");
|
||||
if (!Strings.isEmpty(lang)) {
|
||||
Mvcs.setLocalizationKey(lang);
|
||||
} else {
|
||||
// Mvcs.getLocalizationKey() 1.r.56 版本是null,所以要做两次判断, 1.r.57已修复为默认值 Nutz:Fix issue 1072
|
||||
lang = Strings.isBlank(Mvcs.getLocalizationKey()) ? Mvcs.getDefaultLocalizationKey() : Mvcs.getLocalizationKey();
|
||||
}
|
||||
ac.getRequest().setAttribute("lang", lang);
|
||||
doNext(ac);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
import org.nutz.lang.Stopwatch;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.impl.processor.AbstractProcessor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Created by Wizzer.cn on 2015/7/2.
|
||||
*/
|
||||
public class LogTimeProcessor extends AbstractProcessor {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
public void process(ActionContext ac) throws Throwable {
|
||||
if (log.isDebugEnabled()) {
|
||||
Stopwatch sw = Stopwatch.begin();
|
||||
try {
|
||||
doNext(ac);
|
||||
} finally {
|
||||
sw.stop();
|
||||
HttpServletRequest req = ac.getRequest();
|
||||
log.debugf("[%-4s]URI=%s %sms", req.getMethod(), req.getRequestURI(), sw.getDuration());
|
||||
}
|
||||
} else {
|
||||
doNext(ac);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.wizzer.framework.base.Result;
|
||||
import io.v.nutz.base.enums.Env;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authz.UnauthenticatedException;
|
||||
import org.apache.shiro.authz.UnauthorizedException;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.nutz.integration.shiro.NutShiro;
|
||||
import org.nutz.integration.shiro.NutShiroInterceptor;
|
||||
import org.nutz.integration.shiro.NutShiroMethodInterceptor;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.mvc.*;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
import org.nutz.mvc.impl.processor.AbstractProcessor;
|
||||
import org.nutz.mvc.view.ServerRedirectView;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2016/6/24.
|
||||
*/
|
||||
public class NutShiroProcessor extends AbstractProcessor {
|
||||
|
||||
protected NutShiroMethodInterceptor interceptor;
|
||||
|
||||
protected String noAuthUri = "/platform/login/noPermission";
|
||||
|
||||
//域名
|
||||
protected String appDomain = Globals.AppDomain;
|
||||
|
||||
//cas认证地址
|
||||
protected String casAddress = Globals.CasAddress;
|
||||
|
||||
protected boolean match;
|
||||
|
||||
protected boolean init;
|
||||
|
||||
public NutShiroProcessor() {
|
||||
interceptor = new NutShiroMethodInterceptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(NutConfig config, ActionInfo ai) throws Throwable {
|
||||
|
||||
// 禁止重复初始化,常见于ioc注入且使用了单例
|
||||
if (init) {
|
||||
throw new IllegalStateException("this Processor have bean inited!!");
|
||||
}
|
||||
super.init(config, ai);
|
||||
match = NutShiro.match(ai.getMethod());
|
||||
init = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(ActionContext ac) throws Throwable {
|
||||
HttpServletRequest request = ac.getRequest();
|
||||
String requestURI = request.getRequestURI();
|
||||
|
||||
//不需要认证授权的url
|
||||
String[] authIgnoreUrlArr = Lang.array("/",
|
||||
"/sso/login",
|
||||
"/platform/login",
|
||||
"/platform/login(/doLogin|/logout|/captcha)",
|
||||
"/platform/qywechat/.*",
|
||||
"/platform/home/(500|403|404|UnknownAccountError|LockedAccountError)",
|
||||
"/mobile/login",
|
||||
"/mobile/login/doLogin",
|
||||
"/platform/jsz/login",
|
||||
"/platform/activity/basic/scope/getScopeUser",
|
||||
"/platform/fitnessWalk/stepManage/getActivityDateStep",
|
||||
"/platform/fitnessWalk/stepManage/winningRecord",
|
||||
"/platform/fitnessWalk/stepManage/updateStepMonth",
|
||||
"/platform/fitnessWalk/stepRanking/getUserStepRanking",
|
||||
"/platform/fitnessWalk/stepManage/getActivityQualifyProgressBar",
|
||||
"/platform/fitnessWalk/stepWining/getUserStepWining",
|
||||
"/platform/fitnessWalk/punchLottery/judgeWinningToPunch",
|
||||
"/platform/fitnessWalk/punchLottery/getLotteryRecord",
|
||||
"/platform/fitnessWalk/punchLottery/doExchange",
|
||||
"/platform/fitnessWalk/punchLottery/getPunchWining",
|
||||
"/platform/fitnessWalk/punchLottery/doReadLottery",
|
||||
"/platform/fitnessWalk/stepWining/prizeOption",
|
||||
"/platform/fitnessWalk/stepWining/prizeUsers"
|
||||
);
|
||||
if (Arrays.stream(authIgnoreUrlArr).noneMatch(v -> Pattern.compile(v).matcher(requestURI).matches())) {
|
||||
if (match) {
|
||||
try {
|
||||
interceptor.assertAuthorized(new NutShiroInterceptor(ac));
|
||||
} catch (Exception e) {
|
||||
whenException(ac, e);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
if (!subject.isAuthenticated()) {
|
||||
goAuth(ac);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
doNext(ac);
|
||||
}
|
||||
|
||||
protected void whenException(ActionContext ac, Exception e) throws Throwable {
|
||||
Object val = ac.getRequest().getAttribute("shiro_auth_error");
|
||||
if (val != null && val instanceof View) {
|
||||
((View) val).render(ac.getRequest(), ac.getResponse(), null);
|
||||
return;
|
||||
}
|
||||
if (e instanceof UnauthenticatedException) {
|
||||
whenUnauthenticated(ac, (UnauthenticatedException) e);
|
||||
} else if (e instanceof UnauthorizedException) {
|
||||
whenUnauthorized(ac, (UnauthorizedException) e);
|
||||
} else {
|
||||
whenOtherException(ac, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void whenUnauthenticated(ActionContext ac, UnauthenticatedException e) throws Exception {
|
||||
if (NutShiro.isAjax(ac.getRequest())) {
|
||||
ac.getResponse().addHeader("loginStatus", "accessDenied");
|
||||
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error("登录失效"));
|
||||
} else {
|
||||
goAuth(ac);
|
||||
// if (isPageRequest(ac)) {
|
||||
// goAuth(ac);
|
||||
// } else {
|
||||
// ac.getResponse().addHeader("loginStatus", "accessDenied");
|
||||
// NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error("没有权限"));
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
protected void whenUnauthorized(ActionContext ac, UnauthorizedException e) throws Exception {
|
||||
if (NutShiro.isAjax(ac.getRequest())) {
|
||||
ac.getResponse().addHeader("loginStatus", "unauthorized");
|
||||
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error("没有权限"));
|
||||
} else {
|
||||
new ServerRedirectView("/error/403.html").render(ac.getRequest(), ac.getResponse(), null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void whenOtherException(ActionContext ac, Exception e) throws Exception {
|
||||
if (NutShiro.isAjax(ac.getRequest())) {
|
||||
ac.getResponse().addHeader("loginStatus", "accessDenied");
|
||||
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error("登录失效"));
|
||||
} else {
|
||||
goAuth(ac);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否访问页面
|
||||
*
|
||||
* @param actionContext action上下文
|
||||
* @return
|
||||
*/
|
||||
protected boolean isPageRequest(ActionContext actionContext) {
|
||||
Method method = actionContext.getMethod();
|
||||
Ok okAnnotation = method.getDeclaredAnnotation(Ok.class);
|
||||
String value = okAnnotation.value();
|
||||
boolean beetl = value.contains("beetl");
|
||||
return beetl;
|
||||
}
|
||||
|
||||
private void goAuth(ActionContext ac) throws Exception {
|
||||
//跳转的页面
|
||||
String redirect = null;
|
||||
|
||||
String userAgent = ac.getRequest().getHeader("user-agent");
|
||||
Assert.notNull(userAgent, "Go to your mother's dead reptile!");
|
||||
|
||||
HttpServletRequest request = ac.getRequest();
|
||||
|
||||
StringBuffer requestURL = request.getRequestURL();
|
||||
String queryString = request.getQueryString();
|
||||
if (StrUtil.isNotBlank(queryString)) {
|
||||
redirect = requestURL.append("?").append(queryString).toString();
|
||||
} else {
|
||||
redirect = requestURL.toString();
|
||||
}
|
||||
|
||||
|
||||
if (Globals.isEnv(Env.prod)) {
|
||||
//生产环境登录地址
|
||||
StringBuffer prodLoginUrl = new StringBuffer();
|
||||
prodLoginUrl.append(appDomain + "/sso/login?redirect=");
|
||||
if (StrUtil.isNotBlank(redirect)) {
|
||||
prodLoginUrl.append(URLEncoder.encode(redirect, Charset.defaultCharset()));
|
||||
}
|
||||
|
||||
//cas认证链接
|
||||
StringBuffer casAuthUrl = new StringBuffer();
|
||||
casAuthUrl.append(casAddress + "/login?service=");
|
||||
casAuthUrl.append(URLEncoder.encode(prodLoginUrl.toString(), Charset.defaultCharset()));
|
||||
// System.out.println(casAuthUrl);
|
||||
new ServerRedirectView(casAuthUrl.toString()).render(ac.getRequest(), ac.getResponse(), null);
|
||||
} else {
|
||||
//开发环境登录地址
|
||||
StringBuffer devLoginUrl = new StringBuffer();
|
||||
devLoginUrl.append(request.getScheme());
|
||||
devLoginUrl.append("://");
|
||||
devLoginUrl.append(request.getServerName());
|
||||
devLoginUrl.append("/platform/login?redirect=");
|
||||
devLoginUrl.append(URLEncoder.encode(redirect, Charset.defaultCharset()));
|
||||
new ServerRedirectView(devLoginUrl.toString()).render(ac.getRequest(), ac.getResponse(), null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
import io.v.nutz.base.result.Result;
|
||||
import org.nutz.integration.shiro.NutShiro;
|
||||
import org.nutz.ioc.IocException;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionInfo;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.nutz.mvc.NutConfig;
|
||||
import org.nutz.mvc.impl.processor.ViewProcessor;
|
||||
import org.nutz.mvc.view.ForwardView;
|
||||
|
||||
public class WkFailProcessor extends ViewProcessor {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
private String errorUri = "/platform/home/500";
|
||||
|
||||
@Override
|
||||
public void init(NutConfig config, ActionInfo ai) throws Throwable {
|
||||
view = evalView(config, ai, ai.getFailView());
|
||||
}
|
||||
|
||||
public void process(ActionContext ac) throws Throwable {
|
||||
if (log.isWarnEnabled()) {
|
||||
String uri = Mvcs.getRequestPath(ac.getRequest());
|
||||
log.warn(String.format("Error@%s :", uri), ac.getError());
|
||||
}
|
||||
if (ac.getError() instanceof IocException) {
|
||||
if (NutShiro.isAjax(ac.getRequest())) {
|
||||
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error(Mvcs.getMessage(ac.getRequest(), "system.exception")));
|
||||
} else {
|
||||
ac.getRequest().setAttribute("original_request_uri",ac.getRequest().getRequestURI());
|
||||
new ForwardView(errorUri).render(ac.getRequest(), ac.getResponse(), Mvcs.getMessage(ac.getRequest(), "system.exception"));
|
||||
}
|
||||
}
|
||||
super.process(ac);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import io.v.nutz.base.result.Result;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.nutz.integration.shiro.NutShiro;
|
||||
import org.nutz.ioc.impl.PropertiesProxy;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionInfo;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.nutz.mvc.NutConfig;
|
||||
import org.nutz.mvc.impl.processor.AbstractProcessor;
|
||||
import org.nutz.mvc.view.ForwardView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SQL XSS拦截
|
||||
* Created by wizzer on 2016/7/1.
|
||||
*/
|
||||
public class XssSqlFilterProcessor extends AbstractProcessor {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
protected String lerrorUri = "/error/403.html";
|
||||
private PropertiesProxy conf;
|
||||
private List<String> ignoreList;
|
||||
|
||||
private final static String regxpForHtml = "<([^>]*)>"; // 过滤所有以<开头以>结尾的标签
|
||||
|
||||
private final static String regxpForImgTag = "<\\s*img\\s+([^>]*)\\s*>"; // 找出IMG标签
|
||||
|
||||
private final static String regxpForImaTagSrcAttrib = "src=\"([^\"]+)\""; // 找出IMG标签的SRC属性
|
||||
|
||||
@Override
|
||||
public void init(NutConfig config, ActionInfo ai) throws Throwable {
|
||||
try {
|
||||
conf = config.getIoc().get(org.nutz.ioc.impl.PropertiesProxy.class, "conf");
|
||||
ignoreList = Arrays.asList(Strings.splitIgnoreBlank(conf.get("xsssql.ignore.urls", "")));
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
public void process(ActionContext ac) throws Throwable {
|
||||
if (checkUrl(ac) && checkParams(ac)) {
|
||||
if (NutShiro.isAjax(ac.getRequest())) {
|
||||
ac.getResponse().addHeader("loginStatus", "paramsDenied");
|
||||
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error(Mvcs.getMessage(ac.getRequest(), "system.paramserror")));
|
||||
} else {
|
||||
new ForwardView(lerrorUri).render(ac.getRequest(), ac.getResponse(), Mvcs.getMessage(ac.getRequest(), "system.paramserror"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
doNext(ac);
|
||||
}
|
||||
|
||||
private boolean checkUrl(ActionContext ac) {
|
||||
String path = ac.getPath();
|
||||
return !ignoreList.contains(path);
|
||||
}
|
||||
|
||||
protected boolean checkParams(ActionContext ac) {
|
||||
HttpServletRequest req = ac.getRequest();
|
||||
Iterator<String[]> values = req.getParameterMap().values().iterator();// 获取所有的表单参数
|
||||
Iterator<String[]> values2 = req.getParameterMap().values().iterator();// 因为是游标所以要重新获取
|
||||
boolean isError = false;
|
||||
|
||||
//use remove -|,
|
||||
String regEx_sql = """
|
||||
and|exec|execute|insert|select|delete|update|count|drop|*|%|chr|mid|master|truncate|
|
||||
char|declare|sitename|net user|xp_cmdshell|or|+|like'|and|exec|execute|insert|create|drop|
|
||||
table|from|grant|group_concat|column_name|
|
||||
information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|*|
|
||||
chr|mid|master|truncate|char|declare|or|--|+|like|//|/|%|#
|
||||
""";
|
||||
regEx_sql = "select|update|and|or|delete|insert|trancate|char|chr|into|substr|ascii|declare|exec|count|master|drop|execute";
|
||||
|
||||
String regEx_xss = "script|iframe|img";
|
||||
//SQL过滤
|
||||
while (values.hasNext()) {
|
||||
String[] valueArray = (String[]) values.next();
|
||||
for (int i = 0; i < valueArray.length; i++) {
|
||||
String value = valueArray[i].toLowerCase();
|
||||
|
||||
//排除掉base64
|
||||
if (value.startsWith("data:image/png;base64,")) {
|
||||
break;
|
||||
}
|
||||
|
||||
//分拆关键字
|
||||
String[] inj_stra = StringUtils.split(regEx_sql, "\\|");
|
||||
for (int j = 0; j < inj_stra.length; j++) {
|
||||
// 判断如果路径参数值中含有关键字则返回true,并且结束循环
|
||||
if (value.contains(inj_stra[j] + " ") || value.contains(" " + inj_stra[j] + " ") || value.contains(" " + inj_stra[j])) {
|
||||
// if (value.contains(inj_stra[j])) {
|
||||
isError = true;
|
||||
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SQL关键字过滤:" + value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isError) {
|
||||
// XSS漏洞过滤
|
||||
while (values2.hasNext()) {
|
||||
String[] valueArray = (String[]) values2.next();
|
||||
for (int i = 0; i < valueArray.length; i++) {
|
||||
String value = valueArray[i].toLowerCase();
|
||||
|
||||
value = HtmlUtil.escape(value);
|
||||
|
||||
if (value.trim().startsWith("<") || value.trim().endsWith(">") ||
|
||||
value.trim().endsWith("/")) {
|
||||
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "XSS关键字过滤(已被我转义):" + value);
|
||||
}
|
||||
|
||||
// 分拆关键字
|
||||
String[] inj_stra = StringUtils.split(regEx_xss, "|");
|
||||
for (int j = 0; j < inj_stra.length; j++) {
|
||||
// 判断如果路径参数值中含有关键字则返回true,并且结束循环
|
||||
if (value.contains("<" + inj_stra[j] + ">")
|
||||
|| value.contains("<" + inj_stra[j])
|
||||
|| value.contains(inj_stra[j] + ">")) {
|
||||
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "XSS关键字过滤:" + value);
|
||||
isError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isError;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
public class authProcessor {
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package io.v.nutz.web.commons.proc;
|
||||
|
||||
import io.v.nutz.base.result.Result;
|
||||
import io.v.nutz.web.commons.security.SecurityUtil;
|
||||
import org.nutz.integration.shiro.NutShiro;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.nutz.mvc.impl.processor.AbstractProcessor;
|
||||
import org.nutz.mvc.view.ForwardView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class ssrfFilterProcessor extends AbstractProcessor {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
protected String lerrorUri = "/error/403.html";
|
||||
|
||||
static ArrayList<String> whiteDomainlists = new ArrayList<>() {{
|
||||
add("zhgh.zjiet.edu.cn");
|
||||
}};
|
||||
|
||||
|
||||
@Override
|
||||
public void process(ActionContext ac) throws Throwable {
|
||||
HttpServletRequest req = ac.getRequest();
|
||||
Iterator<String[]> values = req.getParameterMap().values().iterator();// 获取所有的表单参数
|
||||
|
||||
boolean isError = false;
|
||||
|
||||
|
||||
while (values.hasNext()) {
|
||||
String[] valueArray = (String[]) values.next();
|
||||
for (String value : valueArray) {
|
||||
String v = value.toLowerCase();
|
||||
if (v.startsWith("ftp") || v.startsWith("file") || v.startsWith("dict")
|
||||
|| v.startsWith("gopher")) {
|
||||
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SSRF过滤:" + value);
|
||||
isError = true;
|
||||
break;
|
||||
}
|
||||
if (SecurityUtil.isHttp(v)) {
|
||||
// if (!v.startsWith("http://zhgh.zjiet.edu.cn") && !v.startsWith("https://zhgh.zjiet.edu.cn")) {
|
||||
// log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SSRF过滤:" + value);
|
||||
// isError = true;
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*if (!SecurityUtil.isHttp(value)) {
|
||||
log.debugf("[%-4s]URI=%s %s", req.getMethod(), req.getRequestURI(), "SSRF过滤:" + value);
|
||||
isError = true;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if (isError) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
if (NutShiro.isAjax(ac.getRequest())) {
|
||||
ac.getResponse().addHeader("loginStatus", "paramsDenied");
|
||||
NutShiro.rendAjaxResp(ac.getRequest(), ac.getResponse(), Result.error(Mvcs.getMessage(ac.getRequest(), "system.paramserror")));
|
||||
} else {
|
||||
new ForwardView(lerrorUri).render(ac.getRequest(), ac.getResponse(), Mvcs.getMessage(ac.getRequest(), "system.paramserror"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
doNext(ac);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package io.v.nutz.web.commons.security;
|
||||
|
||||
|
||||
import io.v.nutz.web.commons.security.ssrf.SSRFChecker;
|
||||
import io.v.nutz.web.commons.security.ssrf.SocketHook;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
public class SecurityUtil {
|
||||
|
||||
private static final Pattern FILTER_PATTERN = Pattern.compile("^[a-zA-Z0-9_/\\.-]+$");
|
||||
private static Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the URL starts with HTTP.
|
||||
*
|
||||
* @param url url
|
||||
* @return true or false
|
||||
*/
|
||||
public static boolean isHttp(String url) {
|
||||
return url.startsWith("http://") || url.startsWith("https://");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get http url host.
|
||||
*
|
||||
* @param url url
|
||||
* @return host
|
||||
*/
|
||||
public static String gethost(String url) {
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
return uri.getHost().toLowerCase();
|
||||
} catch (URISyntaxException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 同时支持一级域名和多级域名,相关配置在resources目录下url/url_safe_domain.xml文件。
|
||||
* 优先判断黑名单,如果满足黑名单return null。
|
||||
*
|
||||
* @param url the url need to check
|
||||
* @return Safe url returns original url; Illegal url returns null;
|
||||
*/
|
||||
public static String checkURL(String url) {
|
||||
|
||||
if (null == url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayList<String> safeDomains = new ArrayList<>() {{
|
||||
add("zhgh.zjiet.edu.cn");
|
||||
}};
|
||||
ArrayList<String> blockDomains = new ArrayList<>();
|
||||
|
||||
try {
|
||||
String host = gethost(url);
|
||||
|
||||
// 必须http/https
|
||||
if (!isHttp(url)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果满足黑名单返回null
|
||||
if (blockDomains.contains(host)) {
|
||||
return null;
|
||||
}
|
||||
for (String blockDomain : blockDomains) {
|
||||
if (host.endsWith("." + blockDomain)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 支持多级域名
|
||||
if (safeDomains.contains(host)) {
|
||||
return url;
|
||||
}
|
||||
|
||||
// 支持一级域名
|
||||
for (String safedomain : safeDomains) {
|
||||
if (host.endsWith("." + safedomain)) {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (NullPointerException e) {
|
||||
logger.error(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 通过自定义白名单域名处理SSRF漏洞。如果URL范围收敛,强烈建议使用该方案。
|
||||
* 这是最简单也最有效的修复方式。因为SSRF都是发起URL请求时造成,大多数场景是图片场景,一般图片的域名都是CDN或者OSS等,所以限定域名白名单即可完成SSRF漏洞修复。
|
||||
*
|
||||
* @param url 需要校验的url
|
||||
* @return Safe url returns true. Dangerous url returns false.
|
||||
* @author JoyChou @ 2020-03-30
|
||||
*/
|
||||
public static boolean checkSSRFByWhitehosts(String url) {
|
||||
return SSRFChecker.checkURLFckSSRF(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析URL的IP,判断IP是否是内网IP。如果有重定向跳转,循环解析重定向跳转的IP。不建议使用该方案。
|
||||
* <p>
|
||||
* 存在的问题:
|
||||
* 1、会主动发起请求,可能会有性能问题
|
||||
* 2、设置重定向跳转为第一次302不跳转,第二次302跳转到内网IP 即可绕过该防御方案
|
||||
* 3、TTL设置为0会被绕过
|
||||
*
|
||||
* @param url check的url
|
||||
* @return 安全返回true,危险返回false
|
||||
*/
|
||||
@Deprecated
|
||||
public static boolean checkSSRF(String url) {
|
||||
int checkTimes = 10;
|
||||
return SSRFChecker.checkSSRF(url, checkTimes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 不能使用白名单的情况下建议使用该方案。前提是禁用重定向并且TTL默认不为0。
|
||||
* <p>
|
||||
* 存在问题:
|
||||
* 1、TTL为0会被绕过
|
||||
* 2、使用重定向可绕过
|
||||
*
|
||||
* @param url The url that needs to check.
|
||||
* @return Safe url returns true. Dangerous url returns false.
|
||||
*/
|
||||
public static boolean checkSSRFWithoutRedirect(String url) {
|
||||
if (url == null) {
|
||||
return false;
|
||||
}
|
||||
return !SSRFChecker.isInternalIpByUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check ssrf by hook socket. Start socket hook.
|
||||
*
|
||||
* @author liergou @ 2020-04-04 02:15
|
||||
*/
|
||||
public static void startSSRFHook() throws IOException {
|
||||
SocketHook.startHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close socket hook.
|
||||
*
|
||||
* @author liergou @ 2020-04-04 02:15
|
||||
**/
|
||||
public static void stopSSRFHook() {
|
||||
SocketHook.stopHook();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Filter file path to prevent path traversal vulns.
|
||||
*
|
||||
* @param filepath file path
|
||||
* @return illegal file path return null
|
||||
*/
|
||||
public static String pathFilter(String filepath) {
|
||||
String temp = filepath;
|
||||
|
||||
// use while to sovle multi urlencode
|
||||
while (temp.indexOf('%') != -1) {
|
||||
try {
|
||||
temp = URLDecoder.decode(temp, "utf-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
logger.info("Unsupported encoding exception: " + filepath);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.info(e.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (temp.contains("..") || temp.charAt(0) == '/') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return filepath;
|
||||
}
|
||||
|
||||
|
||||
public static String cmdFilter(String input) {
|
||||
if (!FILTER_PATTERN.matcher(input).matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 过滤mybatis中order by不能用#的情况。
|
||||
* 严格限制用户输入只能包含<code>a-zA-Z0-9_-.</code>字符。
|
||||
*
|
||||
* @param sql sql
|
||||
* @return 安全sql,否则返回null
|
||||
*/
|
||||
public static String sqlFilter(String sql) {
|
||||
if (!FILTER_PATTERN.matcher(sql).matches()) {
|
||||
return null;
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将非<code>0-9a-zA-Z/-.</code>的字符替换为空
|
||||
*
|
||||
* @param str 字符串
|
||||
* @return 被过滤的字符串
|
||||
*/
|
||||
public static String replaceSpecialStr(String str) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
str = str.toLowerCase();
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char ch = str.charAt(i);
|
||||
// 如果是0-9
|
||||
if (ch >= 48 && ch <= 57) {
|
||||
sb.append(ch);
|
||||
}
|
||||
// 如果是a-z
|
||||
else if (ch >= 97 && ch <= 122) {
|
||||
sb.append(ch);
|
||||
} else if (ch == '/' || ch == '.' || ch == '-') {
|
||||
sb.append(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package io.v.nutz.web.commons.security.ssrf;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.v.nutz.web.commons.security.SecurityUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.net.util.SubnetUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
public class SSRFChecker {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(SSRFChecker.class);
|
||||
|
||||
public static boolean checkURLFckSSRF(String url) {
|
||||
if (null == url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArrayList<String> ssrfSafeDomains = new ArrayList<>() {{
|
||||
add("zhgh.hnu.edu.cn");
|
||||
}};
|
||||
try {
|
||||
String host = SecurityUtil.gethost(url);
|
||||
|
||||
// 必须http/https
|
||||
if (!SecurityUtil.isHttp(url)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ssrfSafeDomains.contains(host)) {
|
||||
return true;
|
||||
}
|
||||
for (String ssrfSafeDomain : ssrfSafeDomains) {
|
||||
if (host.endsWith("." + ssrfSafeDomain)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.toString());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析url的ip,判断ip是否是内网ip,所以TTL设置为0的情况不适用。
|
||||
* url只允许https或者http,并且设置默认连接超时时间。
|
||||
* 该修复方案会主动请求重定向后的链接。
|
||||
*
|
||||
* @param url check的url
|
||||
* @param checkTimes 设置重定向检测的最大次数,建议设置为10次
|
||||
* @return 安全返回true,危险返回false
|
||||
*/
|
||||
public static boolean checkSSRF(String url, int checkTimes) {
|
||||
|
||||
HttpURLConnection connection;
|
||||
int connectTime = 5 * 1000; // 设置连接超时时间5s
|
||||
int i = 1;
|
||||
String finalUrl = url;
|
||||
try {
|
||||
do {
|
||||
// 判断当前请求的URL是否是内网ip
|
||||
if (isInternalIpByUrl(finalUrl)) {
|
||||
logger.error("[-] SSRF check failed. Dangerous url: " + finalUrl);
|
||||
return false; // 内网ip直接return,非内网ip继续判断是否有重定向
|
||||
}
|
||||
|
||||
connection = (HttpURLConnection) new URL(finalUrl).openConnection();
|
||||
connection.setInstanceFollowRedirects(false);
|
||||
connection.setUseCaches(false); // 设置为false,手动处理跳转,可以拿到每个跳转的URL
|
||||
connection.setConnectTimeout(connectTime);
|
||||
//connection.setRequestMethod("GET");
|
||||
connection.connect(); // send dns request
|
||||
int responseCode = connection.getResponseCode(); // 发起网络请求
|
||||
if (responseCode >= 300 && responseCode <= 307 && responseCode != 304 && responseCode != 306) {
|
||||
String redirectedUrl = connection.getHeaderField("Location");
|
||||
if (null == redirectedUrl)
|
||||
break;
|
||||
finalUrl = redirectedUrl;
|
||||
i += 1; // 重定向次数加1
|
||||
logger.info("redirected url: " + finalUrl);
|
||||
if (i == checkTimes) {
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
break;
|
||||
} while (connection.getResponseCode() != HttpURLConnection.HTTP_OK);
|
||||
connection.disconnect();
|
||||
} catch (Exception e) {
|
||||
return true; // 如果异常了,认为是安全的,防止是超时导致的异常而验证不成功。
|
||||
}
|
||||
return true; // 默认返回true
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断一个URL的IP是否是内网IP
|
||||
*
|
||||
* @return 如果是内网IP,返回true;非内网IP,返回false。
|
||||
*/
|
||||
public static boolean isInternalIpByUrl(String url) {
|
||||
|
||||
String host = url2host(url);
|
||||
if (host.equals("")) {
|
||||
return true; // 异常URL当成内网IP等非法URL处理
|
||||
}
|
||||
|
||||
String ip = host2ip(host);
|
||||
if (ip.equals("")) {
|
||||
return true; // 如果域名转换为IP异常,则认为是非法URL
|
||||
}
|
||||
|
||||
return isInternalIp(ip);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用SubnetUtils库判断ip是否在内网网段
|
||||
*
|
||||
* @param strIP ip字符串
|
||||
* @return 如果是内网ip,返回true,否则返回false。
|
||||
*/
|
||||
static boolean isInternalIp(String strIP) {
|
||||
if (StringUtils.isEmpty(strIP)) {
|
||||
logger.error("[-] SSRF check failed. IP is empty. " + strIP);
|
||||
return true;
|
||||
}
|
||||
|
||||
ArrayList<String> blackSubnets = new ArrayList<>();
|
||||
for (String subnet : blackSubnets) {
|
||||
SubnetUtils utils = new SubnetUtils(subnet);
|
||||
if (utils.getInfo().isInRange(strIP)) {
|
||||
logger.error("[-] SSRF check failed. Internal IP: " + strIP);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* host转换为IP
|
||||
* 会将各种进制的ip转为正常ip
|
||||
* 167772161 转换为 10.0.0.1
|
||||
* 127.0.0.1.xip.io 转换为 127.0.0.1
|
||||
*
|
||||
* @param host 域名host
|
||||
*/
|
||||
private static String host2ip(String host) {
|
||||
try {
|
||||
InetAddress IpAddress = InetAddress.getByName(host); // send dns request
|
||||
return IpAddress.getHostAddress();
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从URL中获取host,限制为http/https协议。只支持http:// 和 https://,不支持//的http协议。
|
||||
*
|
||||
* @param url http的url
|
||||
*/
|
||||
private static String url2host(String url) {
|
||||
try {
|
||||
// 使用URI,而非URL,防止被绕过。
|
||||
URI u = new URI(url);
|
||||
if (SecurityUtil.isHttp(url)) {
|
||||
return u.getHost();
|
||||
}
|
||||
return "";
|
||||
} catch (Exception e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.v.nutz.web.commons.security.ssrf;
|
||||
|
||||
|
||||
/**
|
||||
* SSRFException
|
||||
*
|
||||
* @author JoyChou @2020-04-04
|
||||
*/
|
||||
public class SSRFException extends RuntimeException {
|
||||
|
||||
SSRFException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.v.nutz.web.commons.security.ssrf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
|
||||
|
||||
/**
|
||||
* Socket Hook switch
|
||||
*
|
||||
* @author liergou @ 2020-04-04 02:12
|
||||
*/
|
||||
public class SocketHook {
|
||||
|
||||
public static void startHook() throws IOException {
|
||||
SocketHookFactory.initSocket();
|
||||
SocketHookFactory.setHook(true);
|
||||
try{
|
||||
Socket.setSocketImplFactory(new SocketHookFactory());
|
||||
}catch (SocketException ignored){
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopHook(){
|
||||
SocketHookFactory.setHook(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package io.v.nutz.web.commons.security.ssrf;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketImpl;
|
||||
import java.net.SocketImplFactory;
|
||||
|
||||
|
||||
/**
|
||||
* socket factory impl
|
||||
*
|
||||
* @author liergou @ 2020-04-03 23:41
|
||||
*/
|
||||
public class SocketHookFactory implements SocketImplFactory {
|
||||
|
||||
|
||||
private static Boolean isHook = false;
|
||||
private static Constructor socketConstructor = null;
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
/**
|
||||
* @param set hook switch
|
||||
*/
|
||||
static void setHook(Boolean set) {
|
||||
isHook = set;
|
||||
}
|
||||
|
||||
|
||||
static void initSocket() {
|
||||
|
||||
if (socketConstructor != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Socket socket = new Socket();
|
||||
try {
|
||||
// get impl field in Socket class
|
||||
Field implField = Socket.class.getDeclaredField("impl");
|
||||
implField.setAccessible(true);
|
||||
Class<?> clazz = implField.get(socket).getClass();
|
||||
|
||||
SocketHookImpl.initSocketImpl(clazz);
|
||||
socketConstructor = clazz.getDeclaredConstructor();
|
||||
socketConstructor.setAccessible(true);
|
||||
|
||||
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
|
||||
throw new SSRFException("SocketHookFactory init failed!");
|
||||
}
|
||||
|
||||
try {
|
||||
socket.close();
|
||||
} catch (IOException ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public SocketImpl createSocketImpl() {
|
||||
|
||||
if (isHook) {
|
||||
try {
|
||||
return new SocketHookImpl(socketConstructor);
|
||||
} catch (Exception e) {
|
||||
logger.error("Socket hook failed!");
|
||||
try {
|
||||
return (SocketImpl) socketConstructor.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return (SocketImpl) socketConstructor.newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
logger.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package io.v.nutz.web.commons.security.ssrf;
|
||||
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.*;
|
||||
|
||||
|
||||
/**
|
||||
* Socket impl
|
||||
*
|
||||
* @author liergou @ 2020-04-02 23:39
|
||||
*/
|
||||
public class SocketHookImpl extends SocketImpl implements SocketOptions {
|
||||
|
||||
private static Boolean isInit = false;
|
||||
|
||||
private static SocketImpl socketImpl = null;
|
||||
private static Method createImpl;
|
||||
private static Method connectHostImpl;
|
||||
private static Method connectInetAddressImpl;
|
||||
private static Method connectSocketAddressImpl;
|
||||
private static Method bindImpl;
|
||||
private static Method listenImpl;
|
||||
private static Method acceptImpl;
|
||||
private static Method getInputStreamImpl;
|
||||
private static Method getOutputStreamImpl;
|
||||
private static Method availableImpl;
|
||||
private static Method closeImpl;
|
||||
private static Method shutdownInputImpl;
|
||||
private static Method shutdownOutputImpl;
|
||||
private static Method sendUrgentDataImpl;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
|
||||
SocketHookImpl(Constructor socketConstructor) throws IllegalAccessException,
|
||||
InvocationTargetException, InstantiationException {
|
||||
socketImpl = (SocketImpl) socketConstructor.newInstance();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Init reflect method.
|
||||
*
|
||||
* @author liergou
|
||||
*/
|
||||
static void initSocketImpl(Class<?> initSocketImpl) {
|
||||
|
||||
if (initSocketImpl == null) {
|
||||
SocketHookFactory.setHook(false);
|
||||
throw new RuntimeException("InitSocketImpl failed! Hook stopped!");
|
||||
}
|
||||
|
||||
if (!isInit) {
|
||||
createImpl = SocketHookUtils.findMethod(initSocketImpl, "create", new Class<?>[]{boolean.class});
|
||||
connectHostImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class<?>[]{String.class, int.class});
|
||||
connectInetAddressImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class<?>[]{InetAddress.class, int.class});
|
||||
connectSocketAddressImpl = SocketHookUtils.findMethod(initSocketImpl, "connect", new Class<?>[]{SocketAddress.class, int.class});
|
||||
bindImpl = SocketHookUtils.findMethod(initSocketImpl, "bind", new Class<?>[]{InetAddress.class, int.class});
|
||||
listenImpl = SocketHookUtils.findMethod(initSocketImpl, "listen", new Class<?>[]{int.class});
|
||||
acceptImpl = SocketHookUtils.findMethod(initSocketImpl, "accept", new Class<?>[]{SocketImpl.class});
|
||||
getInputStreamImpl = SocketHookUtils.findMethod(initSocketImpl, "getInputStream", new Class<?>[]{});
|
||||
getOutputStreamImpl = SocketHookUtils.findMethod(initSocketImpl, "getOutputStream", new Class<?>[]{});
|
||||
availableImpl = SocketHookUtils.findMethod(initSocketImpl, "available", new Class<?>[]{});
|
||||
closeImpl = SocketHookUtils.findMethod(initSocketImpl, "close", new Class<?>[]{});
|
||||
shutdownInputImpl = SocketHookUtils.findMethod(initSocketImpl, "shutdownInput", new Class<?>[]{});
|
||||
shutdownOutputImpl = SocketHookUtils.findMethod(initSocketImpl, "shutdownOutput", new Class<?>[]{});
|
||||
sendUrgentDataImpl = SocketHookUtils.findMethod(initSocketImpl, "sendUrgantData", new Class<?>[]{int.class});
|
||||
isInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* socket base method impl
|
||||
*/
|
||||
@Override
|
||||
protected void create(boolean stream) {
|
||||
try {
|
||||
createImpl.invoke(socketImpl, stream);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void connect(String host, int port) {
|
||||
logger.info("host: " + host + "\tport: " + port);
|
||||
try {
|
||||
connectHostImpl.invoke(socketImpl, host, port);
|
||||
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void connect(InetAddress address, int port) {
|
||||
|
||||
logger.info("InetAddress: " + address.toString());
|
||||
|
||||
try {
|
||||
if (SSRFChecker.isInternalIp(address.getHostAddress())) {
|
||||
throw new RuntimeException("Socket SSRF check failed. InetAddress:" + address.toString());
|
||||
}
|
||||
connectInetAddressImpl.invoke(socketImpl, address, port);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void connect(SocketAddress address, int timeout) {
|
||||
|
||||
// convert SocketAddress to InetSocketAddress
|
||||
InetSocketAddress addr = (InetSocketAddress) address;
|
||||
|
||||
String ip = addr.getAddress().getHostAddress();
|
||||
String host = addr.getHostName();
|
||||
logger.info(String.format("[+] SocketAddress address's Hostname: %s IP: %s", host, ip));
|
||||
|
||||
try {
|
||||
if (SSRFChecker.isInternalIp(ip)) {
|
||||
throw new SSRFException(String.format("[-] SSRF check failed. Hostname: %s IP: %s", host, ip));
|
||||
}
|
||||
connectSocketAddressImpl.invoke(socketImpl, address, timeout);
|
||||
} catch (IllegalAccessException | IllegalArgumentException |
|
||||
InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bind(InetAddress host, int port) {
|
||||
try {
|
||||
bindImpl.invoke(socketImpl, host, port);
|
||||
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void listen(int backlog) {
|
||||
|
||||
try {
|
||||
listenImpl.invoke(socketImpl, backlog);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void accept(SocketImpl s) {
|
||||
|
||||
try {
|
||||
acceptImpl.invoke(socketImpl, s);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream getInputStream() {
|
||||
InputStream inStream = null;
|
||||
|
||||
try {
|
||||
inStream = (InputStream) getInputStreamImpl.invoke(socketImpl);
|
||||
} catch (ClassCastException | InvocationTargetException |
|
||||
IllegalArgumentException | IllegalAccessException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
return inStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getOutputStream() {
|
||||
OutputStream outStream = null;
|
||||
|
||||
try {
|
||||
outStream = (OutputStream) getOutputStreamImpl.invoke(socketImpl);
|
||||
} catch (ClassCastException | IllegalArgumentException |
|
||||
IllegalAccessException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
return outStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int available() {
|
||||
|
||||
int result = -1;
|
||||
|
||||
try {
|
||||
result = (Integer) availableImpl.invoke(socketImpl);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close() {
|
||||
try {
|
||||
closeImpl.invoke(socketImpl);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownInput() {
|
||||
try {
|
||||
shutdownInputImpl.invoke(socketImpl);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdownOutput() {
|
||||
try {
|
||||
shutdownOutputImpl.invoke(socketImpl);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendUrgentData(int data) {
|
||||
try {
|
||||
sendUrgentDataImpl.invoke(socketImpl, data);
|
||||
} catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException ex) {
|
||||
logger.error(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void setOption(int optID, Object value) throws SocketException {
|
||||
if (null != socketImpl) {
|
||||
socketImpl.setOption(optID, value);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getOption(int optID) throws SocketException {
|
||||
return socketImpl.getOption(optID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Dont impl other child method now. Don't be sure where will use it.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.v.nutz.web.commons.security.ssrf;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
class SocketHookUtils {
|
||||
|
||||
/**
|
||||
* Poll the parent class to find the reflection method.
|
||||
* SocksSocketImpl -> PlainSocketImpl -> AbstractPlainSocketImpl
|
||||
*
|
||||
* @author liergou @2020-04-04 01:43
|
||||
*/
|
||||
static Method findMethod(Class<?> clazz, String findName, Class<?>[] args) {
|
||||
|
||||
while (clazz != null) {
|
||||
try {
|
||||
Method method = clazz.getDeclaredMethod(findName, args);
|
||||
method.setAccessible(true);
|
||||
return method;
|
||||
} catch (NoSuchMethodException e) {
|
||||
clazz = clazz.getSuperclass();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.v.nutz.web.commons.shiro.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.v.nutz.base.utils.LoginUtil;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class EasyCredentialsMatch extends HashedCredentialsMatcher {
|
||||
|
||||
/**
|
||||
* 重写方法
|
||||
* 区分 密码登录和非密码登录
|
||||
*
|
||||
* @param token
|
||||
* @param info
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
|
||||
PlatformCaptchaToken platformCaptchaToken = (PlatformCaptchaToken) token;
|
||||
LoginUtil.LoginOrigin loginOrigin = platformCaptchaToken.getLoginOrigin();
|
||||
|
||||
//cas和企业微信实现免密登录
|
||||
if (loginOrigin.equals(LoginUtil.LoginOrigin.CAS) || loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String universalPassword = StrUtil.blankToDefault(Globals.MyConfig.getString("UniversalPassword"), "1");
|
||||
|
||||
if (Arrays.equals(platformCaptchaToken.getPassword(), universalPassword.toCharArray())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Object tokenHashedCredentials = hashProvidedCredentials(token, info);
|
||||
Object accountCredentials = getCredentials(info);
|
||||
return equals(tokenHashedCredentials, accountCredentials);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.v.nutz.web.commons.shiro.exception;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/1/10.
|
||||
*/
|
||||
public class CaptchaEmptyException extends AuthenticationException {
|
||||
|
||||
public CaptchaEmptyException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CaptchaEmptyException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CaptchaEmptyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CaptchaEmptyException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.v.nutz.web.commons.shiro.exception;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/1/10.
|
||||
*/
|
||||
public class CaptchaIncorrectException extends AuthenticationException {
|
||||
|
||||
public CaptchaIncorrectException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CaptchaIncorrectException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CaptchaIncorrectException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CaptchaIncorrectException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package io.v.nutz.web.commons.shiro.filter;
|
||||
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
|
||||
import org.jasig.cas.client.authentication.GatewayResolver;
|
||||
import org.jasig.cas.client.util.AbstractCasFilter;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionFilter;
|
||||
import org.nutz.mvc.View;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
|
||||
@IocBean(name = "AuthenticationFilter")
|
||||
public class AuthenticationFilter extends AbstractCasFilter implements ActionFilter {
|
||||
|
||||
private String serverName = Globals.AppDomain;
|
||||
|
||||
private String loginServer = Globals.CasAddress;
|
||||
|
||||
private boolean renew = false;
|
||||
private boolean gateway = false;
|
||||
|
||||
private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
|
||||
|
||||
public AuthenticationFilter() {
|
||||
setCasServerLoginUrl(loginServer+"/login");
|
||||
if (serverName != null) {
|
||||
super.setServerName(serverName);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest request = (HttpServletRequest)servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse)servletResponse;
|
||||
HttpSession session = request.getSession(false);
|
||||
Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
|
||||
if (assertion != null) {
|
||||
filterChain.doFilter(request, response);
|
||||
} else {
|
||||
String serviceUrl = this.constructServiceUrl(request, response);
|
||||
String ticket = CommonUtils.safeGetParameter(request, this.getArtifactParameterName());
|
||||
boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
|
||||
if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
|
||||
this.log.debug("no ticket and no assertion found");
|
||||
String modifiedServiceUrl;
|
||||
if (this.gateway) {
|
||||
this.log.debug("setting gateway attribute in session");
|
||||
modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
|
||||
} else {
|
||||
modifiedServiceUrl = serviceUrl;
|
||||
}
|
||||
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("Constructed service url: " + modifiedServiceUrl);
|
||||
}
|
||||
|
||||
String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
|
||||
if (this.log.isDebugEnabled()) {
|
||||
this.log.debug("redirecting to \"" + urlToRedirectTo + "\"");
|
||||
}
|
||||
|
||||
response.sendRedirect(urlToRedirectTo);
|
||||
} else {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL to the CAS Server login.
|
||||
*/
|
||||
private String casServerLoginUrl;
|
||||
|
||||
public final void setCasServerLoginUrl(final String casServerLoginUrl) {
|
||||
this.casServerLoginUrl = casServerLoginUrl;
|
||||
}
|
||||
|
||||
public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
|
||||
this.gatewayStorage = gatewayStorage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View match(ActionContext actionContext) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package io.v.nutz.web.commons.shiro.filter;
|
||||
|
||||
import io.v.nutz.base.utils.SkipCertificateValidation;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import org.jasig.cas.client.proxy.AbstractEncryptedProxyGrantingTicketStorageImpl;
|
||||
import org.jasig.cas.client.proxy.CleanUpTimerTask;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorageImpl;
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
import org.jasig.cas.client.util.ReflectUtils;
|
||||
import org.jasig.cas.client.validation.AbstractTicketValidationFilter;
|
||||
import org.jasig.cas.client.validation.Cas20ServiceTicketValidator;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
@IocBean(name = "Cas20ProxyReceivingTicketValidationFilter")
|
||||
public class Cas20ProxyReceivingTicketValidationFilter extends AbstractTicketValidationFilter {
|
||||
private static final String[] RESERVED_INIT_PARAMS = new String[]{"proxyGrantingTicketStorageClass", "proxyReceptorUrl", "acceptAnyProxy", "allowedProxyChains", "casServerUrlPrefix", "proxyCallbackUrl", "renew", "exceptionOnValidationFailure", "redirectAfterValidation", "useSession", "serverName", "service", "artifactParameterName", "serviceParameterName", "encodeServiceUrl", "millisBetweenCleanUps"};
|
||||
private static final int DEFAULT_MILLIS_BETWEEN_CLEANUPS = 60000;
|
||||
private String proxyReceptorUrl;
|
||||
private Timer timer;
|
||||
private TimerTask timerTask;
|
||||
private int millisBetweenCleanUps;
|
||||
private ProxyGrantingTicketStorage proxyGrantingTicketStorage = new ProxyGrantingTicketStorageImpl();
|
||||
|
||||
public Cas20ProxyReceivingTicketValidationFilter() {
|
||||
super.setServerName(Globals.AppDomain);
|
||||
super.setTicketValidator(getTicketValidator());
|
||||
}
|
||||
|
||||
protected void initInternal(FilterConfig filterConfig) throws ServletException {
|
||||
this.setProxyReceptorUrl(this.getPropertyFromInitParams(filterConfig, "proxyReceptorUrl", (String) null));
|
||||
String proxyGrantingTicketStorageClass = this.getPropertyFromInitParams(filterConfig, "proxyGrantingTicketStorageClass", (String) null);
|
||||
if (proxyGrantingTicketStorageClass != null) {
|
||||
this.proxyGrantingTicketStorage = (ProxyGrantingTicketStorage) ReflectUtils.newInstance(proxyGrantingTicketStorageClass, new Object[0]);
|
||||
if (this.proxyGrantingTicketStorage instanceof AbstractEncryptedProxyGrantingTicketStorageImpl) {
|
||||
AbstractEncryptedProxyGrantingTicketStorageImpl p = (AbstractEncryptedProxyGrantingTicketStorageImpl) this.proxyGrantingTicketStorage;
|
||||
String cipherAlgorithm = this.getPropertyFromInitParams(filterConfig, "cipherAlgorithm", "DESede");
|
||||
String secretKey = this.getPropertyFromInitParams(filterConfig, "secretKey", (String) null);
|
||||
p.setCipherAlgorithm(cipherAlgorithm);
|
||||
|
||||
try {
|
||||
if (secretKey != null) {
|
||||
p.setSecretKey(secretKey);
|
||||
}
|
||||
} catch (Exception var7) {
|
||||
throw new RuntimeException(var7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.log.trace("Setting proxyReceptorUrl parameter: " + this.proxyReceptorUrl);
|
||||
this.millisBetweenCleanUps = Integer.parseInt(this.getPropertyFromInitParams(filterConfig, "millisBetweenCleanUps", Integer.toString(60000)));
|
||||
super.initInternal(filterConfig);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
super.init();
|
||||
CommonUtils.assertNotNull(this.proxyGrantingTicketStorage, "proxyGrantingTicketStorage cannot be null.");
|
||||
if (this.timer == null) {
|
||||
this.timer = new Timer(true);
|
||||
}
|
||||
|
||||
if (this.timerTask == null) {
|
||||
this.timerTask = new CleanUpTimerTask(this.proxyGrantingTicketStorage);
|
||||
}
|
||||
|
||||
this.timer.schedule(this.timerTask, (long) this.millisBetweenCleanUps, (long) this.millisBetweenCleanUps);
|
||||
}
|
||||
|
||||
protected final TicketValidator getTicketValidator() {
|
||||
final String casServerUrlPrefix = Globals.CasAddress;
|
||||
|
||||
final Cas20ServiceTicketValidator validator;
|
||||
|
||||
validator = new Cas20ServiceTicketValidator(casServerUrlPrefix);
|
||||
validator.setRenew(false);
|
||||
validator.setEncoding("UTF-8");
|
||||
|
||||
final Map<String, String> additionalParameters = new HashMap<String, String>();
|
||||
final List<String> params = Arrays.asList(RESERVED_INIT_PARAMS);
|
||||
|
||||
validator.setCustomParameters(additionalParameters);
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
this.timer.cancel();
|
||||
}
|
||||
|
||||
protected final boolean preFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
try {
|
||||
SkipCertificateValidation.ignoreSsl();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
String requestUri = request.getRequestURI();
|
||||
if (!CommonUtils.isEmpty(this.proxyReceptorUrl) && requestUri.endsWith(this.proxyReceptorUrl)) {
|
||||
try {
|
||||
CommonUtils.readAndRespondToProxyReceptorRequest(request, response, this.proxyGrantingTicketStorage);
|
||||
return false;
|
||||
} catch (RuntimeException var8) {
|
||||
this.log.error(var8.getMessage(), var8);
|
||||
throw var8;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public final void setProxyReceptorUrl(String proxyReceptorUrl) {
|
||||
this.proxyReceptorUrl = proxyReceptorUrl;
|
||||
}
|
||||
|
||||
public void setProxyGrantingTicketStorage(ProxyGrantingTicketStorage storage) {
|
||||
this.proxyGrantingTicketStorage = storage;
|
||||
}
|
||||
|
||||
public void setTimer(Timer timer) {
|
||||
this.timer = timer;
|
||||
}
|
||||
|
||||
public void setTimerTask(TimerTask timerTask) {
|
||||
this.timerTask = timerTask;
|
||||
}
|
||||
|
||||
public void setMillisBetweenCleanUps(int millisBetweenCleanUps) {
|
||||
this.millisBetweenCleanUps = millisBetweenCleanUps;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package io.v.nutz.web.commons.shiro.filter;
|
||||
|
||||
|
||||
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
|
||||
import org.apache.shiro.web.util.WebUtils;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionFilter;
|
||||
import org.nutz.mvc.View;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/1/10.
|
||||
*/
|
||||
@IocBean(name = "platformAuthc")
|
||||
public class PlatformAuthenticationFilter extends FormAuthenticationFilter implements ActionFilter {
|
||||
private final static Log log = Logs.get();
|
||||
private String captchaParam = "platformCaptcha";
|
||||
private String captchaKey = "platformKey";
|
||||
|
||||
public String getCaptchaParam() {
|
||||
return captchaParam;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return captchaKey;
|
||||
}
|
||||
|
||||
protected String getCaptcha(ServletRequest request) {
|
||||
return WebUtils.getCleanParam(request, getCaptchaParam());
|
||||
}
|
||||
|
||||
protected String getKey(ServletRequest request) {
|
||||
return WebUtils.getCleanParam(request, getKey());
|
||||
}
|
||||
|
||||
protected AuthenticationToken createToken(HttpServletRequest request) {
|
||||
String username = getUsername(request);
|
||||
String password = getPassword(request);
|
||||
String captcha = getCaptcha(request);
|
||||
String key = getKey(request);
|
||||
boolean rememberMe = isRememberMe(request);
|
||||
String host = getHost(request);
|
||||
return new PlatformCaptchaToken(username, password, rememberMe, host, captcha, key);
|
||||
}
|
||||
|
||||
public View match(ActionContext actionContext) {
|
||||
HttpServletRequest request = actionContext.getRequest();
|
||||
AuthenticationToken authenticationToken = createToken(request);
|
||||
request.setAttribute("platformLoginToken", authenticationToken);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package io.v.nutz.web.commons.shiro.filter;
|
||||
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
@IocBean(name = "TransNameFilter")
|
||||
public class TransNameFilter extends HttpServlet implements Filter {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public TransNameFilter() {
|
||||
}
|
||||
|
||||
public void init(FilterConfig arg0) throws ServletException {
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletResponse res = (HttpServletResponse) response;
|
||||
res.setHeader("P3P", "CP=CAO PSA OUR");
|
||||
if (chain != null) {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.v.nutz.web.commons.shiro.filter;
|
||||
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.ActionContext;
|
||||
import org.nutz.mvc.ActionFilter;
|
||||
import org.nutz.mvc.View;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @Author JyuHsin
|
||||
* @Date 2022/4/26
|
||||
* @Description
|
||||
*/
|
||||
@IocBean(name = "weChatFilter")
|
||||
public class WeChatFilter extends FormAuthenticationFilter implements ActionFilter {
|
||||
|
||||
@Override
|
||||
public View match(ActionContext actionContext) {
|
||||
HttpServletRequest request = actionContext.getRequest();
|
||||
AuthenticationToken authenticationToken = createToken(request);
|
||||
request.setAttribute("platformLoginToken", authenticationToken);
|
||||
return null;
|
||||
}
|
||||
|
||||
protected AuthenticationToken createToken(HttpServletRequest request) {
|
||||
Sys_user user = (Sys_user) request.getSession().getAttribute("user");
|
||||
String username = user.getLoginname();
|
||||
String password = "dd3s@#2022";
|
||||
boolean rememberMe = isRememberMe(request);
|
||||
String host = getHost(request);
|
||||
return new PlatformCaptchaToken(username, password, rememberMe, host, "", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.v.nutz.web.commons.shiro.listener;
|
||||
|
||||
import io.v.nutz.sys.services.SysUserService;
|
||||
import io.v.nutz.web.commons.base.Globals;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.session.SessionListener;
|
||||
import org.nutz.dao.Chain;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Strings;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2018/7/5.
|
||||
*/
|
||||
@IocBean
|
||||
public class WebSessionListener implements SessionListener {
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@Override
|
||||
public void onStart(Session session) {//会话创建触发 已进入shiro过滤器的会话就触发这个方法
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(Session session) {//退出
|
||||
if ("true".equals(Globals.MyConfig.getOrDefault("SessionOnlyOne", "false"))) {
|
||||
if (Strings.isNotBlank(Strings.sNull(session.getAttribute("platform_loginname")))) {
|
||||
//这里不能使用StringUtil.getPlatformLoginname 方法,因为那会创建新的会话
|
||||
sysUserService.update(Chain.make("userOnline", false), Cnd.where("loginname", "=", Strings.sNull(session.getAttribute("platform_loginname"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExpiration(Session session) {//会话过期时触发
|
||||
if ("true".equals(Globals.MyConfig.getOrDefault("SessionOnlyOne", "false"))) {
|
||||
if (Strings.isNotBlank(Strings.sNull(session.getAttribute("platform_loginname")))) {
|
||||
//这里不能使用StringUtil.getPlatformLoginname 方法,因为那会创建新的会话
|
||||
sysUserService.update(Chain.make("userOnline", false), Cnd.where("loginname", "=", Strings.sNull(session.getAttribute("platform_loginname"))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.v.nutz.web.commons.shiro.pam;
|
||||
|
||||
import io.v.nutz.web.commons.shiro.exception.CaptchaEmptyException;
|
||||
import io.v.nutz.web.commons.shiro.exception.CaptchaIncorrectException;
|
||||
import org.apache.shiro.authc.*;
|
||||
import org.apache.shiro.authc.pam.AbstractAuthenticationStrategy;
|
||||
import org.apache.shiro.realm.Realm;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@IocBean(name = "authenticationStrategy")
|
||||
public class AnySuccessfulStrategy extends AbstractAuthenticationStrategy {
|
||||
|
||||
/**
|
||||
* Returns {@code null} immediately, relying on this class's {@link #merge
|
||||
* merge} implementation to return only the first {@code info} object it
|
||||
* encounters, ignoring all subsequent ones.
|
||||
*/
|
||||
public AuthenticationInfo beforeAllAttempts(Collection<? extends Realm> realms, AuthenticationToken token) throws AuthenticationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the specified {@code aggregate} instance if is non null and valid
|
||||
* (that is, has principals and they are not empty) immediately, or, if it
|
||||
* is null or not valid, the {@code info} argument is returned instead.
|
||||
* <p>
|
||||
* This logic ensures that the first valid info encountered is the one
|
||||
* retained and all subsequent ones are ignored, since this strategy
|
||||
* mandates that only the info from the first successfully authenticated
|
||||
* realm be used.
|
||||
*/
|
||||
protected AuthenticationInfo merge(AuthenticationInfo info, AuthenticationInfo aggregate) {
|
||||
if (aggregate != null && !Lang.isEmpty(aggregate.getPrincipals())) {
|
||||
return aggregate;
|
||||
}
|
||||
return info != null ? info : aggregate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo singleRealmInfo, AuthenticationInfo aggregateInfo, Throwable t) throws AuthenticationException {
|
||||
if (singleRealmInfo == null) {
|
||||
if (t.getClass().isAssignableFrom(CaptchaIncorrectException.class)) {
|
||||
throw Lang.makeThrow(CaptchaIncorrectException.class, t.getMessage());
|
||||
} else if (t.getClass().isAssignableFrom(CaptchaEmptyException.class)) {
|
||||
throw Lang.makeThrow(CaptchaEmptyException.class, t.getMessage());
|
||||
} else if (t.getClass().isAssignableFrom(LockedAccountException.class)) {
|
||||
throw Lang.makeThrow(LockedAccountException.class, t.getMessage());
|
||||
} else if (t.getClass().isAssignableFrom(UnknownAccountException.class)) {
|
||||
throw Lang.makeThrow(UnknownAccountException.class, t.getMessage());
|
||||
} else if (t.getClass().isAssignableFrom(IncorrectCredentialsException.class)) {
|
||||
throw Lang.makeThrow(IncorrectCredentialsException.class, t.getMessage());
|
||||
} else if (t.getClass().isAssignableFrom(ExcessiveAttemptsException.class)) {
|
||||
throw Lang.makeThrow(ExcessiveAttemptsException.class, t.getMessage());
|
||||
}
|
||||
throw Lang.makeThrow(AuthenticationException.class, t.getMessage());
|
||||
}
|
||||
return super.afterAttempt(realm, token, singleRealmInfo, aggregateInfo, t);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package io.v.nutz.web.commons.shiro.realm;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.v.nutz.base.constant.RedisConstant;
|
||||
import io.v.nutz.base.utils.LoginUtil;
|
||||
import io.v.nutz.sys.models.Sys_role;
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.sys.services.SysRoleService;
|
||||
import io.v.nutz.sys.services.SysUnionService;
|
||||
import io.v.nutz.sys.services.SysUserService;
|
||||
import io.v.nutz.web.commons.ext.validate.ValidateService;
|
||||
import io.v.nutz.web.commons.shiro.config.EasyCredentialsMatch;
|
||||
import io.v.nutz.web.commons.shiro.token.PlatformCaptchaToken;
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.*;
|
||||
import org.apache.shiro.authc.credential.CredentialsMatcher;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.cache.CacheManager;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.util.ByteSource;
|
||||
import org.nutz.castor.Castors;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.integration.jedis.RedisService;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/1/11.
|
||||
*/
|
||||
@IocBean(name = "platformRealm")
|
||||
public class PlatformAuthorizingRealm extends AuthorizingRealm {
|
||||
private static final Log log = Logs.get();
|
||||
@Inject
|
||||
private SysUserService sysUserService;
|
||||
@Inject
|
||||
private SysRoleService sysRoleService;
|
||||
@Inject
|
||||
private RedisService redisService;
|
||||
@Inject
|
||||
private ValidateService validateService;
|
||||
|
||||
@Inject
|
||||
private SysUnionService sysUnionService;
|
||||
|
||||
protected SysUserService getUserService() {
|
||||
return sysUserService;
|
||||
}
|
||||
|
||||
protected SysRoleService getRoleService() {
|
||||
return sysRoleService;
|
||||
}
|
||||
|
||||
protected RedisService getRedisService() {
|
||||
return redisService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
|
||||
if (token.getClass().isAssignableFrom(PlatformCaptchaToken.class)) {
|
||||
PlatformCaptchaToken authcToken = (PlatformCaptchaToken) token;
|
||||
String loginname = authcToken.getUsername();
|
||||
String captcha = authcToken.getCaptcha();
|
||||
String key = authcToken.getKey();
|
||||
LoginUtil.LoginOrigin loginOrigin = authcToken.getLoginOrigin();
|
||||
|
||||
Session session = SecurityUtils.getSubject().getSession(true);
|
||||
|
||||
if (!loginOrigin.equals(LoginUtil.LoginOrigin.CAS) && !loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN)) {
|
||||
validateService.checkCode(key, captcha);
|
||||
}
|
||||
|
||||
if (Strings.isBlank(loginname)) {
|
||||
throw Lang.makeThrow(AuthenticationException.class, "登录账号不可为空");
|
||||
}
|
||||
|
||||
Sys_user user = getUserService().fetch(Cnd.NEW().and("loginname", "=", loginname));
|
||||
|
||||
if (Lang.isEmpty(user)) {
|
||||
throw Lang.makeThrow(UnknownAccountException.class, "账号不存在", loginname);
|
||||
}
|
||||
|
||||
// if (user.isDisabled()) {
|
||||
// throw Lang.makeThrow(LockedAccountException.class, "账号被禁用", loginname);
|
||||
// }
|
||||
|
||||
if (StrUtil.isNotBlank(getRedisService().get(RedisConstant.PLATFORM_REDIS_PREFIX + "platform:userId:" + user.getId()))) {
|
||||
throw Lang.makeThrow(LockedAccountException.class, "账号被禁用", loginname);
|
||||
}
|
||||
|
||||
if (!loginOrigin.equals(LoginUtil.LoginOrigin.CAS) && !loginOrigin.equals(LoginUtil.LoginOrigin.QI_YE_WEI_XIN)) {
|
||||
int errCount = NumberUtils.toInt(Strings.sNull(session.getAttribute("platformErrCount")));
|
||||
if (errCount >= 4) {
|
||||
//输错5次禁用账户10分钟
|
||||
// getUserService().update(Chain.make("disabled", 1), Cnd.where("id", "=", user.getId()));
|
||||
|
||||
session.setAttribute("platformErrCount", 0);
|
||||
getRedisService().setex(RedisConstant.PLATFORM_REDIS_PREFIX + "platform:userId:" + user.getId(), 60, user.getId());
|
||||
throw Lang.makeThrow(LockedAccountException.class, "账号被禁用", loginname);
|
||||
}
|
||||
}
|
||||
|
||||
//没有错误后完善use信息
|
||||
getUserService().fetchLinks(user, null);
|
||||
user = getUserService().fillMenu(user);
|
||||
user = getUserService().fillModuleMenus(user);
|
||||
user = getUserService().fillUnitUnion(user);
|
||||
|
||||
getRedisService().del(RedisConstant.PLATFORM_REDIS_PREFIX + "platform:userId:" + user.getId());
|
||||
session.setAttribute("platformErrCount", 0);
|
||||
session.setAttribute("platform_uid", user.getId());
|
||||
session.setAttribute("platform_username", user.getUsername());
|
||||
session.setAttribute("platform_loginname", user.getLoginname());
|
||||
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword().toCharArray(), ByteSource.Util.bytes(user.getSalt()), getName());
|
||||
info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
|
||||
return info;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||
Object object = principals.getPrimaryPrincipal();
|
||||
if (object.getClass().isAssignableFrom(Sys_user.class)) {
|
||||
Sys_user user = Castors.me().castTo(object, Sys_user.class);
|
||||
if (!Lang.isEmpty(user) && !user.isDisabled()) {
|
||||
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
|
||||
info.addRoles(getUserService().getRoleCodeList(user));
|
||||
for (Sys_role role : user.getRoles()) {
|
||||
if (!role.isDisabled())
|
||||
info.addStringPermissions(getRoleService().getPermissionNameList(role));
|
||||
}
|
||||
return info;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PlatformAuthorizingRealm() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public PlatformAuthorizingRealm(CacheManager cacheManager, CredentialsMatcher matcher) {
|
||||
super(cacheManager, matcher);
|
||||
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
|
||||
EasyCredentialsMatch hashedCredentialsMatcher = new EasyCredentialsMatch();
|
||||
hashedCredentialsMatcher.setHashAlgorithmName("SHA-256");
|
||||
hashedCredentialsMatcher.setHashIterations(1024);
|
||||
hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
|
||||
setAuthenticationTokenClass(PlatformCaptchaToken.class);
|
||||
setCredentialsMatcher(hashedCredentialsMatcher);
|
||||
}
|
||||
|
||||
public PlatformAuthorizingRealm(CacheManager cacheManager) {
|
||||
this(cacheManager, null);
|
||||
}
|
||||
|
||||
public PlatformAuthorizingRealm(CredentialsMatcher matcher) {
|
||||
this(null, matcher);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.v.nutz.web.commons.shiro.remember;
|
||||
|
||||
|
||||
import org.apache.shiro.web.mgt.CookieRememberMeManager;
|
||||
|
||||
/**
|
||||
* 由于shiro默认按照jdk生成base64编码cookies策略长度超过4K,造成浏览器无法识别
|
||||
* 所以已经重写生成cookies策略
|
||||
* Created by wizzer on 2017/1/18.
|
||||
*/
|
||||
public class LightCookieRememberMeManager extends CookieRememberMeManager {
|
||||
public LightCookieRememberMeManager() {
|
||||
super();
|
||||
setSerializer(new SimplePrincipalSerializer());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package io.v.nutz.web.commons.shiro.remember;
|
||||
|
||||
import org.apache.shiro.io.SerializationException;
|
||||
import org.apache.shiro.io.Serializer;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* Creates A GZIPed rememberMe cookie, based on the patch for SHIRO-226 (https://issues.apache.org/jira/browse/SHIRO-226)
|
||||
* Created by wizzer on 2017/1/18.
|
||||
*/
|
||||
|
||||
public class SimplePrincipalSerializer implements Serializer<PrincipalCollection> {
|
||||
/**
|
||||
* Magic number to signal that this is a SimplePrincipalSerializer file so that we don't try to decode something crap.
|
||||
*/
|
||||
private static final int MAGIC = 0x0BADBEEF;
|
||||
|
||||
public byte[] serialize(PrincipalCollection pc) throws SerializationException {
|
||||
ByteArrayOutputStream ba = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
GZIPOutputStream gout = new GZIPOutputStream(ba);
|
||||
ObjectOutputStream out = new ObjectOutputStream(gout);
|
||||
|
||||
// Write the magic number which allows us to decode it later on
|
||||
out.writeInt(MAGIC);
|
||||
|
||||
// Limited to 32768 realms. Should be enough for everybody.
|
||||
out.writeShort(pc.getRealmNames().size());
|
||||
|
||||
for (String realm : pc.getRealmNames()) {
|
||||
out.writeUTF(realm);
|
||||
|
||||
Collection<?> principals = pc.fromRealm(realm);
|
||||
|
||||
// Again, limited to 32768 principals.
|
||||
out.writeShort(principals.size());
|
||||
|
||||
for (Object principal : principals) {
|
||||
out.writeObject(principal);
|
||||
}
|
||||
}
|
||||
gout.finish();
|
||||
} catch (IOException e) {
|
||||
throw new SerializationException(e.getMessage());
|
||||
}
|
||||
return ba.toByteArray();
|
||||
}
|
||||
|
||||
public PrincipalCollection deserialize(byte[] serialized) throws SerializationException {
|
||||
ByteArrayInputStream ba = new ByteArrayInputStream(serialized);
|
||||
|
||||
try {
|
||||
GZIPInputStream gin = new GZIPInputStream(ba);
|
||||
ObjectInputStream in = new ObjectInputStream(gin);
|
||||
SimplePrincipalCollection pc = new SimplePrincipalCollection();
|
||||
|
||||
// Check magic number
|
||||
if (in.readInt() != MAGIC)
|
||||
throw new SerializationException(
|
||||
"Not valid magic number while deserializing stored PrincipalCollection - possibly obsolete cookie.");
|
||||
|
||||
int numRealms = in.readShort();
|
||||
|
||||
// realms loop
|
||||
for (int i = 0; i < numRealms; i++) {
|
||||
String realmName = in.readUTF();
|
||||
|
||||
int numPrincipals = in.readShort();
|
||||
|
||||
// principals loop
|
||||
for (int j = 0; j < numPrincipals; j++) {
|
||||
Object principal = in.readObject();
|
||||
|
||||
pc.add(principal, realmName);
|
||||
}
|
||||
}
|
||||
|
||||
return pc;
|
||||
} catch (IOException e) {
|
||||
throw new SerializationException(e.getMessage());
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new SerializationException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.v.nutz.web.commons.shiro.token;
|
||||
|
||||
import io.v.nutz.base.utils.LoginUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/1/11.
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class PlatformCaptchaToken extends UsernamePasswordToken {
|
||||
|
||||
private static final long serialVersionUID = 4676958151524148623L;
|
||||
private String captcha;
|
||||
private String key;
|
||||
private LoginUtil.LoginOrigin loginOrigin;
|
||||
|
||||
public PlatformCaptchaToken(String username, String password, boolean rememberMe, String host, String captcha, String key) {
|
||||
super(username, password, rememberMe, host);
|
||||
this.captcha = captcha;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public PlatformCaptchaToken(String username, String password, boolean rememberMe, String host, String captcha) {
|
||||
super(username, password, rememberMe, host);
|
||||
this.captcha = captcha;
|
||||
}
|
||||
|
||||
/**
|
||||
* cas免密登录
|
||||
*
|
||||
* @param username 用户名
|
||||
*/
|
||||
public PlatformCaptchaToken(String username, LoginUtil.LoginOrigin loginOrigin) {
|
||||
super(username, "", false, null);
|
||||
this.loginOrigin = loginOrigin;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.v.nutz.web.commons.slog;
|
||||
|
||||
import io.v.nutz.web.commons.slog.annotation.SLog;
|
||||
import org.nutz.aop.MethodInterceptor;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.ioc.aop.SimpleAopMaker;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2016/6/22.
|
||||
*/
|
||||
@IocBean(name="$aop_syslog")
|
||||
public class SLogAopConfigration extends SimpleAopMaker<SLog> {
|
||||
|
||||
@Inject("refer:$ioc")
|
||||
protected Ioc ioc;
|
||||
|
||||
public List<? extends MethodInterceptor> makeIt(SLog slog, Method method, Ioc ioc) {
|
||||
return Arrays.asList(new SLogAopInterceptor(ioc, slog, method));
|
||||
}
|
||||
|
||||
public String[] getName() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
public boolean has(String name) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package io.v.nutz.web.commons.slog;
|
||||
|
||||
import io.v.nutz.web.commons.slog.annotation.SLog;
|
||||
import org.nutz.aop.InterceptorChain;
|
||||
import org.nutz.aop.MethodInterceptor;
|
||||
import org.nutz.el.El;
|
||||
import org.nutz.ioc.Ioc;
|
||||
import org.nutz.lang.segment.CharSegment;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2016/6/22.
|
||||
*/
|
||||
public class SLogAopInterceptor implements MethodInterceptor {
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
protected SLogService sLogService;
|
||||
|
||||
protected String source;
|
||||
|
||||
protected String type;
|
||||
protected String tag;
|
||||
protected CharSegment seg;
|
||||
protected boolean param;
|
||||
protected boolean result;
|
||||
protected boolean async;
|
||||
protected Map<String, El> els;
|
||||
protected Ioc ioc;
|
||||
|
||||
public SLogAopInterceptor(Ioc ioc, SLog slog, Method method) {
|
||||
this.seg = new CharSegment(slog.msg());
|
||||
if (seg.hasKey()) {
|
||||
els = new HashMap<String, El>();
|
||||
for (String key : seg.keys()) {
|
||||
els.put(key, new El(key));
|
||||
}
|
||||
}
|
||||
this.param = slog.param();
|
||||
this.result = slog.result();
|
||||
this.ioc = ioc;
|
||||
this.source = method.getDeclaringClass().getName() + "#" + method.getName();
|
||||
this.tag = slog.tag();
|
||||
SLog _s = method.getDeclaringClass().getAnnotation(SLog.class);
|
||||
if (_s != null) {
|
||||
this.tag = _s.tag() + "," + this.tag;
|
||||
}
|
||||
this.type = slog.type();
|
||||
this.async = slog.async();
|
||||
}
|
||||
|
||||
public void filter(InterceptorChain chain) throws Throwable {
|
||||
try {
|
||||
chain.doChain();
|
||||
doLog("aop.after", seg, chain, null);
|
||||
} catch (Throwable e) {
|
||||
doLog("aop.error", seg, chain, e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected void doLog(String t, CharSegment seg, InterceptorChain chain, Throwable e) {
|
||||
if (sLogService == null)
|
||||
sLogService = ioc.get(SLogService.class);
|
||||
try {
|
||||
sLogService.log(t,
|
||||
type,
|
||||
tag,
|
||||
source,
|
||||
seg,
|
||||
els,
|
||||
param,
|
||||
result,
|
||||
async,
|
||||
chain.getArgs(),
|
||||
chain.getReturn(),
|
||||
chain.getCallingMethod(),
|
||||
chain.getCallingObj(),
|
||||
e);
|
||||
} catch (Exception e1) {
|
||||
log.debug("slog fail", e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package io.v.nutz.web.commons.slog;
|
||||
|
||||
import io.v.nutz.sys.models.Sys_log;
|
||||
import io.v.nutz.sys.services.SysLogService;
|
||||
import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import org.nutz.Nutz;
|
||||
import org.nutz.el.El;
|
||||
import org.nutz.ioc.loader.annotation.Inject;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.segment.CharSegment;
|
||||
import org.nutz.lang.util.ClassMetaReader;
|
||||
import org.nutz.lang.util.Context;
|
||||
import org.nutz.lang.util.MethodParamNamesScaner;
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2016/6/22.
|
||||
*/
|
||||
@IocBean(create = "init", depose = "close")
|
||||
public class SLogService implements Runnable {
|
||||
|
||||
private static final Log log = Logs.get();
|
||||
|
||||
ExecutorService es;
|
||||
|
||||
LinkedBlockingQueue<Sys_log> queue;
|
||||
|
||||
@Inject
|
||||
protected SysLogService sysLogService;
|
||||
|
||||
/**
|
||||
* 异步插入日志
|
||||
*
|
||||
* @param syslog 日志对象
|
||||
*/
|
||||
public void async(Sys_log syslog) {
|
||||
LinkedBlockingQueue<Sys_log> queue = this.queue;
|
||||
if (queue != null)
|
||||
try {
|
||||
boolean re = queue.offer(syslog, 50, TimeUnit.MILLISECONDS);
|
||||
if (!re) {
|
||||
log.info("syslog queue is full, drop it ...");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步插入日志
|
||||
*
|
||||
* @param syslog 日志对象
|
||||
*/
|
||||
public void sync(Sys_log syslog) {
|
||||
try {
|
||||
sysLogService.fastInsertSysLog(syslog);
|
||||
} catch (Throwable e) {
|
||||
log.info("insert syslog sync fail", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
LinkedBlockingQueue<Sys_log> queue = this.queue;
|
||||
if (queue == null)
|
||||
break;
|
||||
try {
|
||||
Sys_log sysLog = queue.poll(1, TimeUnit.SECONDS);
|
||||
if (sysLog != null) {
|
||||
sync(sysLog);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 本方法通常由aop拦截器调用.
|
||||
*
|
||||
* @param t 日志类型
|
||||
* @param tag 标签
|
||||
* @param source 源码位置
|
||||
* @param els 消息模板的EL表达式预处理表
|
||||
* @param param 是否异步插入
|
||||
* @param result 是否异步插入
|
||||
* @param async 是否异步插入
|
||||
* @param args 方法参数
|
||||
* @param re 方法返回值
|
||||
* @param method 方法实例
|
||||
* @param obj 被拦截的对象
|
||||
* @param e 异常对象
|
||||
*/
|
||||
public void log(String t, String type, String tag, String source, CharSegment seg,
|
||||
Map<String, El> els, boolean param, boolean result,
|
||||
boolean async,
|
||||
Object[] args, Object re, Method method, Object obj,
|
||||
Throwable e) {
|
||||
String _msg = null;
|
||||
if (seg.hasKey()) {
|
||||
Context ctx = Lang.context();
|
||||
List<String> names = null;
|
||||
if (Nutz.majorVersion() == 1 && Nutz.minorVersion() < 60) {
|
||||
Class<?> klass = obj.getClass();
|
||||
if (klass.getName().endsWith("$$NUTZAOP"))
|
||||
klass = klass.getSuperclass();
|
||||
String key = klass.getName();
|
||||
if (caches.containsKey(key))
|
||||
names = caches.get(key).get(ClassMetaReader.getKey(method));
|
||||
else {
|
||||
try {
|
||||
Map<String, List<String>> tmp = MethodParamNamesScaner.getParamNames(klass);
|
||||
names = tmp.get(ClassMetaReader.getKey(method));
|
||||
caches.put(key, tmp);
|
||||
} catch (IOException e1) {
|
||||
log.debug("error when reading param name");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
names = MethodParamNamesScaner.getParamNames(method);
|
||||
}
|
||||
if (names != null) {
|
||||
for (int i = 0; i < names.size() && i < args.length; i++) {
|
||||
ctx.set(names.get(i), args[i]);
|
||||
}
|
||||
}
|
||||
ctx.set("obj", obj);
|
||||
ctx.set("args", args);
|
||||
ctx.set("re", re);
|
||||
ctx.set("return", re);
|
||||
ctx.set("req", Mvcs.getReq());
|
||||
ctx.set("resp", Mvcs.getResp());
|
||||
Context _ctx = Lang.context();
|
||||
for (String key : seg.keys()) {
|
||||
_ctx.set(key, els.get(key).eval(ctx));
|
||||
}
|
||||
_msg = seg.render(_ctx).toString();
|
||||
} else {
|
||||
_msg = seg.getOrginalString();
|
||||
}
|
||||
String _param = "";
|
||||
String _result = "";
|
||||
if (param && args != null) {
|
||||
try {
|
||||
_param = Json.toJson(args);
|
||||
} catch (Exception e1) {
|
||||
_param = "传参不能转换为JSON格式";
|
||||
}
|
||||
}
|
||||
if (result && re != null) {
|
||||
try {
|
||||
_result = Json.toJson(re);
|
||||
} catch (Exception e1) {
|
||||
_param = "返回对象不能转换为JSON格式";
|
||||
}
|
||||
}
|
||||
log(type, tag, source, _msg, async, _param, _result);
|
||||
}
|
||||
|
||||
|
||||
public void log(String type, String tag, String source, String msg, boolean async, String param, String result) {
|
||||
Sys_log slog = makeLog(type, tag, source, msg, param, result);
|
||||
if (async)
|
||||
async(slog);
|
||||
else
|
||||
sync(slog);
|
||||
}
|
||||
|
||||
protected static Map<String, Map<String, List<String>>> caches = new HashMap<String, Map<String, List<String>>>();
|
||||
|
||||
public void init() {
|
||||
queue = new LinkedBlockingQueue<Sys_log>();
|
||||
int c = Runtime.getRuntime().availableProcessors();
|
||||
es = Executors.newFixedThreadPool(c);
|
||||
for (int i = 0; i < c; i++) {
|
||||
es.submit(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
queue = null; // 触发关闭
|
||||
if (es != null && !es.isShutdown()) {
|
||||
es.shutdown();
|
||||
es.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public static Sys_log makeLog(String type, String tag, String source, String msg, String param, String result) {
|
||||
Sys_log sysLog = new Sys_log();
|
||||
if (type == null || tag == null) {
|
||||
throw new RuntimeException("type/tag can't null");
|
||||
}
|
||||
if (source == null) {
|
||||
StackTraceElement[] tmp = Thread.currentThread().getStackTrace();
|
||||
if (tmp.length > 2) {
|
||||
source = tmp[2].getClassName() + "#" + tmp[2].getMethodName();
|
||||
} else {
|
||||
source = "main";
|
||||
}
|
||||
|
||||
}
|
||||
sysLog.setType(type);
|
||||
sysLog.setTag(tag);
|
||||
sysLog.setSrc(source);
|
||||
sysLog.setMsg(msg);
|
||||
sysLog.setParam(param);
|
||||
sysLog.setResult(result);
|
||||
if (Mvcs.getReq() != null) {
|
||||
sysLog.setIp(Lang.getIP(Mvcs.getReq()));
|
||||
}
|
||||
sysLog.setCreatedBy(ShiroUtil.getPlatformUid());
|
||||
sysLog.setDelFlag(false);
|
||||
sysLog.setUsername(ShiroUtil.getPlatformUsername());
|
||||
sysLog.setLoginname(ShiroUtil.getPlatformLoginname());
|
||||
return sysLog;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.v.nutz.web.commons.slog.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface SLog {
|
||||
String type() default "platform";
|
||||
/**
|
||||
* 标签
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String tag();
|
||||
|
||||
String msg() default "";
|
||||
|
||||
/**
|
||||
* 是否记录传递参数
|
||||
*
|
||||
* @return 消息模板
|
||||
*/
|
||||
boolean param() default false;
|
||||
|
||||
/**
|
||||
* 记录执行结果
|
||||
*
|
||||
* @return 消息模板
|
||||
*/
|
||||
boolean result() default false;
|
||||
|
||||
/**
|
||||
* 是否异步执行,默认为true
|
||||
*
|
||||
* @return true, 如果需要异步执行
|
||||
*/
|
||||
boolean async() default true;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package io.v.nutz.web.commons.utils;
|
||||
|
||||
import com.artofsolving.jodconverter.DocumentConverter;
|
||||
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
|
||||
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
|
||||
import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* 文档转换util
|
||||
*/
|
||||
public class DocConverter {
|
||||
private static final int environment = 1;// 环境1:windows,2:linux(涉及pdf2swf路径问题)
|
||||
private String fileString;
|
||||
private String outputPath = "";// 输入路径,如果不设置就输出在默认位置
|
||||
private String fileName;
|
||||
private File pdfFile;
|
||||
private File swfFile;
|
||||
private File docFile;
|
||||
|
||||
public DocConverter(String fileString) {
|
||||
ini(fileString);
|
||||
}
|
||||
|
||||
/*
|
||||
* 重新设置 file @param fileString
|
||||
*/
|
||||
public void setFile(String fileString) {
|
||||
ini(fileString);
|
||||
}
|
||||
|
||||
/*
|
||||
* 初始化 @param fileString
|
||||
*/
|
||||
private void ini(String fileString) {
|
||||
this.fileString = fileString;
|
||||
fileName = fileString.substring(0, fileString.lastIndexOf("."));
|
||||
docFile = new File(fileString);
|
||||
pdfFile = new File(fileName + ".pdf");
|
||||
swfFile = new File(fileName + ".swf");
|
||||
}
|
||||
|
||||
/*
|
||||
* 转为PDF @param file
|
||||
*/
|
||||
private void doc2pdf() throws Exception {
|
||||
if (docFile.exists()) {
|
||||
if (!pdfFile.exists()) {
|
||||
OpenOfficeConnection connection = new SocketOpenOfficeConnection(8100);
|
||||
try {
|
||||
connection.connect();
|
||||
DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
|
||||
converter.convert(docFile, pdfFile);
|
||||
// close the connection
|
||||
connection.disconnect();
|
||||
System.out.println("****pdf转换成功,PDF输出:" + pdfFile.getPath() + "****");
|
||||
} catch (java.net.ConnectException e) {
|
||||
// ToDo Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
System.out.println("****swf转换异常,openoffice服务未启动!****");
|
||||
throw e;
|
||||
} catch (com.artofsolving.jodconverter.openoffice.connection.OpenOfficeException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("****swf转换器异常,读取转换文件失败****");
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
System.out.println("****已经转换为pdf,不需要再进行转化****");
|
||||
}
|
||||
} else {
|
||||
System.out.println("****swf转换器异常,需要转换的文档不存在,无法转换****");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 转换成swf
|
||||
*/
|
||||
private void pdf2swf() throws Exception {
|
||||
Runtime r = Runtime.getRuntime();
|
||||
if (!swfFile.exists()) {
|
||||
if (pdfFile.exists()) {
|
||||
if (environment == 1)// windows环境处理
|
||||
{
|
||||
try {
|
||||
// 这里根据SWFTools安装路径需要进行相应更改
|
||||
Process p = r.exec("D:\\ewm\\hj\\pdf2swf\\pdf2swf.exe " + pdfFile.getPath() + " -o " + swfFile.getPath() + " -T 9");
|
||||
System.out.print(loadStream(p.getInputStream()));
|
||||
System.err.print(loadStream(p.getErrorStream()));
|
||||
System.out.print(loadStream(p.getInputStream()));
|
||||
System.err.println("****swf转换成功,文件输出:" + swfFile.getPath() + "****");
|
||||
// if (pdfFile.exists()) {
|
||||
// pdfFile.delete();
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
} else if (environment == 2)// linux环境处理
|
||||
{
|
||||
try {
|
||||
Process p = r.exec("pdf2swf " + pdfFile.getPath() + " -o " + swfFile.getPath() + " -T 9");
|
||||
System.out.print(loadStream(p.getInputStream()));
|
||||
System.err.print(loadStream(p.getErrorStream()));
|
||||
System.err.println("****swf转换成功,文件输出:" + swfFile.getPath() + "****");
|
||||
if (pdfFile.exists()) {
|
||||
pdfFile.delete();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
System.out.println("****pdf不存在,无法转换****");
|
||||
}
|
||||
} else {
|
||||
System.out.println("****swf已存在不需要转换****");
|
||||
}
|
||||
}
|
||||
|
||||
static String loadStream(InputStream in) throws IOException {
|
||||
int ptr = 0;
|
||||
//把InputStream字节流 替换为BufferedReader字符流 2013-07-17修改
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
while ((ptr = reader.read()) != -1) {
|
||||
buffer.append((char) ptr);
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/*
|
||||
* 转换主方法
|
||||
*/
|
||||
public boolean conver() {
|
||||
if (swfFile.exists()) {
|
||||
System.out.println("****swf转换器开始工作,该文件已经转换为swf****");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (environment == 1) {
|
||||
System.out.println("****swf转换器开始工作,当前设置运行环境windows****");
|
||||
} else {
|
||||
System.out.println("****swf转换器开始工作,当前设置运行环境linux****");
|
||||
}
|
||||
|
||||
try {
|
||||
doc2pdf();
|
||||
pdf2swf();
|
||||
} catch (Exception e) {
|
||||
// TODO: Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (swfFile.exists()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 返回文件路径 @param s
|
||||
*/
|
||||
public String getswfPath() {
|
||||
if (swfFile.exists()) {
|
||||
String tempString = swfFile.getPath();
|
||||
tempString = tempString.replaceAll("\\\\", "/");
|
||||
return tempString;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 设置输出路径
|
||||
*/
|
||||
public void setOutputPath(String outputPath) {
|
||||
this.outputPath = outputPath;
|
||||
if (!outputPath.equals("")) {
|
||||
String realName = fileName.substring(fileName.lastIndexOf("/"), fileName.lastIndexOf("."));
|
||||
if (outputPath.charAt(outputPath.length()) == '/') {
|
||||
swfFile = new File(outputPath + realName + ".swf");
|
||||
} else {
|
||||
swfFile = new File(outputPath + realName + ".swf");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String s[]) {
|
||||
DocConverter d = new DocConverter("C:\\Users\\mayn\\Desktop\\安全管理系统.docx");
|
||||
d.conver();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package io.v.nutz.web.commons.utils;
|
||||
|
||||
import org.nutz.log.Log;
|
||||
import org.nutz.log.Logs;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @Author: 1V
|
||||
* @DateTime: 2020/12/13 17:07
|
||||
* @Description: TODO
|
||||
*/
|
||||
public class FileUtil {
|
||||
private final static Log log = Logs.get();
|
||||
|
||||
/**
|
||||
* 压缩文件
|
||||
*
|
||||
* @param sourceFilePath 源文件路径
|
||||
* @param zipFilePath 压缩后文件存储路径
|
||||
* @param zipFilename 压缩文件名
|
||||
*/
|
||||
public static void compressToZip(String sourceFilePath, String zipFilePath, String zipFilename) {
|
||||
File sourceFile = new File(sourceFilePath);
|
||||
File zipPath = new File(zipFilePath);
|
||||
if (!zipPath.exists()) {
|
||||
zipPath.mkdirs();
|
||||
}
|
||||
File zipFile = new File(zipPath + File.separator + zipFilename);
|
||||
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFile))) {
|
||||
writeZip(sourceFile, "", zos);
|
||||
//文件压缩完成后,删除被压缩文件
|
||||
boolean flag = deleteDir(sourceFile);
|
||||
log.infof("删除被压缩文件[" + sourceFile + "]标志:{}", flag);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 遍历所有文件,压缩
|
||||
*
|
||||
* @param file 源文件目录
|
||||
* @param parentPath 压缩文件目录
|
||||
* @param zos 文件流
|
||||
*/
|
||||
public static void writeZip(File file, String parentPath, ZipOutputStream zos) {
|
||||
if (file.isDirectory()) {
|
||||
//目录
|
||||
parentPath += file.getName() + File.separator;
|
||||
File[] files = file.listFiles();
|
||||
for (File f : files) {
|
||||
writeZip(f, parentPath, zos);
|
||||
}
|
||||
} else {
|
||||
//文件
|
||||
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
|
||||
//指定zip文件夹
|
||||
ZipEntry zipEntry = new ZipEntry(parentPath + file.getName());
|
||||
zos.putNextEntry(zipEntry);
|
||||
int len;
|
||||
byte[] buffer = new byte[1024 * 10];
|
||||
while ((len = bis.read(buffer, 0, buffer.length)) != -1) {
|
||||
zos.write(buffer, 0, len);
|
||||
zos.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件夹
|
||||
*
|
||||
* @param dir
|
||||
* @return
|
||||
*/
|
||||
public static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
boolean success = deleteDir(new File(dir, children[i]));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//删除空文件夹
|
||||
return dir.delete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
package io.v.nutz.web.commons.utils;
|
||||
|
||||
import io.v.nutz.base.utils.Roles;
|
||||
import io.v.nutz.sys.models.Sys_menu;
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.sys.models.Sys_user_role;
|
||||
import io.v.nutz.sys.services.SysUserService;
|
||||
import io.v.nutz.sys.services.impl.SysUserServiceImpl;
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.json.Json;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.mvc.Mvcs;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Created by wizzer on 2017/1/16.
|
||||
*/
|
||||
public class ShiroUtil {
|
||||
private static final String ROLE_NAMES_DELIMETER = ",";
|
||||
private static final String PERMISSION_NAMES_DELIMETER = ",";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ShiroUtil.class);
|
||||
|
||||
/**
|
||||
* 获取平台当前登录用户的所在单位
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getPlatformUserUnitid() {
|
||||
try {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
if (subject != null) {
|
||||
Sys_user user = (Sys_user) subject.getPrincipal();
|
||||
if (user != null) {
|
||||
return Strings.sNull(user.getUnitid());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台后台登陆UID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getPlatformUid() {
|
||||
try {
|
||||
HttpServletRequest request = Mvcs.getReq();
|
||||
if (request != null) {
|
||||
return Strings.sNull(request.getSession(true).getAttribute("platform_uid"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台后台登陆用户名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getPlatformLoginname() {
|
||||
try {
|
||||
HttpServletRequest request = Mvcs.getReq();
|
||||
if (request != null) {
|
||||
return Strings.sNull(request.getSession(true).getAttribute("platform_loginname"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台后台登陆用户名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getPlatformUsername() {
|
||||
try {
|
||||
HttpServletRequest request = Mvcs.getReq();
|
||||
if (request != null) {
|
||||
return Strings.sNull(request.getSession(true).getAttribute("platform_username"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为已认证通过的用户,不包含已记住的用户,这是与 isUser 标签方法的区别所在
|
||||
*
|
||||
* @return 用户是否已通过认证
|
||||
*/
|
||||
public static boolean isAuthenticated() {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject != null && subject.isAuthenticated() == true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证是否为未认证通过用户,与 isAuthenticated 标签相对应,与 isGuest 标签的区别是,该标签包含已记住用户
|
||||
*
|
||||
* @return 用户是否未通过认证
|
||||
*/
|
||||
public static boolean isNotAuthenticated() {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject == null || subject.isAuthenticated() == false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否为访客,即未认证(包含未记住)的用户
|
||||
*
|
||||
* @return 用户是否为访客
|
||||
*/
|
||||
public static boolean isGuest() {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject == null || subject.getPrincipal() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否认证通过或已记住的用户
|
||||
*
|
||||
* @return 用户是否认证通过或已记住的用户
|
||||
*/
|
||||
public static boolean isUser() {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject != null && subject.getPrincipal() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回用户 Principal
|
||||
*
|
||||
* @return 用户 Principal
|
||||
*/
|
||||
public static Object getPrincipal() {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject != null ? subject.getPrincipal() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回用户属性
|
||||
*
|
||||
* @param property 属性名称
|
||||
* @return 用户属性
|
||||
*/
|
||||
public static Object getPrincipalProperty(String property) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject != null) {
|
||||
Object principal = subject.getPrincipal();
|
||||
|
||||
try {
|
||||
BeanInfo bi = Introspector.getBeanInfo(principal.getClass());
|
||||
|
||||
for (PropertyDescriptor pd : bi.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals(property) == true) {
|
||||
return pd.getReadMethod().invoke(principal, (Object[]) null);
|
||||
}
|
||||
}
|
||||
|
||||
logger.trace("Property [{}] not found in principal of type [{}]", property,
|
||||
principal.getClass().getName());
|
||||
} catch (Exception e) {
|
||||
logger.trace("Error reading property [{}] from principal of type [{}]", property,
|
||||
principal.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回用户属性 json格式 懂得都懂
|
||||
*
|
||||
* @param property
|
||||
* @return
|
||||
*/
|
||||
public static String getPrincipalPropertyJson(String property) {
|
||||
return Json.toJson(getPrincipalProperty(property));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某角色。
|
||||
*
|
||||
* @param role 角色名称
|
||||
* @return 用户是否具备某角色
|
||||
*/
|
||||
public static boolean hasRole(String role) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject != null && subject.hasRole(role) == true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某角色,与 hasRole 逻辑相反。
|
||||
*
|
||||
* @param role 角色名称
|
||||
* @return 用户是否不具备某角色
|
||||
*/
|
||||
public static boolean lacksRole(String role) {
|
||||
return hasRole(role) != true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色。
|
||||
*
|
||||
* @param roleNames 以 delimeter 为分隔符的角色列表
|
||||
* @param delimeter 角色列表分隔符
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public static boolean hasAnyRoles(String roleNames, String delimeter) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
if (subject != null) {
|
||||
if (delimeter == null || delimeter.length() == 0) {
|
||||
delimeter = ROLE_NAMES_DELIMETER;
|
||||
}
|
||||
|
||||
for (String role : roleNames.split(delimeter)) {
|
||||
if (subject.hasRole(role.trim()) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色。
|
||||
*
|
||||
* @param roleNames 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public static boolean hasAnyRoles(String roleNames) {
|
||||
return hasAnyRoles(roleNames, ROLE_NAMES_DELIMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色。
|
||||
*
|
||||
* @param roleNames 角色列表
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public static boolean hasAnyRoles(Collection<String> roleNames) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject != null && roleNames != null) {
|
||||
for (String role : roleNames) {
|
||||
if (role != null && subject.hasRole(role.trim()) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个角色。
|
||||
*
|
||||
* @param roleNames 角色列表
|
||||
* @return 用户是否具有以下任意一个角色
|
||||
*/
|
||||
public static boolean hasAnyRoles(String[] roleNames) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject != null && roleNames != null) {
|
||||
for (int i = 0; i < roleNames.length; i++) {
|
||||
String role = roleNames[i];
|
||||
if (role != null && subject.hasRole(role.trim()) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具备某权限。
|
||||
*
|
||||
* @param permission 权限名称
|
||||
* @return 用户是否具备某权限
|
||||
*/
|
||||
public static boolean hasPermission(String permission) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return subject != null && subject.isPermitted(permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否不具备某权限,与 hasPermission 逻辑相反。
|
||||
*
|
||||
* @param permission 权限名称
|
||||
* @return 用户是否不具备某权限
|
||||
*/
|
||||
public static boolean lacksPermission(String permission) {
|
||||
return hasPermission(permission) != true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限。
|
||||
*
|
||||
* @param permissions 以 delimeter 为分隔符的权限列表
|
||||
* @param delimeter 权限列表分隔符
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public static boolean hasAnyPermissions(String permissions, String delimeter) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject != null) {
|
||||
if (delimeter == null || delimeter.length() == 0) {
|
||||
delimeter = PERMISSION_NAMES_DELIMETER;
|
||||
}
|
||||
|
||||
for (String permission : permissions.split(delimeter)) {
|
||||
if (permission != null && subject.isPermitted(permission.trim()) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限。
|
||||
*
|
||||
* @param permissions 以 PERMISSION_NAMES_DELIMETER 为分隔符的权限列表
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public static boolean hasAnyPermissions(String permissions) {
|
||||
return hasAnyPermissions(permissions, PERMISSION_NAMES_DELIMETER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限。
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public static boolean hasAnyPermissions(Collection<String> permissions) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject != null && permissions != null) {
|
||||
for (String permission : permissions) {
|
||||
if (permission != null && subject.isPermitted(permission.trim()) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证用户是否具有以下任意一个权限。
|
||||
*
|
||||
* @param permissions 权限列表
|
||||
* @return 用户是否具有以下任意一个权限
|
||||
*/
|
||||
public static boolean hasAnyPermissions(String[] permissions) {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
|
||||
if (subject != null && permissions != null) {
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
String permission = permissions[i];
|
||||
if (permission != null && subject.isPermitted(permission.trim()) == true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前实例sessionId
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getSessionId() {
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
return (String) subject.getSession().getId();
|
||||
}
|
||||
|
||||
|
||||
public static String getUserId() {
|
||||
return getPlatformUid();
|
||||
}
|
||||
|
||||
public static String getUnitId() {
|
||||
return (String) getPrincipalProperty("unitid");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取工会小组id
|
||||
*/
|
||||
public static List<String> getUnionGroupIds() {
|
||||
Dao dao = Mvcs.getIoc().get(Dao.class);
|
||||
List<Sys_user_role> userRole = dao.query(Sys_user_role.class, Cnd.where("roleId", "=", Roles.ghxzzz)
|
||||
.and("userId", "=", getPrincipalProperty("id")));
|
||||
List<String> ids = userRole.stream().map(Sys_user_role::getUnionGroupId).collect(Collectors.toList());
|
||||
return Lang.isNotEmpty(ids) ? ids : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户模块菜单
|
||||
*
|
||||
* @return {@link String}
|
||||
*/
|
||||
public static String userModuleMenus() {
|
||||
List<Sys_menu> menus = (List<Sys_menu>) getPrincipalProperty("moduleMenus");
|
||||
if (Lang.isEmpty(menus)) {
|
||||
SysUserService sysUserService = Mvcs.getIoc().get(SysUserServiceImpl.class);
|
||||
sysUserService.deleteCacheAndUpdate(getUserId());
|
||||
menus = (List<Sys_menu>) getPrincipalProperty("moduleMenus");
|
||||
return Json.toJson(menus);
|
||||
}
|
||||
return Json.toJson(menus);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user