commit 6e8cfb63f9b1cc2b320eee044f33b982613ba7a5
Author: TangZhaoZhen <207525215@qq.com>
Date: Mon Oct 16 20:51:07 2023 +0800
初始化
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..09bdfea
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### JRebel ###
+rebel.xml
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b676d10
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+ com.four
+ four-common
+ 3.6.3
+
+ 4.0.0
+
+ four-common-security
+ 3.6.3
+
+ four-common-security安全模块
+
+
+
+
+
+
+ org.springframework
+ spring-webmvc
+
+
+
+
+ com.four
+ four-api-system
+
+
+
+
+ com.four
+ four-common-redis
+
+
+
+
+
diff --git a/src/main/java/com/four/common/security/annotation/EnableCustomConfig.java b/src/main/java/com/four/common/security/annotation/EnableCustomConfig.java
new file mode 100644
index 0000000..a44ac3b
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/EnableCustomConfig.java
@@ -0,0 +1,31 @@
+package com.four.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 org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Import;
+import org.springframework.scheduling.annotation.EnableAsync;
+import com.four.common.security.config.ApplicationConfig;
+import com.four.common.security.feign.FeignAutoConfiguration;
+
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+// 表示通过aop框架暴露该代理对象,AopContext能够访问
+@EnableAspectJAutoProxy(exposeProxy = true)
+// 指定要扫描的Mapper类的包的路径
+@MapperScan("com.four.**.mapper")
+// 开启线程异步执行
+@EnableAsync
+// 自动加载类
+@Import({ ApplicationConfig.class, FeignAutoConfiguration.class })
+public @interface EnableCustomConfig
+{
+
+}
diff --git a/src/main/java/com/four/common/security/annotation/EnableRyFeignClients.java b/src/main/java/com/four/common/security/annotation/EnableRyFeignClients.java
new file mode 100644
index 0000000..adade7c
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/EnableRyFeignClients.java
@@ -0,0 +1,27 @@
+package com.four.common.security.annotation;
+
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import java.lang.annotation.*;
+
+/**
+ * 自定义feign注解
+ * 添加basePackages路径
+ *
+ * @author ruoyi
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EnableFeignClients
+public @interface EnableRyFeignClients
+{
+ String[] value() default {};
+
+ String[] basePackages() default { "com.four" };
+
+ Class>[] basePackageClasses() default {};
+
+ Class>[] defaultConfiguration() default {};
+
+ Class>[] clients() default {};
+}
diff --git a/src/main/java/com/four/common/security/annotation/InnerAuth.java b/src/main/java/com/four/common/security/annotation/InnerAuth.java
new file mode 100644
index 0000000..0d2ea09
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/InnerAuth.java
@@ -0,0 +1,19 @@
+package com.four.common.security.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 内部认证注解
+ *
+ * @author ruoyi
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface InnerAuth
+{
+ /**
+ * 是否校验用户信息
+ */
+ boolean isUser() default false;
+}
\ No newline at end of file
diff --git a/src/main/java/com/four/common/security/annotation/Logical.java b/src/main/java/com/four/common/security/annotation/Logical.java
new file mode 100644
index 0000000..4010f65
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/Logical.java
@@ -0,0 +1,20 @@
+package com.four.common.security.annotation;
+
+/**
+ * 权限注解的验证模式
+ *
+ * @author ruoyi
+ *
+ */
+public enum Logical
+{
+ /**
+ * 必须具有所有的元素
+ */
+ AND,
+
+ /**
+ * 只需具有其中一个元素
+ */
+ OR
+}
diff --git a/src/main/java/com/four/common/security/annotation/RequiresLogin.java b/src/main/java/com/four/common/security/annotation/RequiresLogin.java
new file mode 100644
index 0000000..6c1a16f
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/RequiresLogin.java
@@ -0,0 +1,18 @@
+package com.four.common.security.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 登录认证:只有登录之后才能进入该方法
+ *
+ * @author ruoyi
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface RequiresLogin
+{
+}
diff --git a/src/main/java/com/four/common/security/annotation/RequiresPermissions.java b/src/main/java/com/four/common/security/annotation/RequiresPermissions.java
new file mode 100644
index 0000000..e1d6f90
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/RequiresPermissions.java
@@ -0,0 +1,27 @@
+package com.four.common.security.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 权限认证:必须具有指定权限才能进入该方法
+ *
+ * @author ruoyi
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface RequiresPermissions
+{
+ /**
+ * 需要校验的权限码
+ */
+ String[] value() default {};
+
+ /**
+ * 验证模式:AND | OR,默认AND
+ */
+ Logical logical() default Logical.AND;
+}
diff --git a/src/main/java/com/four/common/security/annotation/RequiresRoles.java b/src/main/java/com/four/common/security/annotation/RequiresRoles.java
new file mode 100644
index 0000000..9ac31a2
--- /dev/null
+++ b/src/main/java/com/four/common/security/annotation/RequiresRoles.java
@@ -0,0 +1,26 @@
+package com.four.common.security.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 角色认证:必须具有指定角色标识才能进入该方法
+ *
+ * @author ruoyi
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.METHOD, ElementType.TYPE })
+public @interface RequiresRoles
+{
+ /**
+ * 需要校验的角色标识
+ */
+ String[] value() default {};
+
+ /**
+ * 验证逻辑:AND | OR,默认AND
+ */
+ Logical logical() default Logical.AND;
+}
diff --git a/src/main/java/com/four/common/security/aspect/InnerAuthAspect.java b/src/main/java/com/four/common/security/aspect/InnerAuthAspect.java
new file mode 100644
index 0000000..4aabdfe
--- /dev/null
+++ b/src/main/java/com/four/common/security/aspect/InnerAuthAspect.java
@@ -0,0 +1,51 @@
+package com.four.common.security.aspect;
+
+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.four.common.core.constant.SecurityConstants;
+import com.four.common.core.exception.InnerAuthException;
+import com.four.common.core.utils.ServletUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.security.annotation.InnerAuth;
+
+/**
+ * 内部服务调用验证处理
+ *
+ * @author ruoyi
+ */
+@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;
+ }
+}
diff --git a/src/main/java/com/four/common/security/aspect/PreAuthorizeAspect.java b/src/main/java/com/four/common/security/aspect/PreAuthorizeAspect.java
new file mode 100644
index 0000000..8811452
--- /dev/null
+++ b/src/main/java/com/four/common/security/aspect/PreAuthorizeAspect.java
@@ -0,0 +1,97 @@
+package com.four.common.security.aspect;
+
+import java.lang.reflect.Method;
+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;
+import com.four.common.security.annotation.RequiresLogin;
+import com.four.common.security.annotation.RequiresPermissions;
+import com.four.common.security.annotation.RequiresRoles;
+import com.four.common.security.auth.AuthUtil;
+
+/**
+ * 基于 Spring Aop 的注解鉴权
+ *
+ * @author kong
+ */
+@Aspect
+@Component
+public class PreAuthorizeAspect
+{
+ /**
+ * 构建
+ */
+ public PreAuthorizeAspect()
+ {
+ }
+
+ /**
+ * 定义AOP签名 (切入所有使用鉴权注解的方法)
+ */
+ public static final String POINTCUT_SIGN = " @annotation(com.four.common.security.annotation.RequiresLogin) || "
+ + "@annotation(com.four.common.security.annotation.RequiresPermissions) || "
+ + "@annotation(com.four.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);
+ }
+ }
+}
diff --git a/src/main/java/com/four/common/security/auth/AuthLogic.java b/src/main/java/com/four/common/security/auth/AuthLogic.java
new file mode 100644
index 0000000..0128214
--- /dev/null
+++ b/src/main/java/com/four/common/security/auth/AuthLogic.java
@@ -0,0 +1,373 @@
+package com.four.common.security.auth;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import org.springframework.util.PatternMatchUtils;
+import com.four.common.core.context.SecurityContextHolder;
+import com.four.common.core.exception.auth.NotLoginException;
+import com.four.common.core.exception.auth.NotPermissionException;
+import com.four.common.core.exception.auth.NotRoleException;
+import com.four.common.core.utils.SpringUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.security.annotation.Logical;
+import com.four.common.security.annotation.RequiresLogin;
+import com.four.common.security.annotation.RequiresPermissions;
+import com.four.common.security.annotation.RequiresRoles;
+import com.four.common.security.service.TokenService;
+import com.four.common.security.utils.SecurityUtils;
+import com.four.system.api.model.LoginUser;
+
+/**
+ * Token 权限验证,逻辑实现类
+ *
+ * @author ruoyi
+ */
+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)
+ {
+ SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ","));
+ if (requiresPermissions.logical() == Logical.AND)
+ {
+ checkPermiAnd(requiresPermissions.value());
+ }
+ else
+ {
+ checkPermiOr(requiresPermissions.value());
+ }
+ }
+
+ /**
+ * 验证用户是否含有指定权限,必须全部拥有
+ *
+ * @param permissions 权限列表
+ */
+ public void checkPermiAnd(String... permissions)
+ {
+ Set permissionList = getPermiList();
+ for (String permission : permissions)
+ {
+ if (!hasPermi(permissionList, permission))
+ {
+ throw new NotPermissionException(permission);
+ }
+ }
+ }
+
+ /**
+ * 验证用户是否含有指定权限,只需包含其中一个
+ *
+ * @param permissions 权限码数组
+ */
+ public void checkPermiOr(String... permissions)
+ {
+ Set 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 roleList = getRoleList();
+ for (String role : roles)
+ {
+ if (!hasRole(roleList, role))
+ {
+ throw new NotRoleException(role);
+ }
+ }
+ }
+
+ /**
+ * 验证用户是否含有指定角色,只需包含其中一个
+ *
+ * @param roles 角色标识数组
+ */
+ public void checkRoleOr(String... roles)
+ {
+ Set 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 getRoleList()
+ {
+ try
+ {
+ LoginUser loginUser = getLoginUser();
+ return loginUser.getRoles();
+ }
+ catch (Exception e)
+ {
+ return new HashSet<>();
+ }
+ }
+
+ /**
+ * 获取当前账号的权限列表
+ *
+ * @return 权限列表
+ */
+ public Set getPermiList()
+ {
+ try
+ {
+ LoginUser loginUser = getLoginUser();
+ return loginUser.getPermissions();
+ }
+ catch (Exception e)
+ {
+ return new HashSet<>();
+ }
+ }
+
+ /**
+ * 判断是否包含权限
+ *
+ * @param authorities 权限列表
+ * @param permission 权限字符串
+ * @return 用户是否具备某权限
+ */
+ public boolean hasPermi(Collection 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 roles, String role)
+ {
+ return roles.stream().filter(StringUtils::hasText)
+ .anyMatch(x -> SUPER_ADMIN.contains(x) || PatternMatchUtils.simpleMatch(x, role));
+ }
+}
diff --git a/src/main/java/com/four/common/security/auth/AuthUtil.java b/src/main/java/com/four/common/security/auth/AuthUtil.java
new file mode 100644
index 0000000..6479e70
--- /dev/null
+++ b/src/main/java/com/four/common/security/auth/AuthUtil.java
@@ -0,0 +1,167 @@
+package com.four.common.security.auth;
+
+import com.four.common.security.annotation.RequiresPermissions;
+import com.four.common.security.annotation.RequiresRoles;
+import com.four.system.api.model.LoginUser;
+
+/**
+ * Token 权限验证工具类
+ *
+ * @author ruoyi
+ */
+public class AuthUtil
+{
+ /**
+ * 底层的 AuthLogic 对象
+ */
+ public static AuthLogic authLogic = new AuthLogic();
+
+ /**
+ * 会话注销
+ */
+ public static void logout()
+ {
+ authLogic.logout();
+ }
+
+ /**
+ * 会话注销,根据指定Token
+ *
+ * @param token 指定token
+ */
+ public static void logoutByToken(String token)
+ {
+ authLogic.logoutByToken(token);
+ }
+
+ /**
+ * 检验当前会话是否已经登录,如未登录,则抛出异常
+ */
+ public static void checkLogin()
+ {
+ authLogic.checkLogin();
+ }
+
+ /**
+ * 获取当前登录用户信息
+ *
+ * @param token 指定token
+ * @return 用户信息
+ */
+ public static LoginUser getLoginUser(String token)
+ {
+ return authLogic.getLoginUser(token);
+ }
+
+ /**
+ * 验证当前用户有效期
+ *
+ * @param loginUser 用户信息
+ */
+ 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);
+ }
+}
diff --git a/src/main/java/com/four/common/security/config/ApplicationConfig.java b/src/main/java/com/four/common/security/config/ApplicationConfig.java
new file mode 100644
index 0000000..4535aee
--- /dev/null
+++ b/src/main/java/com/four/common/security/config/ApplicationConfig.java
@@ -0,0 +1,22 @@
+package com.four.common.security.config;
+
+import java.util.TimeZone;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * 系统配置
+ *
+ * @author ruoyi
+ */
+public class ApplicationConfig
+{
+ /**
+ * 时区配置
+ */
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization()
+ {
+ return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
+ }
+}
diff --git a/src/main/java/com/four/common/security/config/WebMvcConfig.java b/src/main/java/com/four/common/security/config/WebMvcConfig.java
new file mode 100644
index 0000000..930218c
--- /dev/null
+++ b/src/main/java/com/four/common/security/config/WebMvcConfig.java
@@ -0,0 +1,33 @@
+package com.four.common.security.config;
+
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import com.four.common.security.interceptor.HeaderInterceptor;
+
+/**
+ * 拦截器配置
+ *
+ * @author ruoyi
+ */
+public class WebMvcConfig implements WebMvcConfigurer
+{
+ /** 不需要拦截地址 */
+ public static final String[] excludeUrls = { "/login", "/logout", "/refresh" };
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry)
+ {
+ registry.addInterceptor(getHeaderInterceptor())
+ .addPathPatterns("/**")
+ .excludePathPatterns(excludeUrls)
+ .order(-10);
+ }
+
+ /**
+ * 自定义请求头拦截器
+ */
+ public HeaderInterceptor getHeaderInterceptor()
+ {
+ return new HeaderInterceptor();
+ }
+}
diff --git a/src/main/java/com/four/common/security/feign/FeignAutoConfiguration.java b/src/main/java/com/four/common/security/feign/FeignAutoConfiguration.java
new file mode 100644
index 0000000..0ef42d3
--- /dev/null
+++ b/src/main/java/com/four/common/security/feign/FeignAutoConfiguration.java
@@ -0,0 +1,20 @@
+package com.four.common.security.feign;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import feign.RequestInterceptor;
+
+/**
+ * Feign 配置注册
+ *
+ * @author ruoyi
+ **/
+@Configuration
+public class FeignAutoConfiguration
+{
+ @Bean
+ public RequestInterceptor requestInterceptor()
+ {
+ return new FeignRequestInterceptor();
+ }
+}
diff --git a/src/main/java/com/four/common/security/feign/FeignRequestInterceptor.java b/src/main/java/com/four/common/security/feign/FeignRequestInterceptor.java
new file mode 100644
index 0000000..f2af96d
--- /dev/null
+++ b/src/main/java/com/four/common/security/feign/FeignRequestInterceptor.java
@@ -0,0 +1,54 @@
+package com.four.common.security.feign;
+
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.stereotype.Component;
+import com.four.common.core.constant.SecurityConstants;
+import com.four.common.core.utils.ServletUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.core.utils.ip.IpUtils;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+
+/**
+ * feign 请求拦截器
+ *
+ * @author ruoyi
+ */
+@Component
+public class FeignRequestInterceptor implements RequestInterceptor
+{
+ @Override
+ public void apply(RequestTemplate requestTemplate)
+ {
+ HttpServletRequest httpServletRequest = ServletUtils.getRequest();
+ if (StringUtils.isNotNull(httpServletRequest))
+ {
+ Map headers = ServletUtils.getHeaders(httpServletRequest);
+ // 传递用户信息请求头,防止丢失
+ String userId = headers.get(SecurityConstants.DETAILS_USER_ID);
+ if (StringUtils.isNotEmpty(userId))
+ {
+ requestTemplate.header(SecurityConstants.DETAILS_USER_ID, userId);
+ }
+ String userKey = headers.get(SecurityConstants.USER_KEY);
+ if (StringUtils.isNotEmpty(userKey))
+ {
+ requestTemplate.header(SecurityConstants.USER_KEY, userKey);
+ }
+ String userName = headers.get(SecurityConstants.DETAILS_USERNAME);
+ if (StringUtils.isNotEmpty(userName))
+ {
+ requestTemplate.header(SecurityConstants.DETAILS_USERNAME, userName);
+ }
+ String authentication = headers.get(SecurityConstants.AUTHORIZATION_HEADER);
+ if (StringUtils.isNotEmpty(authentication))
+ {
+ requestTemplate.header(SecurityConstants.AUTHORIZATION_HEADER, authentication);
+ }
+
+ // 配置客户端IP
+ requestTemplate.header("X-Forwarded-For", IpUtils.getIpAddr());
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/four/common/security/handler/GlobalExceptionHandler.java b/src/main/java/com/four/common/security/handler/GlobalExceptionHandler.java
new file mode 100644
index 0000000..8eaf6c3
--- /dev/null
+++ b/src/main/java/com/four/common/security/handler/GlobalExceptionHandler.java
@@ -0,0 +1,159 @@
+package com.four.common.security.handler;
+
+import com.four.common.core.constant.HttpStatus;
+import com.four.common.core.exception.DemoModeException;
+import com.four.common.core.exception.InnerAuthException;
+import com.four.common.core.exception.ServiceException;
+import com.four.common.core.exception.auth.NotPermissionException;
+import com.four.common.core.exception.auth.NotRoleException;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.core.web.domain.AjaxResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.validation.BindException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingPathVariableException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 全局异常处理器
+ *
+ * @author ruoyi
+ */
+@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(MissingPathVariableException.class)
+ public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
+ {
+ String requestURI = request.getRequestURI();
+ log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
+ return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
+ }
+
+ /**
+ * 请求参数类型不匹配
+ */
+ @ExceptionHandler(MethodArgumentTypeMismatchException.class)
+ public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
+ {
+ String requestURI = request.getRequestURI();
+ log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
+ return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
+ }
+
+ /**
+ * 拦截未知的运行时异常
+ */
+ @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 Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
+ {
+ log.error(e.getMessage(), e);
+ String message = e.getBindingResult().getFieldError().getDefaultMessage();
+ return AjaxResult.error(message);
+ }
+
+ /**
+ * 内部认证异常
+ */
+ @ExceptionHandler(InnerAuthException.class)
+ public AjaxResult handleInnerAuthException(InnerAuthException e)
+ {
+ return AjaxResult.error(e.getMessage());
+ }
+
+ /**
+ * 演示模式异常
+ */
+ @ExceptionHandler(DemoModeException.class)
+ public AjaxResult handleDemoModeException(DemoModeException e)
+ {
+ return AjaxResult.error("演示模式,不允许操作");
+ }
+}
diff --git a/src/main/java/com/four/common/security/interceptor/HeaderInterceptor.java b/src/main/java/com/four/common/security/interceptor/HeaderInterceptor.java
new file mode 100644
index 0000000..c0989c3
--- /dev/null
+++ b/src/main/java/com/four/common/security/interceptor/HeaderInterceptor.java
@@ -0,0 +1,54 @@
+package com.four.common.security.interceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
+import com.four.common.core.constant.SecurityConstants;
+import com.four.common.core.context.SecurityContextHolder;
+import com.four.common.core.utils.ServletUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.security.auth.AuthUtil;
+import com.four.common.security.utils.SecurityUtils;
+import com.four.system.api.model.LoginUser;
+
+/**
+ * 自定义请求头拦截器,将Header数据封装到线程变量中方便获取
+ * 注意:此拦截器会同时验证当前用户有效期自动刷新有效期
+ *
+ * @author ruoyi
+ */
+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();
+ }
+}
diff --git a/src/main/java/com/four/common/security/service/TokenService.java b/src/main/java/com/four/common/security/service/TokenService.java
new file mode 100644
index 0000000..91297ab
--- /dev/null
+++ b/src/main/java/com/four/common/security/service/TokenService.java
@@ -0,0 +1,174 @@
+package com.four.common.security.service;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.http.HttpServletRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import com.four.common.core.constant.CacheConstants;
+import com.four.common.core.constant.SecurityConstants;
+import com.four.common.core.utils.JwtUtils;
+import com.four.common.core.utils.ServletUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.core.utils.ip.IpUtils;
+import com.four.common.core.utils.uuid.IdUtils;
+import com.four.common.redis.service.RedisService;
+import com.four.common.security.utils.SecurityUtils;
+import com.four.system.api.model.LoginUser;
+
+/**
+ * token验证处理
+ *
+ * @author ruoyi
+ */
+@Component
+public class TokenService
+{
+ private static final Logger log = LoggerFactory.getLogger(TokenService.class);
+
+ @Autowired
+ private RedisService redisService;
+
+ protected static final long MILLIS_SECOND = 1000;
+
+ protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
+
+ private final static long expireTime = CacheConstants.EXPIRATION;
+
+ private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;
+
+ private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;
+
+ /**
+ * 创建令牌
+ */
+ public Map createToken(LoginUser loginUser)
+ {
+ String token = IdUtils.fastUUID();
+ Long userId = loginUser.getSysUser().getUserId();
+ String userName = loginUser.getSysUser().getUserName();
+ loginUser.setToken(token);
+ loginUser.setUserid(userId);
+ loginUser.setUsername(userName);
+ loginUser.setIpaddr(IpUtils.getIpAddr());
+ refreshToken(loginUser);
+
+ // Jwt存储信息
+ Map claimsMap = new HashMap();
+ claimsMap.put(SecurityConstants.USER_KEY, token);
+ claimsMap.put(SecurityConstants.DETAILS_USER_ID, userId);
+ claimsMap.put(SecurityConstants.DETAILS_USERNAME, userName);
+
+ // 接口返回信息
+ Map rspMap = new HashMap();
+ rspMap.put("access_token", JwtUtils.createToken(claimsMap));
+ rspMap.put("expires_in", expireTime);
+ return rspMap;
+ }
+
+ /**
+ * 获取用户身份信息
+ *
+ * @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)
+ {
+ log.error("获取用户信息异常'{}'", e.getMessage());
+ }
+ 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);
+ }
+ }
+
+ /**
+ * 刷新令牌有效期
+ *
+ * @param loginUser 登录信息
+ */
+ public void refreshToken(LoginUser loginUser)
+ {
+ loginUser.setLoginTime(System.currentTimeMillis());
+ loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE);
+ // 根据uuid将loginUser缓存
+ String userKey = getTokenKey(loginUser.getToken());
+ redisService.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES);
+ }
+
+ private String getTokenKey(String token)
+ {
+ return ACCESS_TOKEN + token;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/four/common/security/utils/DictUtils.java b/src/main/java/com/four/common/security/utils/DictUtils.java
new file mode 100644
index 0000000..b3968c9
--- /dev/null
+++ b/src/main/java/com/four/common/security/utils/DictUtils.java
@@ -0,0 +1,75 @@
+package com.four.common.security.utils;
+
+import java.util.Collection;
+import java.util.List;
+import com.alibaba.fastjson2.JSONArray;
+import com.four.common.core.constant.CacheConstants;
+import com.four.common.core.utils.SpringUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.common.redis.service.RedisService;
+import com.four.system.api.domain.SysDictData;
+
+/**
+ * 字典工具类
+ *
+ * @author ruoyi
+ */
+public class DictUtils
+{
+ /**
+ * 设置字典缓存
+ *
+ * @param key 参数键
+ * @param dictDatas 字典数据列表
+ */
+ public static void setDictCache(String key, List dictDatas)
+ {
+ SpringUtils.getBean(RedisService.class).setCacheObject(getCacheKey(key), dictDatas);
+ }
+
+ /**
+ * 获取字典缓存
+ *
+ * @param key 参数键
+ * @return dictDatas 字典数据列表
+ */
+ public static List 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 keys = SpringUtils.getBean(RedisService.class).keys(CacheConstants.SYS_DICT_KEY + "*");
+ SpringUtils.getBean(RedisService.class).deleteObject(keys);
+ }
+
+ /**
+ * 设置cache key
+ *
+ * @param configKey 参数键
+ * @return 缓存键key
+ */
+ public static String getCacheKey(String configKey)
+ {
+ return CacheConstants.SYS_DICT_KEY + configKey;
+ }
+}
diff --git a/src/main/java/com/four/common/security/utils/SecurityUtils.java b/src/main/java/com/four/common/security/utils/SecurityUtils.java
new file mode 100644
index 0000000..9e41a5f
--- /dev/null
+++ b/src/main/java/com/four/common/security/utils/SecurityUtils.java
@@ -0,0 +1,117 @@
+package com.four.common.security.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import com.four.common.core.constant.SecurityConstants;
+import com.four.common.core.constant.TokenConstants;
+import com.four.common.core.context.SecurityContextHolder;
+import com.four.common.core.utils.ServletUtils;
+import com.four.common.core.utils.StringUtils;
+import com.four.system.api.model.LoginUser;
+
+/**
+ * 权限获取工具类
+ *
+ * @author ruoyi
+ */
+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);
+ }
+}
diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..328b019
--- /dev/null
+++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1,5 @@
+com.four.common.security.config.WebMvcConfig
+com.four.common.security.service.TokenService
+com.four.common.security.aspect.PreAuthorizeAspect
+com.four.common.security.aspect.InnerAuthAspect
+com.four.common.security.handler.GlobalExceptionHandler