优化权限和上传文件等1
This commit is contained in:
parent
bf9c3c2ec1
commit
642cb0d61e
@ -43,6 +43,10 @@ public enum CodeEnum {
|
|||||||
CREDENTIALS_EXPIRED(3003, "credentials_expired"),
|
CREDENTIALS_EXPIRED(3003, "credentials_expired"),
|
||||||
ACCOUNT_LOCKED(3004, "account_locked"),
|
ACCOUNT_LOCKED(3004, "account_locked"),
|
||||||
USERNAME_NOT_FOUND(3005, "username_not_found"),
|
USERNAME_NOT_FOUND(3005, "username_not_found"),
|
||||||
|
/**
|
||||||
|
* 需要验证码
|
||||||
|
*/
|
||||||
|
NEED_CODE(3006, "need_code"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求错误
|
* 请求错误
|
||||||
|
|||||||
@ -21,12 +21,17 @@ import com.zhgd.xmgl.modules.xz.service.impl.XzSupplierQualificationApplyService
|
|||||||
import com.zhgd.xmgl.security.entity.UserInfo;
|
import com.zhgd.xmgl.security.entity.UserInfo;
|
||||||
import com.zhgd.xmgl.security.util.SecurityUtils;
|
import com.zhgd.xmgl.security.util.SecurityUtils;
|
||||||
import com.zhgd.xmgl.util.EnvironmentUtil;
|
import com.zhgd.xmgl.util.EnvironmentUtil;
|
||||||
|
import com.zhgd.xmgl.util.MapBuilder;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.JSQLParserException;
|
import net.sf.jsqlparser.JSQLParserException;
|
||||||
import net.sf.jsqlparser.expression.Alias;
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
import net.sf.jsqlparser.expression.StringValue;
|
import net.sf.jsqlparser.expression.StringValue;
|
||||||
import net.sf.jsqlparser.expression.*;
|
import net.sf.jsqlparser.expression.*;
|
||||||
|
import net.sf.jsqlparser.expression.Alias;
|
||||||
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
import net.sf.jsqlparser.expression.Parenthesis;
|
||||||
|
import net.sf.jsqlparser.expression.StringValue;
|
||||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||||
@ -41,10 +46,12 @@ import net.sf.jsqlparser.statement.select.Join;
|
|||||||
import net.sf.jsqlparser.statement.select.PlainSelect;
|
import net.sf.jsqlparser.statement.select.PlainSelect;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -70,6 +77,9 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
@Lazy
|
@Lazy
|
||||||
@Autowired
|
@Autowired
|
||||||
private IOcrBuildLogService ocrBuildLogService;
|
private IOcrBuildLogService ocrBuildLogService;
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private IXzSecurityQualityInspectionEnterpriseService xzSecurityQualityInspectionEnterpriseService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Expression getSqlSegment(Expression where, String mappedStatementId) {
|
public Expression getSqlSegment(Expression where, String mappedStatementId) {
|
||||||
@ -143,10 +153,6 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Lazy
|
|
||||||
@Autowired
|
|
||||||
private IXzSecurityQualityInspectionEnterpriseService xzSecurityQualityInspectionEnterpriseService;
|
|
||||||
|
|
||||||
private PlainSelect dataScopeFilterByProject(PlainSelect plainSelect, UserInfo user, Object obj) {
|
private PlainSelect dataScopeFilterByProject(PlainSelect plainSelect, UserInfo user, Object obj) {
|
||||||
JSONObject jo = (JSONObject) obj;
|
JSONObject jo = (JSONObject) obj;
|
||||||
Object parameter = jo.get("parameter");
|
Object parameter = jo.get("parameter");
|
||||||
@ -154,8 +160,28 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
init(plainSelect);
|
init(plainSelect);
|
||||||
//expressions
|
//expressions
|
||||||
List<Expression> expressions = new ArrayList<>();
|
List<Expression> expressions = new ArrayList<>();
|
||||||
|
Long userId = SecurityUtils.getUser().getUserId();
|
||||||
if (!DataScopeInterceptor.findIgnoreDataScope(parameter, ds)) {
|
if (!DataScopeInterceptor.findIgnoreDataScope(parameter, ds)) {
|
||||||
if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.PROJECT_SUB_ACCOUNT.getValue())) {
|
if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.ENTERPRISE_ADMINISTRATOR_ACCOUNT.getValue())) {
|
||||||
|
filterCompany(plainSelect, ds, expressions, userId, (userFilterItem) -> get1CompanySql(userId, userFilterItem));
|
||||||
|
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.ENTERPRISE_DISTRICT_ACCOUNT.getValue())) {
|
||||||
|
filterCompany(plainSelect, ds, expressions, userId, (userFilterItem) -> get2CompanySql(userId, userFilterItem));
|
||||||
|
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.ENTERPRISE_CITY_ACCOUNT.getValue())) {
|
||||||
|
filterCompany(plainSelect, ds, expressions, userId, (userFilterItem) -> get3CompanySql(userId, userFilterItem));
|
||||||
|
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.ENTERPRISE_SUB_ACCOUNT.getValue())) {
|
||||||
|
filterCompany(plainSelect, ds, expressions, userId, (userFilterItem) -> get4CompanySql(userId, userFilterItem));
|
||||||
|
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.PROJECT_ACCOUNT.getValue())) {
|
||||||
|
List<String> userFilterItems = getAuthUserFilterItem(plainSelect, ds);
|
||||||
|
for (String userFilterItem : userFilterItems) {
|
||||||
|
String sql = StrUtil.format(" \n" +
|
||||||
|
" {} in (\n" +
|
||||||
|
" select u.sn\n" +
|
||||||
|
" from system_user u\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" )", userFilterItem, userId);
|
||||||
|
expressions.add(parseCondExpression(sql));
|
||||||
|
}
|
||||||
|
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.PROJECT_SUB_ACCOUNT.getValue())) {
|
||||||
List<String> authEnterpriseIds = userEnterpriseService.getEnterpriseIdsIfSubProject();
|
List<String> authEnterpriseIds = userEnterpriseService.getEnterpriseIdsIfSubProject();
|
||||||
authEnterpriseIds.add("0");
|
authEnterpriseIds.add("0");
|
||||||
List<String> filterEnterprises = getNeedFilterLeftExpression(plainSelect, getFieldEnterpriseTables(), ds);
|
List<String> filterEnterprises = getNeedFilterLeftExpression(plainSelect, getFieldEnterpriseTables(), ds);
|
||||||
@ -182,14 +208,8 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
videoItems = videoItems.stream().map(s -> "'" + s + "'").collect(Collectors.toList());
|
videoItems = videoItems.stream().map(s -> "'" + s + "'").collect(Collectors.toList());
|
||||||
for (String filterAi : filterAis) {
|
for (String filterAi : filterAis) {
|
||||||
String sql = StrUtil.format(" ({}.hardware_id in ({}) OR ({}.quality_region_id in (select distinct quality_region_id from quality_region_to_user where user_id = {}))) ",
|
String sql = StrUtil.format(" ({}.hardware_id in ({}) OR ({}.quality_region_id in (select distinct quality_region_id from quality_region_to_user where user_id = {}))) ",
|
||||||
filterAi, StrUtil.join(",", videoItems), filterAi, SecurityUtils.getUser().getUserId());
|
filterAi, StrUtil.join(",", videoItems), filterAi, userId);
|
||||||
Expression expression = null;
|
expressions.add(parseCondExpression(sql));
|
||||||
try {
|
|
||||||
expression = CCJSqlParserUtil.parseCondExpression(sql);
|
|
||||||
expressions.add(expression);
|
|
||||||
} catch (JSQLParserException e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +221,7 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
for (String filterEnterprise : filterOcrBuildLogTables) {
|
for (String filterEnterprise : filterOcrBuildLogTables) {
|
||||||
String uploaderIdField = StrUtil.subBefore(filterEnterprise, ".", false) + "." + "uploader_id";
|
String uploaderIdField = StrUtil.subBefore(filterEnterprise, ".", false) + "." + "uploader_id";
|
||||||
String sql = StrUtil.format(" ( ({} in (select ocr_build_log_id from ocr_build_log_enterprise where enterprise_id in ({}))) OR ( {} = {}))",
|
String sql = StrUtil.format(" ( ({} in (select ocr_build_log_id from ocr_build_log_enterprise where enterprise_id in ({}))) OR ( {} = {}))",
|
||||||
filterEnterprise, StrUtil.join(",", authEnterpriseIds), uploaderIdField, SecurityUtils.getUser().getUserId());
|
filterEnterprise, StrUtil.join(",", authEnterpriseIds), uploaderIdField, userId);
|
||||||
try {
|
try {
|
||||||
Expression expression = CCJSqlParserUtil.parseCondExpression(sql);
|
Expression expression = CCJSqlParserUtil.parseCondExpression(sql);
|
||||||
expressions.add(expression);
|
expressions.add(expression);
|
||||||
@ -228,7 +248,7 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
|
|
||||||
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.SUPPLIER.getValue())) {
|
} else if (Objects.equals(user.getAccountType(), SystemUserAccountTypeEnum.SUPPLIER.getValue())) {
|
||||||
List<String> filterEnterprises = getNeedFilterLeftExpression(plainSelect, getFieldEnterpriseTables(), ds);
|
List<String> filterEnterprises = getNeedFilterLeftExpression(plainSelect, getFieldEnterpriseTables(), ds);
|
||||||
EnterpriseInfo ei = enterpriseInfoMapper.getXzSupplierByUserId(SecurityUtils.getUser().getUserId());
|
EnterpriseInfo ei = enterpriseInfoMapper.getXzSupplierByUserId(userId);
|
||||||
Long id;
|
Long id;
|
||||||
if (ei == null) {
|
if (ei == null) {
|
||||||
id = -1L;
|
id = -1L;
|
||||||
@ -239,13 +259,7 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
String sql = StrUtil.format(" ({} = {} OR {} IN ( SELECT DISTINCT t.enterprise_id FROM " +
|
String sql = StrUtil.format(" ({} = {} OR {} IN ( SELECT DISTINCT t.enterprise_id FROM " +
|
||||||
"(SELECT t.id FROM project_enterprise t WHERE t.enterprise_id = {}) t2 join project_enterprise t on find_in_set( t2.id, ancestors ) )) ",
|
"(SELECT t.id FROM project_enterprise t WHERE t.enterprise_id = {}) t2 join project_enterprise t on find_in_set( t2.id, ancestors ) )) ",
|
||||||
filterEnterprise, id, filterEnterprise, id);
|
filterEnterprise, id, filterEnterprise, id);
|
||||||
Expression expression = null;
|
expressions.add(parseCondExpression(sql));
|
||||||
try {
|
|
||||||
expression = CCJSqlParserUtil.parseCondExpression(sql);
|
|
||||||
expressions.add(expression);
|
|
||||||
} catch (JSQLParserException e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//解析ai预警
|
//解析ai预警
|
||||||
@ -258,17 +272,10 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
videoItems = videoItems.stream().map(s -> "'" + s + "'").collect(Collectors.toList());
|
videoItems = videoItems.stream().map(s -> "'" + s + "'").collect(Collectors.toList());
|
||||||
for (String filterAi : filterAis) {
|
for (String filterAi : filterAis) {
|
||||||
String sql = StrUtil.format(" ({}.hardware_id in ({}) OR ({}.quality_region_id in (select distinct quality_region_id from quality_region_to_user where user_id = {}))) ",
|
String sql = StrUtil.format(" ({}.hardware_id in ({}) OR ({}.quality_region_id in (select distinct quality_region_id from quality_region_to_user where user_id = {}))) ",
|
||||||
filterAi, StrUtil.join(",", videoItems), filterAi, SecurityUtils.getUser().getUserId());
|
filterAi, StrUtil.join(",", videoItems), filterAi, userId);
|
||||||
Expression expression = null;
|
expressions.add(parseCondExpression(sql));
|
||||||
try {
|
|
||||||
expression = CCJSqlParserUtil.parseCondExpression(sql);
|
|
||||||
expressions.add(expression);
|
|
||||||
} catch (JSQLParserException e) {
|
|
||||||
log.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (expressions.size() > 0) {
|
if (expressions.size() > 0) {
|
||||||
Expression dataExpression;
|
Expression dataExpression;
|
||||||
@ -339,6 +346,155 @@ public class DataScopeHandler implements DataPermissionHandler {
|
|||||||
return plainSelect;
|
return plainSelect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 过滤企业的用户权限
|
||||||
|
*
|
||||||
|
* @param plainSelect
|
||||||
|
* @param ds
|
||||||
|
* @param expressions
|
||||||
|
* @param userId
|
||||||
|
* @param companySqlProvider
|
||||||
|
*/
|
||||||
|
private void filterCompany(PlainSelect plainSelect, DataScope ds, List<Expression> expressions, Long userId, Function<String, String> companySqlProvider) {
|
||||||
|
List<String> userFilterItems = getAuthUserFilterItem(plainSelect, ds);
|
||||||
|
List<String> ownFilterItems = getOwnUserFilterItem(plainSelect, ds);
|
||||||
|
for (int i = 0; i < userFilterItems.size(); i++) {
|
||||||
|
String userFilterItem = userFilterItems.get(i);
|
||||||
|
String sql = StrUtil.format(" (({}) OR {}={})",
|
||||||
|
companySqlProvider.apply(userFilterItem),
|
||||||
|
ownFilterItems.get(i),
|
||||||
|
userId);
|
||||||
|
expressions.add(parseCondExpression(sql));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private List<String> getOwnUserFilterItem(PlainSelect plainSelect, DataScope ds) {
|
||||||
|
List<String> ownFilterItems = getNeedFilterLeftExpression(plainSelect, new MapBuilder<String, String>()
|
||||||
|
.put("system_user", "user_id")
|
||||||
|
.build(), ds);
|
||||||
|
return ownFilterItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取企业查询的用户权限的sql的表和字段
|
||||||
|
*
|
||||||
|
* @param plainSelect
|
||||||
|
* @param ds
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private List<String> getAuthUserFilterItem(PlainSelect plainSelect, DataScope ds) {
|
||||||
|
List<String> userFilterItems = getNeedFilterLeftExpression(plainSelect, new MapBuilder<String, String>()
|
||||||
|
.put("system_user", "sn")
|
||||||
|
.build(), ds);
|
||||||
|
return userFilterItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String get1CompanySql(Long userId, String userFilterItem) {
|
||||||
|
String sql = StrUtil.format(" \n" +
|
||||||
|
" {} in (\n" +
|
||||||
|
" SELECT DISTINCT\n" +
|
||||||
|
" c.company_sn \n" +
|
||||||
|
" FROM\n" +
|
||||||
|
" company c\n" +
|
||||||
|
" JOIN company head ON c.headquarters_sn = head.company_sn\n" +
|
||||||
|
" JOIN system_user u ON head.company_sn = u.sn \n" +
|
||||||
|
" WHERE\n" +
|
||||||
|
" u.user_id ={}\n" +
|
||||||
|
" UNION ALL\n" +
|
||||||
|
" select a.project_sn\n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" INNER JOIN company b ON cp.parent_id = b.company_id\n" +
|
||||||
|
" INNER JOIN company f ON b.parent_id = f.company_id\n" +
|
||||||
|
" JOIN system_user u on f.headquarters_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" )", userFilterItem, userId, userId);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String get2CompanySql(Long userId, String userFilterItem) {
|
||||||
|
String sql = StrUtil.format(" \n" +
|
||||||
|
" {} in (\n" +
|
||||||
|
" SELECT DISTINCT\n" +
|
||||||
|
" cp.company_sn \n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" INNER JOIN company b ON cp.parent_id = b.company_id\n" +
|
||||||
|
" INNER JOIN company f ON b.parent_id = f.company_id\n" +
|
||||||
|
" JOIN system_user u on f.company_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" UNION ALL\n" +
|
||||||
|
" SELECT DISTINCT\n" +
|
||||||
|
" b.company_sn \n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" INNER JOIN company b ON cp.parent_id = b.company_id\n" +
|
||||||
|
" INNER JOIN company f ON b.parent_id = f.company_id\n" +
|
||||||
|
" JOIN system_user u on f.company_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" UNION ALL\n" +
|
||||||
|
" select a.project_sn\n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" INNER JOIN company b ON cp.parent_id = b.company_id\n" +
|
||||||
|
" INNER JOIN company f ON b.parent_id = f.company_id\n" +
|
||||||
|
" JOIN system_user u on f.company_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" )", userFilterItem, userId, userId, userId);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String get3CompanySql(Long userId, String userFilterItem) {
|
||||||
|
String sql = StrUtil.format(" \n" +
|
||||||
|
" {} in (\n" +
|
||||||
|
" SELECT DISTINCT\n" +
|
||||||
|
" cp.company_sn \n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" INNER JOIN company b ON cp.parent_id = b.company_id\n" +
|
||||||
|
" JOIN system_user u on b.company_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" UNION ALL\n" +
|
||||||
|
" select a.project_sn\n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" INNER JOIN company b ON cp.parent_id = b.company_id\n" +
|
||||||
|
" JOIN system_user u on b.company_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" )", userFilterItem, userId, userId);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String get4CompanySql(Long userId, String userFilterItem) {
|
||||||
|
String sql = StrUtil.format(" \n" +
|
||||||
|
" {} in (\n" +
|
||||||
|
" select a.project_sn\n" +
|
||||||
|
" FROM project a\n" +
|
||||||
|
" INNER JOIN company cp ON a.company_sn = cp.company_sn\n" +
|
||||||
|
" JOIN system_user u on cp.company_sn=u.sn\n" +
|
||||||
|
" WHERE u.user_id={}\n" +
|
||||||
|
" )", userFilterItem, userId);
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取转换后的sql表达式
|
||||||
|
*
|
||||||
|
* @param sql
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Expression parseCondExpression(String sql) {
|
||||||
|
Expression expression = null;
|
||||||
|
try {
|
||||||
|
expression = CCJSqlParserUtil.parseCondExpression(sql);
|
||||||
|
} catch (JSQLParserException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取需要过滤的表别名或加字段
|
* 获取需要过滤的表别名或加字段
|
||||||
*
|
*
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import cn.hutool.core.util.StrUtil;
|
|||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
|||||||
@ -116,4 +116,8 @@ public interface Cts {
|
|||||||
* 手机号登录的验证码前缀
|
* 手机号登录的验证码前缀
|
||||||
*/
|
*/
|
||||||
String LOGIN_VERIFICATION_CODE = "LOGIN_VERIFICATION_CODE_";
|
String LOGIN_VERIFICATION_CODE = "LOGIN_VERIFICATION_CODE_";
|
||||||
|
/**
|
||||||
|
* 敏感字符
|
||||||
|
*/
|
||||||
|
String SENSITIVE_CHAR = "******";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package com.zhgd.xmgl.modules.basicdata.controller;
|
package com.zhgd.xmgl.modules.basicdata.controller;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.captcha.CaptchaUtil;
|
||||||
|
import cn.hutool.captcha.LineCaptcha;
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
@ -26,11 +28,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,6 +105,33 @@ public class LoginController {
|
|||||||
return Result.success(resultMap);
|
return Result.success(resultMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "获取登录的图片验证码", notes = "获取登录的图片验证码", httpMethod = "GET")
|
||||||
|
@GetMapping("/login/captcha")
|
||||||
|
public void getCaptcha(HttpServletResponse response) throws IOException {
|
||||||
|
String captchaId = UUID.randomUUID().toString();
|
||||||
|
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 40, 4, 50);
|
||||||
|
redisRepository.setExpire("CAPTCHA:" + captchaId, captcha.getCode(), 300);
|
||||||
|
response.setHeader("captcha-id", captchaId);
|
||||||
|
response.setHeader("access-control-expose-headers", "captcha-id");
|
||||||
|
response.setContentType("image/png");
|
||||||
|
captcha.write(response.getOutputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@OperLog(operModul = "用户登录", operType = "账号密码登录(带验证码)", operDesc = "账号密码登录(带验证码)")
|
||||||
|
@ApiOperation(value = "账号密码登录(带验证码)", notes = "账号密码登录(带验证码)", httpMethod = "POST")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "account", required = true, value = "登录账号", paramType = "body"),
|
||||||
|
@ApiImplicitParam(name = "md5Password", required = true, value = "账号md5密码登录", paramType = "body"),
|
||||||
|
@ApiImplicitParam(name = "timestamp", required = true, value = "时间戳", paramType = "body"),
|
||||||
|
@ApiImplicitParam(name = "code", required = true, value = "验证码", paramType = "body"),
|
||||||
|
})
|
||||||
|
@PostMapping(value = "/verify/login")
|
||||||
|
public Result<Map<String, Object>> verifyLogin(@RequestBody Map<String, Object> map, @RequestHeader("captcha-id") String captchaId) {
|
||||||
|
map.put("captchaId", captchaId);
|
||||||
|
Map<String, Object> resultMap = systemUserService.verifyLogin(map);
|
||||||
|
return Result.success(resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
@OperLog(operModul = "用户登录", operType = "用户id登录", operDesc = "用户id登录")
|
@OperLog(operModul = "用户登录", operType = "用户id登录", operDesc = "用户id登录")
|
||||||
@ApiOperation(value = "用户id登录", notes = "用户id登录", httpMethod = "POST")
|
@ApiOperation(value = "用户id登录", notes = "用户id登录", httpMethod = "POST")
|
||||||
@ApiImplicitParams({
|
@ApiImplicitParams({
|
||||||
|
|||||||
@ -42,10 +42,11 @@ public class UploadFileController {
|
|||||||
}
|
}
|
||||||
Map<String, Object> resultMap = new HashMap<>(16);
|
Map<String, Object> resultMap = new HashMap<>(16);
|
||||||
try {
|
try {
|
||||||
resultMap = uploadFileService.uploadImage(files);
|
resultMap = uploadFileService.uploadImageSafety(files);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
throw new OpenAlertException("安全限制: " + e.getMessage(), e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("error:", e);
|
throw new OpenAlertException("上传失败: " + e.getMessage(), e);
|
||||||
|
|
||||||
}
|
}
|
||||||
return resultMap;
|
return resultMap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import java.util.*;
|
|||||||
* @version: V1.0
|
* @version: V1.0
|
||||||
*/
|
*/
|
||||||
@Mapper
|
@Mapper
|
||||||
|
@DataScope(includeTable = {"system_user"})
|
||||||
public interface SystemUserMapper extends BaseMapper<SystemUser> {
|
public interface SystemUserMapper extends BaseMapper<SystemUser> {
|
||||||
/**
|
/**
|
||||||
* 根据企业或项目SN查找账号列表
|
* 根据企业或项目SN查找账号列表
|
||||||
@ -37,6 +38,7 @@ public interface SystemUserMapper extends BaseMapper<SystemUser> {
|
|||||||
* @param page
|
* @param page
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@DataScope(includeTable = {"system_user"})
|
||||||
Page<SystemUser> getSystemUsersBySn(@Param("param") Map<String, Object> map, Page<SystemUser> page);
|
Page<SystemUser> getSystemUsersBySn(@Param("param") Map<String, Object> map, Page<SystemUser> page);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -381,4 +381,11 @@ public interface ISystemUserService extends IService<SystemUser> {
|
|||||||
*/
|
*/
|
||||||
Map<Long, SystemUser> getUserMapByProjectSn(String projectSn);
|
Map<Long, SystemUser> getUserMapByProjectSn(String projectSn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带验证码登录
|
||||||
|
*
|
||||||
|
* @param map
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Map<String, Object> verifyLogin(Map<String, Object> map);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,8 +25,11 @@ import java.util.Map;
|
|||||||
* @date 2017年7月20日 下午4:38:27
|
* @date 2017年7月20日 下午4:38:27
|
||||||
*/
|
*/
|
||||||
public interface UploadFileService {
|
public interface UploadFileService {
|
||||||
|
Map<String, Object> uploadImageSafety(MultipartFile[] files) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 文件上传
|
* 文件上传
|
||||||
|
*
|
||||||
* @param files
|
* @param files
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
|||||||
@ -19,8 +19,10 @@ import com.wflow.bean.entity.WflowModels;
|
|||||||
import com.wflow.mapper.WflowModelsMapper;
|
import com.wflow.mapper.WflowModelsMapper;
|
||||||
import com.zhgd.exception.CustomException;
|
import com.zhgd.exception.CustomException;
|
||||||
import com.zhgd.jeecg.common.api.vo.Result;
|
import com.zhgd.jeecg.common.api.vo.Result;
|
||||||
|
import com.zhgd.jeecg.common.execption.CodeEnum;
|
||||||
import com.zhgd.jeecg.common.execption.OpenAlertException;
|
import com.zhgd.jeecg.common.execption.OpenAlertException;
|
||||||
import com.zhgd.jeecg.common.mybatis.EntityMap;
|
import com.zhgd.jeecg.common.mybatis.EntityMap;
|
||||||
|
import com.zhgd.jeecg.common.util.SpringContextUtils;
|
||||||
import com.zhgd.redis.lock.RedisRepository;
|
import com.zhgd.redis.lock.RedisRepository;
|
||||||
import com.zhgd.xmgl.entity.sj.JwtPayloadUserInfo;
|
import com.zhgd.xmgl.entity.sj.JwtPayloadUserInfo;
|
||||||
import com.zhgd.xmgl.modules.basicdata.entity.*;
|
import com.zhgd.xmgl.modules.basicdata.entity.*;
|
||||||
@ -74,6 +76,7 @@ import org.apache.commons.collections.MapUtils;
|
|||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.flowable.engine.HistoryService;
|
import org.flowable.engine.HistoryService;
|
||||||
import org.flowable.engine.history.HistoricProcessInstance;
|
import org.flowable.engine.history.HistoricProcessInstance;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
@ -230,20 +233,20 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
log.info(passwordEncoder.encode(password));
|
log.info(passwordEncoder.encode(password));
|
||||||
|
|
||||||
//登录失败次数不得多于5次,登录失败后锁定时间不少于10分钟
|
//登录失败次数不得多于5次,登录失败后锁定时间不少于10分钟
|
||||||
String key = PW_FAILED_COUNT_PREFIX + EnvironmentUtil.getActiveEnvironment() + ":" + account;
|
String failedCountKey = getFailedCountKey(account);
|
||||||
Integer num = (Integer) redisRepository.get(key);
|
Integer num = (Integer) redisRepository.get(failedCountKey);
|
||||||
int i = 5;
|
int i = 10;
|
||||||
if (num != null && num >= i) {
|
if (num != null && num >= i) {
|
||||||
throw new OpenAlertException("您已登录失败次数达到5次,锁定账号时间10分钟,请稍后重试");
|
throw new OpenAlertException("账户或密码错误,登录失败次数超出阈值,请10分钟之后再尝试");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systemUser == null || systemUser.getUserId() == null) {
|
if (systemUser == null || systemUser.getUserId() == null) {
|
||||||
log.info("查询不到systemUser");
|
log.info("查询不到systemUser");
|
||||||
failedPrompt(key, num);
|
failedPrompt(failedCountKey, num);
|
||||||
} else {
|
} else {
|
||||||
if (!passwordEncoder.matches(password, systemUser.getPassword())) {
|
if (!passwordEncoder.matches(password, systemUser.getPassword())) {
|
||||||
log.info("密码不正确,原:{},现:{}", password, systemUser.getPassword());
|
log.info("密码不正确,原:{},现:{}", password, systemUser.getPassword());
|
||||||
failedPrompt(key, num);
|
failedPrompt(failedCountKey, num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,6 +269,17 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录失败存redis的key
|
||||||
|
*
|
||||||
|
* @param account
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
private String getFailedCountKey(String account) {
|
||||||
|
return PW_FAILED_COUNT_PREFIX + EnvironmentUtil.getActiveEnvironment() + ":" + account + ":" + IpUtil.getRemortIP(SpringContextUtils.getHttpServletRequest());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 失败提示抛出异常
|
* 失败提示抛出异常
|
||||||
*
|
*
|
||||||
@ -1595,11 +1609,11 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
String timestamp = MapUtils.getString(map, "timestamp");
|
String timestamp = MapUtils.getString(map, "timestamp");
|
||||||
|
|
||||||
//登录失败次数不得多于5次,登录失败后锁定时间不少于10分钟
|
//登录失败次数不得多于5次,登录失败后锁定时间不少于10分钟
|
||||||
String key = PW_FAILED_COUNT_PREFIX + EnvironmentUtil.getActiveEnvironment() + ":" + account;
|
String failedCountKey = getFailedCountKey(account);
|
||||||
Integer num = (Integer) redisRepository.get(key);
|
Integer num = (Integer) redisRepository.get(failedCountKey);
|
||||||
int loginNum = 5;
|
int loginNum = 10;
|
||||||
if (num != null && num >= loginNum) {
|
if (num != null && num >= loginNum) {
|
||||||
throw new OpenAlertException("您已登录失败次数达到5次,锁定账号时间10分钟,请稍后重试");
|
throw new OpenAlertException("用户名或密码错误,您已登录失败次数达到5次,锁定时间10分钟,请稍后重试");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (systemUser == null || systemUser.getUserId() == null) {
|
if (systemUser == null || systemUser.getUserId() == null) {
|
||||||
@ -1610,19 +1624,21 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
.eq(XzRegistry::getApprovalProcess, 1)
|
.eq(XzRegistry::getApprovalProcess, 1)
|
||||||
);
|
);
|
||||||
if (CollUtil.isNotEmpty(registryList)) {
|
if (CollUtil.isNotEmpty(registryList)) {
|
||||||
throw new OpenAlertException("账号待审核");
|
//账号待审核
|
||||||
|
throw new OpenAlertException("用户名或密码错误");
|
||||||
} else {
|
} else {
|
||||||
throw new OpenAlertException("账号不存在");
|
//账号不存在
|
||||||
|
throw new OpenAlertException("用户名或密码错误");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!StringUtils.equalsIgnoreCase(SecureUtil.md5(SecureUtil.md5(systemUser.getShowPassword()) + timestamp), md5Password)) {
|
if (!StringUtils.equalsIgnoreCase(SecureUtil.md5(SecureUtil.md5(systemUser.getShowPassword()) + timestamp), md5Password)) {
|
||||||
log.info("密码不正确,原:{},现:{}", md5Password, systemUser.getPassword());
|
log.info("密码不正确,原:{},现:{}", md5Password, systemUser.getPassword());
|
||||||
failedPrompt(key, num);
|
failedPrompt(failedCountKey, num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(account, systemUser.getShowPassword()));
|
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(account, systemUser.getShowPassword()));
|
||||||
|
redisRepository.del(failedCountKey);
|
||||||
return doLogin(map, systemUser);
|
return doLogin(map, systemUser);
|
||||||
} catch (AuthenticationException e) {
|
} catch (AuthenticationException e) {
|
||||||
log.error("error:", e);
|
log.error("error:", e);
|
||||||
@ -1635,7 +1651,8 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
try {
|
try {
|
||||||
SystemUser systemUser = systemUserMapper.selectById(MapUtils.getString(map, "userId"));
|
SystemUser systemUser = systemUserMapper.selectById(MapUtils.getString(map, "userId"));
|
||||||
if (systemUser == null || systemUser.getUserId() == null) {
|
if (systemUser == null || systemUser.getUserId() == null) {
|
||||||
throw new OpenAlertException("账号不存在");
|
//账号不存在
|
||||||
|
throw new OpenAlertException("用户名或密码错误");
|
||||||
}
|
}
|
||||||
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(systemUser.getAccount(), systemUser.getShowPassword()));
|
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(systemUser.getAccount(), systemUser.getShowPassword()));
|
||||||
return doLogin(map, systemUser);
|
return doLogin(map, systemUser);
|
||||||
@ -1705,7 +1722,8 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
Integer count = Math.toIntExact(systemUserMapper.selectCount(new LambdaQueryWrapper<SystemUser>()
|
Integer count = Math.toIntExact(systemUserMapper.selectCount(new LambdaQueryWrapper<SystemUser>()
|
||||||
.eq(SystemUser::getAccount, systemUser.getAccount())));
|
.eq(SystemUser::getAccount, systemUser.getAccount())));
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
throw new OpenAlertException("账号不存在");
|
//账号不存在
|
||||||
|
throw new OpenAlertException("用户名或密码错误");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1719,7 +1737,8 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
SystemUser su = systemUserMapper.selectOne(new LambdaQueryWrapper<SystemUser>()
|
SystemUser su = systemUserMapper.selectOne(new LambdaQueryWrapper<SystemUser>()
|
||||||
.eq(SystemUser::getAccount, systemUser.getAccount()));
|
.eq(SystemUser::getAccount, systemUser.getAccount()));
|
||||||
if (su == null) {
|
if (su == null) {
|
||||||
throw new OpenAlertException("账号不存在");
|
//账号不存在
|
||||||
|
throw new OpenAlertException("用户名或密码错误");
|
||||||
}
|
}
|
||||||
String code = NumberUtils.randomNum(6);
|
String code = NumberUtils.randomNum(6);
|
||||||
redisRepository.set(UPDATE_PW_EMAIL_CODE + su.getAccount(), code, 300L);
|
redisRepository.set(UPDATE_PW_EMAIL_CODE + su.getAccount(), code, 300L);
|
||||||
@ -1881,4 +1900,44 @@ public class SystemUserServiceImpl extends ServiceImpl<SystemUserMapper, SystemU
|
|||||||
return this.list(new LambdaQueryWrapper<SystemUser>()
|
return this.list(new LambdaQueryWrapper<SystemUser>()
|
||||||
.eq(SystemUser::getSn, projectSn)).stream().collect(Collectors.toMap(SystemUser::getUserId, Function.identity(), (o1, o2) -> o1));
|
.eq(SystemUser::getSn, projectSn)).stream().collect(Collectors.toMap(SystemUser::getUserId, Function.identity(), (o1, o2) -> o1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> verifyLogin(Map<String, Object> map) {
|
||||||
|
String account = MapUtils.getString(map, "account");
|
||||||
|
String code = MapUtils.getString(map, "code");
|
||||||
|
// 从缓存获取失败次数
|
||||||
|
String failedCountKey = getFailedCountKey(account);
|
||||||
|
Integer failCount = (Integer) redisRepository.get(failedCountKey);
|
||||||
|
// 如果失败次数≥3,但请求没带验证码或验证码错误,则拒绝
|
||||||
|
if (failCount != null && failCount >= 2) {
|
||||||
|
if (StrUtil.isBlank(code)) {
|
||||||
|
throw new OpenAlertException(CodeEnum.NEED_CODE.getCode(), "请输入验证码");
|
||||||
|
}
|
||||||
|
//校验验证码
|
||||||
|
validCode(code, MapUtils.getString(map, "captchaId"));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return md5Login(map);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new OpenAlertException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验验证码
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @param captchaId
|
||||||
|
*/
|
||||||
|
private void validCode(String code, String captchaId) {
|
||||||
|
String key = "CAPTCHA:" + captchaId;
|
||||||
|
String storedCode = (String) redisRepository.get(key);
|
||||||
|
if (storedCode == null) {
|
||||||
|
throw new OpenAlertException("验证码过期");
|
||||||
|
}
|
||||||
|
if (!storedCode.equalsIgnoreCase(code)) {
|
||||||
|
redisRepository.del(key);
|
||||||
|
throw new OpenAlertException("验证码错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,11 +7,13 @@ import com.zhgd.file.FileUtil;
|
|||||||
import com.zhgd.jeecg.common.api.vo.Result;
|
import com.zhgd.jeecg.common.api.vo.Result;
|
||||||
import com.zhgd.xmgl.modules.basicdata.entity.vo.UploadImageVo;
|
import com.zhgd.xmgl.modules.basicdata.entity.vo.UploadImageVo;
|
||||||
import com.zhgd.xmgl.modules.basicdata.service.UploadFileService;
|
import com.zhgd.xmgl.modules.basicdata.service.UploadFileService;
|
||||||
|
import com.zhgd.xmgl.util.FileSecurityUtil;
|
||||||
import com.zhgd.xmgl.util.MessageUtil;
|
import com.zhgd.xmgl.util.MessageUtil;
|
||||||
import com.zhgd.xmgl.util.UrlUtil;
|
import com.zhgd.xmgl.util.UrlUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@ -19,6 +21,14 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.nio.file.attribute.PosixFilePermission;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
@ -40,6 +50,164 @@ public class UploadFileServiceImpl implements UploadFileService {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private FileStorageService fileStorageService;
|
private FileStorageService fileStorageService;
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private FileSecurityUtil fileSecurityUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全文件上传方法
|
||||||
|
*/
|
||||||
|
public Map<String, Object> uploadImageSafety(MultipartFile[] files) throws Exception {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
List<Map<String, Object>> dataList = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
for (MultipartFile file : files) {
|
||||||
|
// 1. 安全检查
|
||||||
|
if (!validateFile(file)) {
|
||||||
|
throw new SecurityException("文件安全检查未通过: " + file.getOriginalFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 生成安全文件名
|
||||||
|
String safeFilename = fileSecurityUtil.generateSafeFilename(file.getOriginalFilename());
|
||||||
|
|
||||||
|
// 3. 保存文件
|
||||||
|
String filePath = saveFileSecurely(file, safeFilename);
|
||||||
|
|
||||||
|
// 4. 构建返回数据(保持原有格式)
|
||||||
|
Map<String, Object> dataMap = new HashMap<>();
|
||||||
|
dataMap.put("filename", safeFilename);
|
||||||
|
dataMap.put("imageUrl", safeFilename); // 只返回文件名,隐藏路径
|
||||||
|
|
||||||
|
// 构建fileInfo对象
|
||||||
|
Map<String, Object> fileInfo = buildFileInfo(file, safeFilename, filePath);
|
||||||
|
dataMap.put("fileInfo", fileInfo);
|
||||||
|
|
||||||
|
dataList.add(dataMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.put("data", dataList);
|
||||||
|
result.put("status", "SUCCESS");
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
result.put("status", "ERROR");
|
||||||
|
result.put("data", Collections.emptyList());
|
||||||
|
result.put("msg", "文件上传失败: " + ex.getMessage());
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建fileInfo对象(保持原有结构)
|
||||||
|
*/
|
||||||
|
private Map<String, Object> buildFileInfo(MultipartFile file, String safeFilename, String filePath) {
|
||||||
|
Map<String, Object> fileInfo = new HashMap<>();
|
||||||
|
String originalFilename = file.getOriginalFilename();
|
||||||
|
String ext = originalFilename.substring(originalFilename.lastIndexOf(".") + 1).toLowerCase();
|
||||||
|
|
||||||
|
fileInfo.put("id", null);
|
||||||
|
fileInfo.put("url", safeFilename); // 只返回文件名,隐藏真实路径
|
||||||
|
fileInfo.put("size", String.valueOf(file.getSize()));
|
||||||
|
fileInfo.put("filename", safeFilename);
|
||||||
|
fileInfo.put("originalFilename", originalFilename);
|
||||||
|
fileInfo.put("basePath", ""); // 隐藏真实路径
|
||||||
|
fileInfo.put("path", ""); // 隐藏真实路径
|
||||||
|
fileInfo.put("ext", ext);
|
||||||
|
fileInfo.put("contentType", file.getContentType());
|
||||||
|
fileInfo.put("platform", "local");
|
||||||
|
fileInfo.put("thUrl", null);
|
||||||
|
fileInfo.put("thFilename", null);
|
||||||
|
fileInfo.put("thSize", null);
|
||||||
|
fileInfo.put("thContentType", null);
|
||||||
|
fileInfo.put("objectId", null);
|
||||||
|
fileInfo.put("objectType", null);
|
||||||
|
fileInfo.put("attr", new HashMap<>());
|
||||||
|
fileInfo.put("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全面的文件验证
|
||||||
|
*/
|
||||||
|
private boolean validateFile(MultipartFile file) throws IOException {
|
||||||
|
// 1. 文件大小检查
|
||||||
|
if (!fileSecurityUtil.isValidSize(file)) {
|
||||||
|
throw new IllegalArgumentException("文件大小超过限制");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 扩展名检查
|
||||||
|
if (!fileSecurityUtil.isValidExtension(file.getOriginalFilename())) {
|
||||||
|
throw new SecurityException("不支持的文件类型");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. MIME类型检查
|
||||||
|
if (!fileSecurityUtil.isValidMimeType(file)) {
|
||||||
|
throw new SecurityException("文件MIME类型不匹配");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 内容安全检查
|
||||||
|
if (!fileSecurityUtil.isFileContentSafe(file)) {
|
||||||
|
throw new SecurityException("文件内容安全检查未通过");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全保存文件
|
||||||
|
*/
|
||||||
|
private String saveFileSecurely(MultipartFile file, String filename)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
Path uploadPath = Paths.get(basePath);
|
||||||
|
|
||||||
|
// 确保目录存在且权限正确
|
||||||
|
if (!Files.exists(uploadPath)) {
|
||||||
|
Files.createDirectories(uploadPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置目录权限(禁止执行)
|
||||||
|
setDirectoryPermissions(uploadPath);
|
||||||
|
|
||||||
|
Path filePath = uploadPath.resolve(filename);
|
||||||
|
|
||||||
|
// 保存文件
|
||||||
|
Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
|
||||||
|
// 设置文件权限(只读)
|
||||||
|
setFilePermissions(filePath);
|
||||||
|
|
||||||
|
return filePath.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置目录权限(禁止执行)
|
||||||
|
*/
|
||||||
|
private void setDirectoryPermissions(Path path) throws IOException {
|
||||||
|
// Linux系统设置权限
|
||||||
|
if (!System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||||
|
Set<PosixFilePermission> perms = new HashSet<>();
|
||||||
|
perms.add(PosixFilePermission.OWNER_READ);
|
||||||
|
perms.add(PosixFilePermission.OWNER_WRITE);
|
||||||
|
perms.add(PosixFilePermission.GROUP_READ);
|
||||||
|
Files.setPosixFilePermissions(path, perms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置文件权限(只读)
|
||||||
|
*/
|
||||||
|
private void setFilePermissions(Path path) throws IOException {
|
||||||
|
// Linux系统设置权限
|
||||||
|
if (!System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||||
|
Set<PosixFilePermission> perms = new HashSet<>();
|
||||||
|
perms.add(PosixFilePermission.OWNER_READ);
|
||||||
|
perms.add(PosixFilePermission.GROUP_READ);
|
||||||
|
Files.setPosixFilePermissions(path, perms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws Exception 2017年7月20日 下午5:17:36 @throws
|
* @throws Exception 2017年7月20日 下午5:17:36 @throws
|
||||||
|
|||||||
126
src/main/java/com/zhgd/xmgl/util/FileSecurityUtil.java
Normal file
126
src/main/java/com/zhgd/xmgl/util/FileSecurityUtil.java
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package com.zhgd.xmgl.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class FileSecurityUtil {
|
||||||
|
// 最大文件大小 10000MB
|
||||||
|
public static final long MAX_FILE_SIZE = 10 * 1024 * 1024 * 1024L;
|
||||||
|
// 允许的文件类型白名单
|
||||||
|
public static Set<String> ALLOWED_EXTENSIONS = new HashSet<String>();
|
||||||
|
// 允许的MIME类型
|
||||||
|
public static Set<String> ALLOWED_MIME_TYPES = new HashSet<>();
|
||||||
|
|
||||||
|
@Value("${file.upload.allowed-extensions}")
|
||||||
|
public void setAllowedExtensions(String allowedExtensions) {
|
||||||
|
ALLOWED_EXTENSIONS.addAll(StrUtil.split(allowedExtensions, ","));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${file.upload.allowed-mime-types}")
|
||||||
|
public void setAllowedMimeTypes(String allowedMimeTypes) {
|
||||||
|
ALLOWED_MIME_TYPES.addAll(StrUtil.split(allowedMimeTypes, ","));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件扩展名
|
||||||
|
*/
|
||||||
|
public boolean isValidExtension(String filename) {
|
||||||
|
if (filename == null) return false;
|
||||||
|
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
|
||||||
|
return ALLOWED_EXTENSIONS.contains(ext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查MIME类型
|
||||||
|
*/
|
||||||
|
public boolean isValidMimeType(MultipartFile file) {
|
||||||
|
try {
|
||||||
|
String mimeType = file.getContentType();
|
||||||
|
return ALLOWED_MIME_TYPES.contains(mimeType);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件大小
|
||||||
|
*/
|
||||||
|
public boolean isValidSize(MultipartFile file) {
|
||||||
|
return file.getSize() <= MAX_FILE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全重命名文件
|
||||||
|
*/
|
||||||
|
public String generateSafeFilename(String originalFilename) {
|
||||||
|
String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
|
||||||
|
String baseName = UUID.randomUUID().toString().replace("-", "");
|
||||||
|
return baseName + ext.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件内容安全检查
|
||||||
|
*/
|
||||||
|
public boolean isFileContentSafe(MultipartFile file) {
|
||||||
|
try {
|
||||||
|
// 1. 检查文件头是否符合声明类型
|
||||||
|
if (!validateFileHeader(file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 2. 病毒扫描(如果有的话)
|
||||||
|
// if (antivirusService != null) {
|
||||||
|
// return antivirusService.scanFile(file.getBytes());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 3. 检查是否包含可疑内容(简单示例)
|
||||||
|
return !containsSuspiciousContent(file);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateFileHeader(MultipartFile file) throws IOException {
|
||||||
|
byte[] header = new byte[20];
|
||||||
|
try (InputStream is = file.getInputStream()) {
|
||||||
|
is.read(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的文件头验证逻辑
|
||||||
|
String mimeType = file.getContentType();
|
||||||
|
if (mimeType.startsWith("image/")) {
|
||||||
|
return isImageFile(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // 对于非图片文件,可以放宽检查
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isImageFile(byte[] header) {
|
||||||
|
// 简单的图片文件头检查
|
||||||
|
return (header[0] == (byte) 0xFF && header[1] == (byte) 0xD8) || // JPEG
|
||||||
|
(header[0] == (byte) 0x89 && header[1] == (byte) 0x50 && // PNG
|
||||||
|
header[2] == (byte) 0x4E && header[3] == (byte) 0x47);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean containsSuspiciousContent(MultipartFile file) throws IOException {
|
||||||
|
// 检查是否包含可执行文件特征(简单示例)
|
||||||
|
byte[] content = file.getBytes();
|
||||||
|
String contentStr = new String(content).toLowerCase();
|
||||||
|
|
||||||
|
// return contentStr.contains("<?php") ||
|
||||||
|
// contentStr.contains("<%") ||
|
||||||
|
// contentStr.contains("eval(") ||
|
||||||
|
// contentStr.contains("exec(");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -219,3 +219,6 @@ sada.host=http://api.e.v1.i-sada.net
|
|||||||
# 仁智测控的扬尘上传端口
|
# 仁智测控的扬尘上传端口
|
||||||
rengzhicekong.hj212.port=24041
|
rengzhicekong.hj212.port=24041
|
||||||
spring.main.allow-circular-references=true
|
spring.main.allow-circular-references=true
|
||||||
|
#上传文件限制
|
||||||
|
file.upload.allowed-extensions=jpg,jpeg,png,gif,bmp,webp,svg,tiff,ico,pdf,doc,docx,xls,xlsx,ppt,pptx,txt,rtf,csv,mp4,avi,mov,wmv,flv,mkv,webm,m4v,3gp,mp3,wav,ogg,flac,aac,wma,m4a,zip,rar,7z,tar,gz
|
||||||
|
file.upload.allowed-mime-types=image/jpeg,image/png,image/gif,image/bmp,image/webp,image/svg+xml,image/tiff,image/x-icon,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,text/plain,application/rtf,text/csv,video/mp4,video/x-msvideo,video/quicktime,video/x-ms-wmv,video/x-flv,video/x-matroska,video/webm,video/3gpp,audio/mpeg,audio/wav,audio/ogg,audio/flac,audio/aac,audio/x-ms-wma,audio/mp4,application/zip,application/x-rar-compressed,application/x-7z-compressed,application/x-tar,application/gzip
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user