first commit

This commit is contained in:
陈现府
2025-11-20 10:45:16 +08:00
commit 8355431361
2643 changed files with 291314 additions and 0 deletions
@@ -0,0 +1,32 @@
package com.mhd.common.security.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.mhd.common.security.config.ApplicationConfig;
import com.mhd.system.api.feign.FeignAutoConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.mhd.**.mapper")
// 开启线程异步执行
@EnableAsync
// 自动加载类
@Import({ ApplicationConfig.class, FeignAutoConfiguration.class })
public @interface EnableCustomConfig
{
}
@@ -0,0 +1,28 @@
package com.mhd.common.security.annotation;
import org.springframework.cloud.openfeign.EnableFeignClients;
import java.lang.annotation.*;
/**
* 自定义feign注解
* 添加basePackages路径
*
* @author mhd
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableMhdAuthFeignClients
{
String[] value() default {};
String[] basePackages() default { "com.mhd" };
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}
@@ -0,0 +1,27 @@
package com.mhd.common.security.annotation;
import org.springframework.cloud.openfeign.EnableFeignClients;
import java.lang.annotation.*;
/**
* 自定义feign注解
* 添加basePackages路径
*
* @author mhd
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@EnableFeignClients
public @interface EnableRyFeignClients
{
String[] value() default {};
String[] basePackages() default { "com.mhd" };
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}
@@ -0,0 +1,19 @@
package com.mhd.common.security.annotation;
import java.lang.annotation.*;
/**
* 内部认证注解
*
* @author mhd
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InnerAuth
{
/**
* 是否校验用户信息
*/
boolean isUser() default false;
}
@@ -0,0 +1,20 @@
package com.mhd.common.security.annotation;
/**
* 权限注解的验证模式
*
* @author mhd
*
*/
public enum Logical
{
/**
* 必须具有所有的元素
*/
AND,
/**
* 只需具有其中一个元素
*/
OR
}
@@ -0,0 +1,27 @@
package com.mhd.common.security.annotation;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 自定义注解防止表单重复提交
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RepeatSubmit {
/**
* 间隔时间(ms),小于此时间视为重复提交
*/
long interval() default 1000;
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
/**
* 提示消息
*/
String message() default "不允许重复提交!";
}
@@ -0,0 +1,18 @@
package com.mhd.common.security.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 登录认证:只有登录之后才能进入该方法
*
* @author mhd
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresLogin
{
}
@@ -0,0 +1,27 @@
package com.mhd.common.security.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 权限认证:必须具有指定权限才能进入该方法
*
* @author mhd
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresPermissions
{
/**
* 需要校验的权限码
*/
String[] value() default {};
/**
* 验证模式:AND | OR,默认AND
*/
Logical logical() default Logical.AND;
}
@@ -0,0 +1,26 @@
package com.mhd.common.security.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 角色认证:必须具有指定角色标识才能进入该方法
*
* @author mhd
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresRoles
{
/**
* 需要校验的角色标识
*/
String[] value() default {};
/**
* 验证逻辑:AND | OR,默认AND
*/
Logical logical() default Logical.AND;
}
@@ -0,0 +1,51 @@
package com.mhd.common.security.aspect;
import com.mhd.common.security.annotation.InnerAuth;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import com.mhd.common.core.constant.SecurityConstants;
import com.mhd.common.core.exception.InnerAuthException;
import com.mhd.common.core.utils.ServletUtils;
import com.mhd.common.core.utils.StringUtils;
/**
* 内部服务调用验证处理
*
* @author mhd
*/
@Aspect
@Component
public class InnerAuthAspect implements Ordered
{
@Around("@annotation(innerAuth)")
public Object innerAround(ProceedingJoinPoint point, InnerAuth innerAuth) throws Throwable
{
String source = ServletUtils.getRequest().getHeader(SecurityConstants.FROM_SOURCE);
// 内部请求验证
if (!StringUtils.equals(SecurityConstants.INNER, source))
{
throw new InnerAuthException("没有内部访问权限,不允许访问");
}
String userid = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USER_ID);
String username = ServletUtils.getRequest().getHeader(SecurityConstants.DETAILS_USERNAME);
// 用户信息验证
if (innerAuth.isUser() && (StringUtils.isEmpty(userid) || StringUtils.isEmpty(username)))
{
throw new InnerAuthException("没有设置用户信息,不允许访问 ");
}
return point.proceed();
}
/**
* 确保在权限认证aop执行前执行
*/
@Override
public int getOrder()
{
return Ordered.HIGHEST_PRECEDENCE + 1;
}
}
@@ -0,0 +1,98 @@
package com.mhd.common.security.aspect;
import java.lang.reflect.Method;
import com.mhd.common.security.annotation.RequiresLogin;
import com.mhd.common.security.annotation.RequiresPermissions;
import com.mhd.common.security.annotation.RequiresRoles;
import com.mhd.common.security.auth.AuthUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
/**
* 基于 Spring Aop 的注解鉴权
*
* @author kong
*/
@Aspect
@Component
public class PreAuthorizeAspect
{
/**
* 构建
*/
public PreAuthorizeAspect()
{
}
/**
* 定义AOP签名 (切入所有使用鉴权注解的方法)
*/
public static final String POINTCUT_SIGN = " @annotation(com.mhd.common.security.annotation.RequiresLogin) || "
+ "@annotation(com.mhd.common.security.annotation.RequiresPermissions) || "
+ "@annotation(com.mhd.common.security.annotation.RequiresRoles)";
/**
* 声明AOP签名
*/
@Pointcut(POINTCUT_SIGN)
public void pointcut()
{
}
/**
* 环绕切入
*
* @param joinPoint 切面对象
* @return 底层方法执行后的返回值
* @throws Throwable 底层方法抛出的异常
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable
{
// 注解鉴权
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
checkMethodAnnotation(signature.getMethod());
try
{
// 执行原有逻辑
Object obj = joinPoint.proceed();
return obj;
}
catch (Throwable e)
{
throw e;
}
}
/**
* 对一个Method对象进行注解检查
*/
public void checkMethodAnnotation(Method method)
{
// 校验 @RequiresLogin 注解
RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class);
if (requiresLogin != null)
{
AuthUtil.checkLogin();
}
// 校验 @RequiresRoles 注解
RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
if (requiresRoles != null)
{
AuthUtil.checkRole(requiresRoles);
}
// 校验 @RequiresPermissions 注解
RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
if (requiresPermissions != null)
{
AuthUtil.checkPermi(requiresPermissions);
}
}
}
@@ -0,0 +1,130 @@
package com.mhd.common.security.aspect;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson2.JSON;
import com.mhd.common.security.annotation.RepeatSubmit;
import com.mhd.common.core.constant.Constants;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.ServletUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.redis.service.RedisService;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 基于 Spring Aop 的注解鉴权
*
*/
@Aspect
@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class RepeatSubmitAspect implements Ordered
{
private final RedisService redisService;
/**
* 环绕切入
*
* @param joinPoint 切面对象
* @return 底层方法执行后的返回值
* @throws Throwable 底层方法抛出的异常
*/
@Around("@annotation(repeatSubmit)")
public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) throws Throwable
{
// 如果注解不为0 则使用注解数值
long interval = 1000L;
if (repeatSubmit.interval() > 0) {
interval = repeatSubmit.timeUnit().toMillis(repeatSubmit.interval());
}
if (interval < 1000) {
throw new ServiceException("重复提交间隔时间不能小于'1'秒");
}
HttpServletRequest request = ServletUtils.getRequest();
String nowParams = argsArrayToString(joinPoint.getArgs());
// 请求地址+请求ip(作为存放cache的key值)
String submitKey = request.getRemoteAddr() + request.getRequestURI();
submitKey = SecureUtil.md5(submitKey + ":" + nowParams);
// 唯一标识(指定key + 消息头)
String cacheRepeatKey = Constants.REPEAT_SUBMIT_KEY + submitKey;
String key = redisService.getCacheObject(cacheRepeatKey);
if (key == null) {
redisService.setCacheObject(cacheRepeatKey, "", interval, TimeUnit.MILLISECONDS);
} else {
throw new ServiceException(repeatSubmit.message());
}
return joinPoint.proceed();
}
/**
* 设置优先级
* @return
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray) {
StringBuilder params = new StringBuilder();
if (paramsArray != null && paramsArray.length > 0) {
for (Object o : paramsArray) {
if (StringUtils.isNotNull(o) && !isFilterObject(o)) {
try {
params.append(JSON.toJSONString(o)).append(" ");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return params.toString().trim();
}
/**
* 判断是否需要过滤的对象。
*
* @param obj 对象信息。
* @return 如果是需要过滤的对象,则返回true;否则返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object obj) {
Class<?> clazz = obj.getClass();
if (clazz.isArray()) {
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) obj;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) obj;
for (Object value : map.entrySet()) {
Map.Entry entry = (Map.Entry) value;
return entry.getValue() instanceof MultipartFile;
}
}
return obj instanceof MultipartFile || obj instanceof HttpServletRequest || obj instanceof HttpServletResponse
|| obj instanceof BindingResult;
}
}
@@ -0,0 +1,372 @@
package com.mhd.common.security.auth;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import com.mhd.common.security.service.TokenService;
import org.springframework.util.PatternMatchUtils;
import com.mhd.common.core.exception.auth.NotLoginException;
import com.mhd.common.core.exception.auth.NotPermissionException;
import com.mhd.common.core.exception.auth.NotRoleException;
import com.mhd.common.core.utils.SpringUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.annotation.Logical;
import com.mhd.common.security.annotation.RequiresLogin;
import com.mhd.common.security.annotation.RequiresPermissions;
import com.mhd.common.security.annotation.RequiresRoles;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
/**
* Token 权限验证,逻辑实现类
*
* @author mhd
*/
public class AuthLogic
{
/** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin";
public TokenService tokenService = SpringUtils.getBean(TokenService.class);
/**
* 会话注销
*/
public void logout()
{
String token = SecurityUtils.getToken();
if (token == null)
{
return;
}
logoutByToken(token);
}
/**
* 会话注销,根据指定Token
*/
public void logoutByToken(String token)
{
tokenService.delLoginUser(token);
}
/**
* 检验用户是否已经登录,如未登录,则抛出异常
*/
public void checkLogin()
{
getLoginUser();
}
/**
* 获取当前用户缓存信息, 如果未登录,则抛出异常
*
* @return 用户缓存信息
*/
public LoginUser getLoginUser()
{
String token = SecurityUtils.getToken();
if (token == null)
{
throw new NotLoginException("未提供token");
}
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser == null)
{
throw new NotLoginException("无效的token");
}
return loginUser;
}
/**
* 获取当前用户缓存信息, 如果未登录,则抛出异常
*
* @param token 前端传递的认证信息
* @return 用户缓存信息
*/
public LoginUser getLoginUser(String token)
{
return tokenService.getLoginUser(token);
}
/**
* 验证当前用户有效期, 如果相差不足120分钟,自动刷新缓存
*
* @param loginUser 当前用户信息
*/
public void verifyLoginUserExpire(LoginUser loginUser)
{
tokenService.verifyToken(loginUser);
}
/**
* 验证用户是否具备某权限
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(String permission)
{
return hasPermi(getPermiList(), permission);
}
/**
* 验证用户是否具备某权限, 如果验证未通过,则抛出异常: NotPermissionException
*
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public void checkPermi(String permission)
{
if (!hasPermi(getPermiList(), permission))
{
throw new NotPermissionException(permission);
}
}
/**
* 根据注解(@RequiresPermissions)鉴权, 如果验证未通过,则抛出异常: NotPermissionException
*
* @param requiresPermissions 注解对象
*/
public void checkPermi(RequiresPermissions requiresPermissions)
{
if (requiresPermissions.logical() == Logical.AND)
{
checkPermiAnd(requiresPermissions.value());
}
else
{
checkPermiOr(requiresPermissions.value());
}
}
/**
* 验证用户是否含有指定权限,必须全部拥有
*
* @param permissions 权限列表
*/
public void checkPermiAnd(String... permissions)
{
Set<String> permissionList = getPermiList();
for (String permission : permissions)
{
if (!hasPermi(permissionList, permission))
{
throw new NotPermissionException(permission);
}
}
}
/**
* 验证用户是否含有指定权限,只需包含其中一个
*
* @param permissions 权限码数组
*/
public void checkPermiOr(String... permissions)
{
Set<String> permissionList = getPermiList();
for (String permission : permissions)
{
if (hasPermi(permissionList, permission))
{
return;
}
}
if (permissions.length > 0)
{
throw new NotPermissionException(permissions);
}
}
/**
* 判断用户是否拥有某个角色
*
* @param role 角色标识
* @return 用户是否具备某角色
*/
public boolean hasRole(String role)
{
return hasRole(getRoleList(), role);
}
/**
* 判断用户是否拥有某个角色, 如果验证未通过,则抛出异常: NotRoleException
*
* @param role 角色标识
*/
public void checkRole(String role)
{
if (!hasRole(role))
{
throw new NotRoleException(role);
}
}
/**
* 根据注解(@RequiresRoles)鉴权
*
* @param requiresRoles 注解对象
*/
public void checkRole(RequiresRoles requiresRoles)
{
if (requiresRoles.logical() == Logical.AND)
{
checkRoleAnd(requiresRoles.value());
}
else
{
checkRoleOr(requiresRoles.value());
}
}
/**
* 验证用户是否含有指定角色,必须全部拥有
*
* @param roles 角色标识数组
*/
public void checkRoleAnd(String... roles)
{
Set<String> roleList = getRoleList();
for (String role : roles)
{
if (!hasRole(roleList, role))
{
throw new NotRoleException(role);
}
}
}
/**
* 验证用户是否含有指定角色,只需包含其中一个
*
* @param roles 角色标识数组
*/
public void checkRoleOr(String... roles)
{
Set<String> roleList = getRoleList();
for (String role : roles)
{
if (hasRole(roleList, role))
{
return;
}
}
if (roles.length > 0)
{
throw new NotRoleException(roles);
}
}
/**
* 根据注解(@RequiresLogin)鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(RequiresLogin at)
{
this.checkLogin();
}
/**
* 根据注解(@RequiresRoles)鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(RequiresRoles at)
{
String[] roleArray = at.value();
if (at.logical() == Logical.AND)
{
this.checkRoleAnd(roleArray);
}
else
{
this.checkRoleOr(roleArray);
}
}
/**
* 根据注解(@RequiresPermissions)鉴权
*
* @param at 注解对象
*/
public void checkByAnnotation(RequiresPermissions at)
{
String[] permissionArray = at.value();
if (at.logical() == Logical.AND)
{
this.checkPermiAnd(permissionArray);
}
else
{
this.checkPermiOr(permissionArray);
}
}
/**
* 获取当前账号的角色列表
*
* @return 角色列表
*/
public Set<String> getRoleList()
{
try
{
LoginUser loginUser = getLoginUser();
return loginUser.getRoles();
}
catch (Exception e)
{
return new HashSet<>();
}
}
/**
* 获取当前账号的权限列表
*
* @return 权限列表
*/
public Set<String> getPermiList()
{
try
{
LoginUser loginUser = getLoginUser();
return loginUser.getPermissions();
}
catch (Exception e)
{
return new HashSet<>();
}
}
/**
* 判断是否包含权限
*
* @param authorities 权限列表
* @param permission 权限字符串
* @return 用户是否具备某权限
*/
public boolean hasPermi(Collection<String> authorities, String permission)
{
return authorities.stream().filter(StringUtils::hasText)
.anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
}
/**
* 判断是否包含角色
*
* @param roles 角色列表
* @param role 角色
* @return 用户是否具备某角色权限
*/
public boolean hasRole(Collection<String> roles, String role)
{
return roles.stream().filter(StringUtils::hasText)
.anyMatch(x -> SUPER_ADMIN.contains(x) || PatternMatchUtils.simpleMatch(x, role));
}
}
@@ -0,0 +1,162 @@
package com.mhd.common.security.auth;
import com.mhd.common.security.annotation.RequiresPermissions;
import com.mhd.common.security.annotation.RequiresRoles;
import com.mhd.system.api.model.LoginUser;
/**
* Token 权限验证工具类
*
* @author mhd
*/
public class AuthUtil
{
/**
* 底层的 AuthLogic 对象
*/
public static AuthLogic authLogic = new AuthLogic();
/**
* 会话注销
*/
public static void logout()
{
authLogic.logout();
}
/**
* 会话注销,根据指定Token
*
* @param tokenValue 指定token
*/
public static void logoutByToken(String token)
{
authLogic.logoutByToken(token);
}
/**
* 检验当前会话是否已经登录,如未登录,则抛出异常
*/
public static void checkLogin()
{
authLogic.checkLogin();
}
/**
* 获取当前登录用户信息
*/
public static LoginUser getLoginUser(String token)
{
return authLogic.getLoginUser(token);
}
/**
* 验证当前用户有效期
*/
public static void verifyLoginUserExpire(LoginUser loginUser)
{
authLogic.verifyLoginUserExpire(loginUser);
}
/**
* 当前账号是否含有指定角色标识, 返回true或false
*
* @param role 角色标识
* @return 是否含有指定角色标识
*/
public static boolean hasRole(String role)
{
return authLogic.hasRole(role);
}
/**
* 当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
*
* @param role 角色标识
*/
public static void checkRole(String role)
{
authLogic.checkRole(role);
}
/**
* 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotRoleException
*
* @param requiresRoles 角色权限注解
*/
public static void checkRole(RequiresRoles requiresRoles)
{
authLogic.checkRole(requiresRoles);
}
/**
* 当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
*
* @param roles 角色标识数组
*/
public static void checkRoleAnd(String... roles)
{
authLogic.checkRoleAnd(roles);
}
/**
* 当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
*
* @param roles 角色标识数组
*/
public static void checkRoleOr(String... roles)
{
authLogic.checkRoleOr(roles);
}
/**
* 当前账号是否含有指定权限, 返回true或false
*
* @param permission 权限码
* @return 是否含有指定权限
*/
public static boolean hasPermi(String permission)
{
return authLogic.hasPermi(permission);
}
/**
* 当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
*
* @param permission 权限码
*/
public static void checkPermi(String permission)
{
authLogic.checkPermi(permission);
}
/**
* 根据注解传入参数鉴权, 如果验证未通过,则抛出异常: NotPermissionException
*
* @param requiresPermissions 权限注解
*/
public static void checkPermi(RequiresPermissions requiresPermissions)
{
authLogic.checkPermi(requiresPermissions);
}
/**
* 当前账号是否含有指定权限 [指定多个,必须全部验证通过]
*
* @param permissions 权限码数组
*/
public static void checkPermiAnd(String... permissions)
{
authLogic.checkPermiAnd(permissions);
}
/**
* 当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
*
* @param permissions 权限码数组
*/
public static void checkPermiOr(String... permissions)
{
authLogic.checkPermiOr(permissions);
}
}
@@ -0,0 +1,22 @@
package com.mhd.common.security.config;
import java.util.TimeZone;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
/**
* 系统配置
*
* @author mhd
*/
public class ApplicationConfig
{
/**
* 时区配置
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
{
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
}
@@ -0,0 +1,32 @@
package com.mhd.common.security.config;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.mhd.common.security.interceptor.HeaderInterceptor;
/**
* 拦截器配置
*
* @author mhd
*/
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 不需要拦截地址
*/
public static final String[] excludeUrls = {"/login", "/loginApi","/dingloginApi", "/logout", "/refresh", "/basicApi/findAgreement","/approvalDocumentApi/dingdingHuiDiao"};
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getHeaderInterceptor())
.addPathPatterns("/**")
.excludePathPatterns(excludeUrls)
.order(-10);
}
/**
* 自定义请求头拦截器
*/
public HeaderInterceptor getHeaderInterceptor() {
return new HeaderInterceptor();
}
}
@@ -0,0 +1,167 @@
package com.mhd.common.security.handler;
import javax.servlet.http.HttpServletRequest;
import cn.hutool.json.JSONUtil;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import com.mhd.common.core.constant.HttpStatus;
import com.mhd.common.core.exception.DemoModeException;
import com.mhd.common.core.exception.InnerAuthException;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.exception.auth.NotPermissionException;
import com.mhd.common.core.exception.auth.NotRoleException;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.core.web.domain.AjaxResult;
import java.util.List;
/**
* 全局异常处理器
*
* @author mhd
*/
@RestControllerAdvice
public class GlobalExceptionHandler
{
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 权限码异常
*/
@ExceptionHandler(NotPermissionException.class)
public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权");
}
/**
* 角色权限异常
*/
@ExceptionHandler(NotRoleException.class)
public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有访问权限,请联系管理员授权");
}
/**
* 请求方式不支持
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
return AjaxResult.error(e.getMessage());
}
/**
* 业务异常
*/
@ExceptionHandler(ServiceException.class)
public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request)
{
log.error(e.getMessage(), e);
Integer code = e.getCode();
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
}
/**
* 拦截未知的运行时异常
*/
@ExceptionHandler(RuntimeException.class)
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 自定义验证异常
*/
@ExceptionHandler(BindException.class)
public AjaxResult handleBindException(BindException e)
{
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return AjaxResult.error(message);
}
/**
* 自定义验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public String handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生参数校验异常.", requestURI);
BindingResult bindingResult = e.getBindingResult();
StringBuilder errorMsg = new StringBuilder();
if (bindingResult.hasErrors()) {
List<ObjectError> errors = bindingResult.getAllErrors();
for (ObjectError objectError : errors) {
FieldError fieldError = (FieldError) objectError;
log.error("Data check failure : object: {},field: {},errorMessage: {}",
fieldError.getObjectName(), fieldError.getField(), fieldError.getDefaultMessage());
errorMsg.append(objectError.getDefaultMessage());
errorMsg.append(",");
}
errorMsg = new StringBuilder(errorMsg.substring(0, errorMsg.length() - 1));
}
return JSONUtil.toJsonStr(AjaxResult.error(HttpStatus.BAD_REQUEST,errorMsg.toString()));
}
/**
* 内部认证异常
*/
@ExceptionHandler(InnerAuthException.class)
public AjaxResult handleInnerAuthException(InnerAuthException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生内部认证异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
/**
* 演示模式异常
*/
@ExceptionHandler(DemoModeException.class)
public AjaxResult handleDemoModeException(DemoModeException e)
{
return AjaxResult.error("演示模式,不允许操作");
}
@ExceptionHandler(DigitalLogisticsException.class)
public AjaxResult<?> BusinessException(DigitalLogisticsException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生 DigitalLogisticsException 异常.", requestURI, e);
return AjaxResult.error(e.getErrno(),e.getDetailMessage());
}
}
@@ -0,0 +1,55 @@
package com.mhd.common.security.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mhd.common.security.auth.AuthUtil;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import com.mhd.common.core.constant.SecurityConstants;
import com.mhd.common.core.context.SecurityContextHolder;
import com.mhd.common.core.utils.ServletUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
/**
* 自定义请求头拦截器,将Header数据封装到线程变量中方便获取
* 注意:此拦截器会同时验证当前用户有效期自动刷新有效期
*
* @author mhd
*/
public class HeaderInterceptor implements AsyncHandlerInterceptor
{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
{
if (!(handler instanceof HandlerMethod))
{
return true;
}
SecurityContextHolder.setUserId(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USER_ID));
SecurityContextHolder.setUserName(ServletUtils.getHeader(request, SecurityConstants.DETAILS_USERNAME));
SecurityContextHolder.setUserKey(ServletUtils.getHeader(request, SecurityConstants.USER_KEY));
String token = SecurityUtils.getToken();
if (StringUtils.isNotEmpty(token))
{
LoginUser loginUser = AuthUtil.getLoginUser(token);
if (StringUtils.isNotNull(loginUser))
{
AuthUtil.verifyLoginUserExpire(loginUser);
SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception
{
SecurityContextHolder.remove();
}
}
@@ -0,0 +1,306 @@
package com.mhd.common.security.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mhd.common.core.domain.po.UserConfigSwitchPo;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.JwtUtils;
import com.mhd.common.core.utils.RequestHeaderUtil;
import com.mhd.common.core.utils.ServletUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.core.utils.ip.IpUtil;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.system.api.SystemServiceFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.mhd.common.core.constant.CacheConstants;
import com.mhd.common.core.constant.SecurityConstants;
import com.mhd.common.core.utils.ip.IpUtils;
import com.mhd.common.core.utils.uuid.IdUtils;
import com.mhd.common.redis.service.RedisService;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
/**
* token验证处理
*
* @author mhd
*/
@Component
public class TokenService
{
@Autowired
private RedisService redisService;
@Resource
private SystemServiceFeign systemServiceFeign;
protected static final long MILLIS_SECOND = 1000;
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
private static ThreadLocal<Long> expireTime = new ThreadLocal<Long>(){
@Override
protected Long initialValue() {
return CacheConstants.EXPIRATION;
}
};
// private final static long expireTime = CacheConstants.EXPIRATION;
private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
private final static String USER_LOGIN_TOKENS = CacheConstants.USER_LOGIN_TOKENS;
private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser()
{
return getLoginUser(ServletUtils.getRequest());
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(HttpServletRequest request)
{
// 获取请求携带的令牌
String token = SecurityUtils.getToken(request);
return getLoginUser(token);
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser getLoginUser(String token)
{
LoginUser user = null;
try
{
if (StringUtils.isNotEmpty(token))
{
String userkey = JwtUtils.getUserKey(token);
user = redisService.getCacheObject(getTokenKey(userkey));
return user;
}
}
catch (Exception e)
{
}
return user;
}
/**
* 设置用户身份信息
*/
public void setLoginUser(LoginUser loginUser)
{
if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken()))
{
refreshToken(loginUser);
}
}
/**
* 删除用户缓存信息
*/
public void delLoginUser(String token)
{
if (StringUtils.isNotEmpty(token))
{
String userkey = JwtUtils.getUserKey(token);
redisService.deleteObject(getTokenKey(userkey));
}
}
/**
* 验证令牌有效期,相差不足120分钟,自动刷新缓存
*
* @param loginUser
*/
public void verifyToken(LoginUser loginUser)
{
long expireTime = loginUser.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
{
refreshToken(loginUser);
}
}
/**
* 刷新令牌有效期
* 过期时间720分钟
* @param loginUser 登录信息
*/
public void refreshToken(LoginUser loginUser)
{
String expireTimeKey = CacheConstants.TOKEN_EXPIRE_TIME+loginUser.getUserPo().getTopOrganizationId();
Object cacheObject = redisService.getCacheObject(expireTimeKey);
if (cacheObject != null){
long l = Long.parseLong(cacheObject.toString());
expireTime.set(l);
}
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime.get() * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisService.setCacheObject(userKey, loginUser, expireTime.get(), TimeUnit.MINUTES);
}
private String getTokenKey(String token)
{
return ACCESS_TOKEN + token;
}
/**
* @Description 创建令牌方法新
* @Author Alex
* @Date 2022/12/6 23:09
*/
public Map<String, Object> createTokenApi(LoginUser loginUser)
{
String token = IdUtils.fastUUID();
Long userId = loginUser.getUserPo().getUserId();
String userAccount = loginUser.getUserPo().getUserAccount();
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
loginUser.setToken(token);
loginUser.setUserid(userId);
loginUser.setUsername(userAccount);
loginUser.setOs("1");
loginUser.setTerminal(RequestHeaderUtil.getTerminal());
loginUser.setTerminalLogo(RequestHeaderUtil.getTerminalOS());
loginUser.setInitialLoginTime(System.currentTimeMillis());
loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
loginUser.setIpRegion(StrUtil.isNotEmpty(loginUser.getIpaddr()) ? IpUtil.getLocationSplicing(loginUser.getIpaddr()) : "");
refreshToken(loginUser);
//是否单点登录
AjaxResult<?> ajaxResult = systemServiceFeign.getUserConfigSwitchInfoByOrgId(topOrganizationId);
if (!"200".equals(String.valueOf(ajaxResult.get("code"))) || ObjUtil.isNull(JSONObject.toJSONString(ajaxResult.get("data")))) {
throw new ServiceException("获取开关配置信息失败");
}
UserConfigSwitchPo userConfigSwitchPo = JSONUtil.toBean(JSONUtil.toJsonStr(ajaxResult.get("data")), UserConfigSwitchPo.class);
Integer isSingleSign = userConfigSwitchPo.getIsSingleSign();//单点登录:0-否,1-是
if (ObjUtil.isNotNull(isSingleSign) && isSingleSign == 1) {
String key = CacheConstants.USER_LOGIN_TOKENS + userId;
String tokenStr = redisService.getCacheObject(key);
if (StrUtil.isNotEmpty(tokenStr)) {
redisService.deleteObject(tokenStr);
}
redisService.setCacheObject(key, CacheConstants.LOGIN_TOKEN_KEY + token, expireTime.get(), TimeUnit.MINUTES);
}
// Jwt存储信息
Map<String, Object> claimsMap = new HashMap<String, Object>();
claimsMap.put(SecurityConstants.USER_KEY, token);
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userAccount);
claimsMap.put(SecurityConstants.TOP_ORGANIZATION_ID, topOrganizationId);
// 接口返回信息
Map<String, Object> rspMap = new HashMap<String, Object>();
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
rspMap.put("expires_in", expireTime.get());
return rspMap;
}
public Map<String, Object> createTokenApiDingDing(LoginUser loginUser)
{
String token = IdUtils.fastUUID();
Long userId = loginUser.getUserPo().getUserId();
String userAccount = loginUser.getUserPo().getUserAccount();
Long topOrganizationId = loginUser.getUserPo().getTopOrganizationId();
loginUser.setToken(token);
loginUser.setUserid(userId);
loginUser.setUsername(userAccount);
loginUser.setOs("1");
loginUser.setTerminal(RequestHeaderUtil.getTerminal());
loginUser.setTerminalLogo(RequestHeaderUtil.getTerminalOS());
loginUser.setInitialLoginTime(System.currentTimeMillis());
loginUser.setIpaddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
loginUser.setIpRegion(StrUtil.isNotEmpty(loginUser.getIpaddr()) ? IpUtil.getLocationSplicing(loginUser.getIpaddr()) : "");
refreshToken(loginUser);
/* //是否单点登录
AjaxResult<?> ajaxResult = systemServiceFeign.getUserConfigSwitchInfoByOrgId(topOrganizationId);
if (!"200".equals(String.valueOf(ajaxResult.get("code"))) || ObjUtil.isNull(JSONObject.toJSONString(ajaxResult.get("data")))) {
throw new ServiceException("获取开关配置信息失败");
}*/
/* UserConfigSwitchPo userConfigSwitchPo = JSONUtil.toBean(JSONUtil.toJsonStr(ajaxResult.get("data")), UserConfigSwitchPo.class);
Integer isSingleSign = userConfigSwitchPo.getIsSingleSign();//单点登录:0-否,1-是
if (ObjUtil.isNotNull(isSingleSign) && isSingleSign == 1) {
String key = CacheConstants.USER_LOGIN_TOKENS + userId;
String tokenStr = redisService.getCacheObject(key);
if (StrUtil.isNotEmpty(tokenStr)) {
redisService.deleteObject(tokenStr);
}
redisService.setCacheObject(key, CacheConstants.LOGIN_TOKEN_KEY + token, expireTime.get(), TimeUnit.MINUTES);
}*/
// Jwt存储信息
Map<String, Object> claimsMap = new HashMap<String, Object>();
claimsMap.put(SecurityConstants.USER_KEY, token);
claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
claimsMap.put(SecurityConstants.DETAILS_USERNAME, userAccount);
claimsMap.put(SecurityConstants.TOP_ORGANIZATION_ID, topOrganizationId);
// 接口返回信息
Map<String, Object> rspMap = new HashMap<String, Object>();
rspMap.put("access_token", JwtUtils.createToken(claimsMap));
rspMap.put("expires_in", expireTime.get());
rspMap.put("userId",userId);
return rspMap;
}
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public LoginUser refreshData(HttpServletRequest request)
{
// 获取请求携带的令牌
String token = SecurityUtils.getToken(request);
LoginUser loginUser = getLoginUser(token);
return loginUser;
}
/**
* 刷新令牌有效期
* 过期时间720分钟
* @param loginUser 登录信息
*/
public void refreshTokenData(LoginUser loginUser) {
loginUser.setLoginTime(System.currentTimeMillis());
loginUser.setExpireTime(loginUser.getLoginTime() + expireTime.get() * MILLIS_MINUTE);
// 根据uuid将loginUser缓存
String userKey = getTokenKey(loginUser.getToken());
redisService.setCacheObject(userKey, loginUser, expireTime.get(), TimeUnit.MINUTES);
}
}
@@ -0,0 +1,329 @@
package com.mhd.common.security.service.report;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mhd.common.core.constant.CacheConstants;
import com.mhd.common.core.domain.dto.wlhy.report.CapitalFlowReport;
import com.mhd.common.core.domain.dto.wlhy.report.DriverReport;
import com.mhd.common.core.domain.dto.wlhy.report.TransportNoteReport;
import com.mhd.common.core.domain.dto.wlhy.report.VehicleReport;
import com.mhd.common.core.domain.entity.OrgProductConfig;
import com.mhd.common.core.domain.po.UserConfigSwitchPo;
import com.mhd.common.core.domain.po.UserDriverPo;
import com.mhd.common.core.domain.po.UserPo;
import com.mhd.common.core.domain.po.VehiclePo;
import com.mhd.common.core.domain.po.report.UserDriverReportPo;
import com.mhd.common.core.domain.po.report.VehicleReportPo;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.exception.digitalLogisticsException.DigitalLogisticsException;
import com.mhd.common.core.exception.digitalLogisticsException.UserError;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.redis.service.RedisService;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.ReportServiceFeign;
import com.mhd.system.api.SystemServiceFeign;
import com.mhd.system.api.domain.WlhyLoginUser;
import com.mhd.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* 上报通用服务类
*/
@Service
public class ReportDomainService {
@Resource
private RedisService redisService;
@Resource
private ReportServiceFeign reportServiceFeign;
@Resource
private SystemServiceFeign systemServiceFeign;
/**
* 数字物流网货司机 - 数据上送
*/
public boolean driverReport(UserDriverReportPo driverReportPo) {
if (ObjectUtil.isNull(driverReportPo)) {
throw new ServiceException("上送数据不能为空");
}
//检查是否开启网货
boolean flag = checkSwitch();
if (!flag) {
//如果未配置网货产品,返回跳过
return true;
}
//检查是否开始数据保存
Integer isPushReportData = getUserConfigSwitch().getIsPushReportData();
if (isPushReportData != 1){
return true;
}
DriverReport driverReport = driverEntityConvertReport(driverReportPo);
return aa(reportServiceFeign.receive(driverReport));
}
/**
* 数字物流网货车辆 - 数据上送
*/
public boolean vehicleReport(VehicleReportPo vehicleReportPo) {
if (ObjectUtil.isNull(vehicleReportPo)) {
throw new ServiceException("上送数据不能为空");
}
//检查是否开启网货
boolean flag = checkSwitch();
if (!flag) {
//如果未配置网货产品,返回跳过
return true;
}
//检查是否开始数据保存
Integer isPushReportData = getUserConfigSwitch().getIsPushReportData();
if (isPushReportData != 1){
return true;
}
VehicleReport vehicleReport = vehicleEntityConvertReport(vehicleReportPo);
return aa(reportServiceFeign.receive(vehicleReport));
}
/**
* 数字物流网货流水 - 数据上送
*/
public boolean capitalFlowReport(List<CapitalFlowReport> capitalFlowReports) {
if (ObjectUtil.isNull(capitalFlowReports)) {
throw new ServiceException("上送数据不能为空");
}
//检查是否开启网货
boolean flag = checkSwitch();
if (!flag) {
//如果未配置网货产品,返回跳过
return true;
}
//检查是否开始数据保存
Integer isPushReportData = getUserConfigSwitch().getIsPushReportData();
if (isPushReportData != 1){
return true;
}
return aa(reportServiceFeign.receive(capitalFlowReports));
}
// /**
// * 数字物流网货运单 - 数据上送
// */
// public boolean transportNoteReport(VehicleReportPo vehicleReportPo) {
//
// if (ObjectUtil.isNull(vehicleReportPo)) {
// throw new ServiceException("上送数据不能为空");
// }
// //检查是否开启网货
// boolean flag = checkSwitch();
// if (!flag) {
// //如果未配置网货产品,返回跳过
// return true;
// }
//
// TransportNoteReport vehicleReport = transportNoteEntityConvertReport(vehicleReportPo);
// return aa(reportServiceFeign.receive(vehicleReport));
// }
//
// /**
// * 数字物流网货运单 - 数据拼装
// */
// private TransportNoteReport transportNoteEntityConvertReport(TransportNoteReport transportNoteReport) {
// WlhyLoginUser wlhyLoginUser = SecurityUtils.getLoginUser().getWlhyLoginUser();
//
//
// return TransportNoteReport.builder()
// .build();
// }
//
// /**
// * 数字物流网货流水 - 数据拼装
// */
// private TransportNoteReport capitalFlowEntityConvertReport(TransportNoteReport transportNoteReport) {
// WlhyLoginUser wlhyLoginUser = SecurityUtils.getLoginUser().getWlhyLoginUser();
//
//
// return TransportNoteReport.builder()
// .build();
// }
/**
* 数字物流网货司机 - 数据拼装
*/
private DriverReport driverEntityConvertReport(UserDriverReportPo driver) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
int isCheckWorkCertificate = 0;
switch (driver.getDriverWorkLicenseAuthStatus()) {
case 2:
isCheckWorkCertificate = 1;
break;
case 3:
isCheckWorkCertificate = 2;
break;
}
return DriverReport
.builder()
.driverId(driver.getUserId().toString())
.driverName(driver.getUserName())
.drivingLicense(driver.getUserIdcardNumber())
.vehicleClass(driver.getDriverAllowDrivingTypeName())
.issuingOrganizations(driver.getDriverCertificationAuthority())
.qualificationCertificate(driver.getDriverWorkLicenseNumber())
.telephone(driver.getUserPhone())
.remark(driver.getUserRemark())
.validPeriodFrom(StrUtil.isNotEmpty(driver.getDriverLicenseDateFrom()) ? DateUtil.parseDate(driver.getDriverLicenseDateFrom()) : null)
.validPeriodTo(StrUtil.isNotEmpty(driver.getDriverLicenseDateTo()) ? DateUtil.parseDate(driver.getDriverLicenseDateTo()) : null)
.idCardPeriodFrom(StrUtil.isNotEmpty(driver.getUserIdcardDateFrom()) ? DateUtil.parseDate(driver.getUserIdcardDateFrom()) : null)
.idCardPeriodTo(StrUtil.isNotEmpty(driver.getUserIdcardDateTo()) ? DateUtil.parseDate(driver.getUserIdcardDateTo()) : null)
.idCardIsEndless(driver.getUserIdcardLong() == 1 ? 1 : 0)
.icenseFirstGetDate(StrUtil.isNotEmpty(driver.getDriverLicenseFirstDate()) ? DateUtil.parseDate(driver.getDriverLicenseFirstDate()) : null)
.identificationNumber(driver.getUserIdcardNumber())
.vehicleId(driver.getVehicleId().toString())
.vehicleNumber(driver.getVehicleLicensePlateNumber())
.grossMass(driver.getVehicleApprovedLoadQuantity())
.isCheckWorkCertificate(isCheckWorkCertificate)
.typeFlag(driver.getTypeFlag())
.organizationId(driver.getOrganizationId())
.organizationName(driver.getOrganizationName())
.topOrganizationId(driver.getTopOrganizationId())
.belongSystem("wlhy")
.build();
}
/**
* 数字物流网货车辆 - 数据拼装
*/
private VehicleReport vehicleEntityConvertReport(VehicleReportPo vehicle) {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
int checkRtcNo = 0;
switch (vehicle.getRoadTransportCertificateCheckoutStatus()) {
case 1:
checkRtcNo = 0;
break;
case 2:
checkRtcNo = 1;
break;
default:
checkRtcNo = 2;
break;
}
return VehicleReport
.builder()
.vehicleId(vehicle.getVehicleId().toString())
.vehicleNumber(vehicle.getVehicleLicensePlateNumber())
.vehiclePlateColorCode(vehicle.getOtherLicensePlateColorId())
.vehiclePlateColor(vehicle.getOtherLicensePlateColorName())
.vehicleTypeId(vehicle.getVehicleTypeId())
.vehicleType(vehicle.getVehicleTypeName())
.owner(vehicle.getVehicleUsePerson())
.useCharacter(vehicle.getVehicleUseNature())
.vin(vehicle.getVehicleIdentificationNumber())
.issuingOrganizations(vehicle.getVehicleLicenceIssuingAuthority())
.brand(vehicle.getVehicleBrandType())
.engineNumber(vehicle.getVehicleEngineNumber())
.registerDate(vehicle.getVehicleRegistrationDate())
.issueDate(vehicle.getVehicleIssueDate())
.transportLicenseExpireDate(vehicle.getRoadTransportCertificateExpireDate())
.vehicleEnergyTypeId(vehicle.getVehicleEnergyTypeId())
.vehicleEnergyType(vehicle.getVehicleEnergyTypeName())
.vehicleTonnage(vehicle.getVehicleApprovedLoadQuantity())
.grossMass(vehicle.getVehicleTotalMass())
.roadTransportCertificateNumber(vehicle.getRoadTransportCertificateNo())
.trailerVehiclePlateNumber(vehicle.getTrailerLicensePlateNumber())
.remark(vehicle.getVehicleRemark())
.driverId(vehicle.getUserId().toString())
.driverName(vehicle.getUserName())
.phone(vehicle.getUserPhone())
.checkRtcNo(checkRtcNo)
.checkEnterpriseRoadLicense(vehicle.getCheckEnterpriseRoadLicense())
.checkTruckExist(vehicle.getNetInStatus())
.organizationId(vehicle.getOrganizationId())
.organizationName(vehicle.getOrganizationName())
.topOrganizationId(vehicle.getTopOrganizationId())
.belongSystem("wlhy")
.build();
}
/**
* 校验是否配置网货产品
*
* @return true-开启,false-关闭
*/
private boolean checkSwitch() {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser)) {
throw new DigitalLogisticsException(UserError.TIMEOUT);
}
String key = CacheConstants.ORG_PRODUCT_KEY + loginUser.getUserPo().getTopOrganizationId();
OrgProductConfig orgProductConfig = redisService.getCacheObject(key);
if (null == orgProductConfig) {
return false;
}
//配置网货
return orgProductConfig.getWlhyFlag() == 1;
}
/**
* 获取用户所在组织的开关配置信息
* @return
*/
public UserConfigSwitchPo getUserConfigSwitch() {
//获取登录人信息
UserConfigSwitchPo userConfigSwitchPo;
LoginUser loginUser = SecurityUtils.getLoginUser();
UserPo userPo = loginUser.getUserPo();
Long organizationId = userPo.getOrganizationId();
userConfigSwitchPo = redisService.getCacheObject(CacheConstants.USER_COMFIG_SWITCH_KEY + organizationId);
if(null == userConfigSwitchPo || userConfigSwitchPo.getUserConfigSwitchId() == null){
//查询数据库,并且存到redis中
AjaxResult ajaxResult = systemServiceFeign.getUserConfigSwitchInfo();
if("200".equals(String.valueOf(ajaxResult.get("code")))){
userConfigSwitchPo = JSONObject.parseObject(JSONObject.toJSONString(ajaxResult.get("data")), UserConfigSwitchPo.class);
if(null != userConfigSwitchPo){
//存入redis中
redisService.setCacheObject(CacheConstants.USER_COMFIG_SWITCH_KEY + organizationId ,userConfigSwitchPo);
}
}
}
return userConfigSwitchPo;
}
/**
* 处理响应结果
*/
private boolean aa(AjaxResult receive) {
if (null != receive && "200".equals(String.valueOf(receive.get("code")))) {
return true;
} else if (null != receive && !"200".equals(String.valueOf(receive.get("code")))) {
throw new ServiceException("数据上送失败:" + receive.get("msg"));
}
return false;
}
}
@@ -0,0 +1,75 @@
package com.mhd.common.security.utils;
import java.util.Collection;
import java.util.List;
import com.alibaba.fastjson2.JSONArray;
import com.mhd.common.core.constant.Constants;
import com.mhd.common.core.utils.SpringUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.redis.service.RedisService;
import com.mhd.system.api.domain.SysDictData;
/**
* 字典工具类
*
* @author mhd
*/
public class DictUtils
{
/**
* 设置字典缓存
*
* @param key 参数键
* @param dictDatas 字典数据列表
*/
public static void setDictCache(String key, List<SysDictData> dictDatas)
{
SpringUtils.getBean(RedisService.class).setCacheObject(getCacheKey(key), dictDatas);
}
/**
* 获取字典缓存
*
* @param key 参数键
* @return dictDatas 字典数据列表
*/
public static List<SysDictData> getDictCache(String key)
{
JSONArray arrayCache = SpringUtils.getBean(RedisService.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(arrayCache))
{
return arrayCache.toList(SysDictData.class);
}
return null;
}
/**
* 删除指定字典缓存
*
* @param key 字典键
*/
public static void removeDictCache(String key)
{
SpringUtils.getBean(RedisService.class).deleteObject(getCacheKey(key));
}
/**
* 清空字典缓存
*/
public static void clearDictCache()
{
Collection<String> keys = SpringUtils.getBean(RedisService.class).keys(Constants.SYS_DICT_KEY + "*");
SpringUtils.getBean(RedisService.class).deleteObject(keys);
}
/**
* 设置cache key
*
* @param configKey 参数键
* @return 缓存键key
*/
public static String getCacheKey(String configKey)
{
return Constants.SYS_DICT_KEY + configKey;
}
}
@@ -0,0 +1,117 @@
package com.mhd.common.security.utils;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.mhd.common.core.constant.SecurityConstants;
import com.mhd.common.core.constant.TokenConstants;
import com.mhd.common.core.context.SecurityContextHolder;
import com.mhd.common.core.utils.ServletUtils;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.system.api.model.LoginUser;
/**
* 权限获取工具类
*
* @author mhd
*/
public class SecurityUtils
{
/**
* 获取用户ID
*/
public static Long getUserId()
{
return SecurityContextHolder.getUserId();
}
/**
* 获取用户名称
*/
public static String getUsername()
{
return SecurityContextHolder.getUserName();
}
/**
* 获取用户key
*/
public static String getUserKey()
{
return SecurityContextHolder.getUserKey();
}
/**
* 获取登录用户信息
*/
public static LoginUser getLoginUser()
{
return SecurityContextHolder.get(SecurityConstants.LOGIN_USER, LoginUser.class);
}
/**
* 获取请求token
*/
public static String getToken()
{
return getToken(ServletUtils.getRequest());
}
/**
* 根据request获取请求token
*/
public static String getToken(HttpServletRequest request)
{
// 从header获取token标识
String token = request.getHeader(TokenConstants.AUTHENTICATION);
return replaceTokenPrefix(token);
}
/**
* 裁剪token前缀
*/
public static String replaceTokenPrefix(String token)
{
// 如果前端设置了令牌前缀,则裁剪掉前缀
if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
{
token = token.replaceFirst(TokenConstants.PREFIX, "");
}
return token;
}
/**
* 是否为管理员
*
* @param userId 用户ID
* @return 结果
*/
public static boolean isAdmin(Long userId)
{
return userId != null && 1L == userId;
}
/**
* 生成BCryptPasswordEncoder密码
*
* @param password 密码
* @return 加密字符串
*/
public static String encryptPassword(String password)
{
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.encode(password);
}
/**
* 判断密码是否相同
*
* @param rawPassword 真实密码
* @param encodedPassword 加密后字符
* @return 结果
*/
public static boolean matchesPassword(String rawPassword, String encodedPassword)
{
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.matches(rawPassword, encodedPassword);
}
}
@@ -0,0 +1,93 @@
package com.mhd.common.security.utils.password;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.mhd.common.core.domain.po.UserPo;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.AESUtil;
import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.UserServiceFeign;
import com.mhd.system.api.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @Description 密码校验工具类
* @Author zg
* @Version 1.0
* @Date 2023/11/8 8:55
*/
//@Component
public class PasswordCheckUtils {
// @Autowired
// private UserServiceFeign userServiceFeign;
//
// private static PasswordCheckUtils utils;
//
// public PasswordCheckUtils(UserServiceFeign userServiceFeign) {
// this.userServiceFeign = userServiceFeign;
// }
//
//
// @PostConstruct
// public void init(){
// utils = this;
// utils.userServiceFeign= userServiceFeign;
// }
/**
* @param payPassword 支付密码
* @param userPo 用户信息
* @return
*/
public static boolean payPasswordCheck(String payPassword, UserPo userPo) {
//解密支付密码
payPassword = AESUtil.decrypt(payPassword);
if (StrUtil.isEmpty(payPassword)){
throw new ServiceException("解密失败");
}
if (ObjUtil.isEmpty(userPo)){
throw new ServiceException("用户信息不能为空");
}
if (StrUtil.isEmpty(userPo.getUserPayPassword())) {
throw new ServiceException("请设置支付密码");
}
//校验用户支付密码是否正确
return PasswordUtil.matchesPassword(userPo.getUserAccount(), payPassword, userPo.getUserSalt(), userPo.getUserPayPassword());
}
// public static boolean payPasswordCheck1(String payPassword) {
// //解密支付密码
// payPassword = AESUtil.decrypt(payPassword);
// if (StrUtil.isEmpty(payPassword)){
// throw new ServiceException("密码解密失败");
// }
//
// //获取当前登录人
// LoginUser loginUser = SecurityUtils.getLoginUser();
// UserPo userPo = loginUser.getUserPo();
// //临时方案,用户修改密码后,用token获取用户信息,数据不一致,需要重新获取用户信息
// AjaxResult ajaxResult = utils.userServiceFeign.getInfo(userPo.getUserId());
// if("200".equals(String.valueOf(ajaxResult.get("code")))){
// userPo = JSONUtil.toBean(JSONObject.toJSONString(ajaxResult.get("data")), UserPo.class);
// }else {
// throw new ServiceException("用户信息获取失败");
// }
// if (StrUtil.isEmpty(userPo.getUserPayPassword())) {
// throw new ServiceException("请设置支付密码");
// }
//
// //校验用户支付密码是否正确
// return PasswordUtil.matchesPassword(userPo.getUserAccount(), payPassword, userPo.getUserSalt(), userPo.getUserPayPassword());
// }
}
@@ -0,0 +1,186 @@
package com.mhd.common.security.utils.password;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.Key;
import java.security.SecureRandom;
public class PasswordUtil {
/**
* JAVA6支持以下任意一种算法 PBEWITHMD5ANDDES PBEWITHMD5ANDTRIPLEDES
* PBEWITHSHAANDDESEDE PBEWITHSHA1ANDRC2_40 PBKDF2WITHHMACSHA1
* */
/**
* 定义使用的算法为:PBEWITHMD5andDES算法
*/
public static final String ALGORITHM = "PBEWithMD5AndDES";//加密算法
public static final String Salt = "26847931";//密钥
/**
* 定义迭代次数为1000次
*/
private static final int ITERATIONCOUNT = 1000;
/**
* 获取加密算法中使用的盐值,解密中使用的盐值必须与加密中使用的相同才能完成操作. 盐长度必须为8字节
*
* @return byte[] 盐值
*/
public static byte[] getSalt() throws Exception {
// 实例化安全随机数
SecureRandom random = new SecureRandom();
// 产出盐
return random.generateSeed(8);
}
public static byte[] getStaticSalt() {
// 产出盐
return Salt.getBytes();
}
/**
* 根据PBE密码生成一把密钥
*
* @param password 生成密钥时所使用的密码
* @return Key PBE算法密钥
*/
private static Key getPBEKey(String password) {
// 实例化使用的算法
SecretKeyFactory keyFactory;
SecretKey secretKey = null;
try {
keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
// 设置PBE密钥参数
PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
// 生成密钥
secretKey = keyFactory.generateSecret(keySpec);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return secretKey;
}
/**
* 加密明文字符串
*
* @param plaintext 待加密的明文字符串
* @param password 生成密钥时所使用的密码
* @param salt 盐值
* @return 加密后的密文字符串
* @throws Exception
*/
public static String encrypt(String plaintext, String password, String salt) {
Key key = getPBEKey(password);
byte[] encipheredData = null;
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT);
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
//update-begin-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
encipheredData = cipher.doFinal(plaintext.getBytes("utf-8"));
//update-end-author:sccott date:20180815 for:中文作为用户名时,加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
} catch (Exception e) {
}
return bytesToHexString(encipheredData);
}
/**
* 解密密文字符串
*
* @param ciphertext 待解密的密文字符串
* @param password 生成密钥时所使用的密码(如需解密,该参数需要与加密时使用的一致)
* @param salt 盐值(如需解密,该参数需要与加密时使用的一致)
* @return 解密后的明文字符串
* @throws Exception
*/
public static String decrypt(String ciphertext, String password, String salt) {
Key key = getPBEKey(password);
byte[] passDec = null;
PBEParameterSpec parameterSpec = new PBEParameterSpec(salt.getBytes(), ITERATIONCOUNT);
try {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, parameterSpec);
passDec = cipher.doFinal(hexStringToBytes(ciphertext));
} catch (Exception e) {
// TODO: handle exception
}
return new String(passDec);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param src 字节数组
* @return
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder("");
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
/**
* 将十六进制字符串转换为字节数组
*
* @param hexString 十六进制字符串
* @return
*/
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* 判断密码是否相同
*
* @param userAccount 登录账号
* @param rawPassword 真实密码
* @param salt 盐
* @param encodedPassword 加密后字符
* @return 结果
*/
public static boolean matchesPassword(String userAccount,String rawPassword, String salt, String encodedPassword) {
Boolean matches = false;
String passwordEncode = encrypt(userAccount, rawPassword, salt);
matches = passwordEncode.equals(encodedPassword);
return matches;
}
}