common-security

master
Lemon 2023-08-13 15:34:39 +08:00
commit 27996f5dc5
23 changed files with 1652 additions and 0 deletions

46
.gitignore vendored 100644
View File

@ -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

41
pom.xml 100644
View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.luck</groupId>
<artifactId>luck-common</artifactId>
<version>3.6.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>luck-common-security</artifactId>
<version>3.6.3</version>
<description>
luck-common-security安全模块
</description>
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<!-- RuoYi Api System -->
<dependency>
<groupId>com.luck</groupId>
<artifactId>luck-system-remote</artifactId>
</dependency>
<!-- RuoYi Common Redis-->
<dependency>
<groupId>com.luck</groupId>
<artifactId>luck-common-redis</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,27 @@
package com.luck.common.security.annotation;
import com.luck.common.security.config.ApplicationConfig;
import com.luck.common.security.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;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 表示通过aop框架暴露该代理对象,AopContext能够访问
@EnableAspectJAutoProxy(exposeProxy = true)
// 指定要扫描的Mapper类的包的路径
@MapperScan("com.luck.**.mapper")
// 开启线程异步执行
@EnableAsync
// 自动加载类
@Import({ ApplicationConfig.class, FeignAutoConfiguration.class })
public @interface EnableCustomConfig
{
}

View File

@ -0,0 +1,27 @@
package com.luck.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.luck" };
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}

View File

@ -0,0 +1,19 @@
package com.luck.common.security.annotation;
import java.lang.annotation.*;
/**
*
*
* @author ruoyi
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InnerAuth
{
/**
*
*/
boolean isUser() default false;
}

View File

@ -0,0 +1,20 @@
package com.luck.common.security.annotation;
/**
*
*
* @author ruoyi
*
*/
public enum Logical
{
/**
*
*/
AND,
/**
*
*/
OR
}

View File

@ -0,0 +1,18 @@
package com.luck.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
{
}

View File

@ -0,0 +1,27 @@
package com.luck.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 | ORAND
*/
Logical logical() default Logical.AND;
}

View File

@ -0,0 +1,26 @@
package com.luck.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 | ORAND
*/
Logical logical() default Logical.AND;
}

View File

@ -0,0 +1,51 @@
package com.luck.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.luck.common.core.constant.SecurityConstants;
import com.luck.common.core.exception.InnerAuthException;
import com.luck.common.core.utils.ServletUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.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;
}
}

View File

@ -0,0 +1,97 @@
package com.luck.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.luck.common.security.annotation.RequiresLogin;
import com.luck.common.security.annotation.RequiresPermissions;
import com.luck.common.security.annotation.RequiresRoles;
import com.luck.common.security.auth.AuthUtil;
/**
* Spring Aop
*
* @author kong
*/
@Aspect
@Component
public class PreAuthorizeAspect
{
/**
*
*/
public PreAuthorizeAspect()
{
}
/**
* AOP (使)
*/
public static final String POINTCUT_SIGN = " @annotation(com.luck.common.security.annotation.RequiresLogin) || "
+ "@annotation(com.luck.common.security.annotation.RequiresPermissions) || "
+ "@annotation(com.luck.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);
}
}
}

View File

@ -0,0 +1,374 @@
package com.luck.common.security.auth;
import com.luck.common.core.context.SecurityContextHolder;
import com.luck.common.core.exception.auth.NotLoginException;
import com.luck.common.core.exception.auth.NotPermissionException;
import com.luck.common.core.exception.auth.NotRoleException;
import com.luck.common.core.utils.SpringUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.common.security.annotation.Logical;
import com.luck.common.security.annotation.RequiresLogin;
import com.luck.common.security.annotation.RequiresPermissions;
import com.luck.common.security.annotation.RequiresRoles;
import com.luck.common.security.service.TokenService;
import com.luck.common.security.utils.SecurityUtils;
import com.luck.system.model.LoginUser;
import org.springframework.util.PatternMatchUtils;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* 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<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));
}
}

View File

@ -0,0 +1,167 @@
package com.luck.common.security.auth;
import com.luck.common.security.annotation.RequiresPermissions;
import com.luck.common.security.annotation.RequiresRoles;
import com.luck.system.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);
}
/**
* , truefalse
*
* @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);
}
/**
* , truefalse
*
* @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);
}
}

View File

@ -0,0 +1,22 @@
package com.luck.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());
}
}

View File

@ -0,0 +1,33 @@
package com.luck.common.security.config;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.luck.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();
}
}

View File

@ -0,0 +1,20 @@
package com.luck.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();
}
}

View File

@ -0,0 +1,54 @@
package com.luck.common.security.feign;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import com.luck.common.core.constant.SecurityConstants;
import com.luck.common.core.utils.ServletUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.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<String, String> 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());
}
}
}

View File

@ -0,0 +1,159 @@
package com.luck.common.security.handler;
import com.luck.common.core.constant.HttpStatus;
import com.luck.common.core.exception.DemoModeException;
import com.luck.common.core.exception.InnerAuthException;
import com.luck.common.core.exception.ServiceException;
import com.luck.common.core.exception.auth.NotPermissionException;
import com.luck.common.core.exception.auth.NotRoleException;
import com.luck.common.core.utils.StringUtils;
import com.luck.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("演示模式,不允许操作");
}
}

View File

@ -0,0 +1,55 @@
package com.luck.common.security.interceptor;
import com.luck.common.core.constant.SecurityConstants;
import com.luck.common.core.context.SecurityContextHolder;
import com.luck.common.core.utils.ServletUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.common.security.auth.AuthUtil;
import com.luck.common.security.utils.SecurityUtils;
import com.luck.system.model.LoginUser;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 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();
}
}

View File

@ -0,0 +1,170 @@
package com.luck.common.security.service;
import com.luck.common.core.constant.CacheConstants;
import com.luck.common.core.constant.SecurityConstants;
import com.luck.common.core.utils.JwtUtils;
import com.luck.common.core.utils.ServletUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.common.core.utils.ip.IpUtils;
import com.luck.common.core.utils.uuid.IdUtils;
import com.luck.common.redis.service.RedisService;
import com.luck.common.security.utils.SecurityUtils;
import com.luck.system.model.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* token
*
* @author ruoyi
*/
@Component
public class TokenService
{
@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<String, Object> 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<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, userName);
// 接口返回信息
Map<String, Object> rspMap = new HashMap<String, Object>();
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)
{
}
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;
}
}

View File

@ -0,0 +1,76 @@
package com.luck.common.security.utils;
import com.alibaba.fastjson2.JSONArray;
import com.luck.common.core.constant.CacheConstants;
import com.luck.common.core.utils.SpringUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.common.redis.service.RedisService;
import com.luck.system.domain.SysDictData;
import java.util.Collection;
import java.util.List;
/**
*
*
* @author ruoyi
*/
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(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;
}
}

View File

@ -0,0 +1,118 @@
package com.luck.common.security.utils;
import com.luck.common.core.constant.SecurityConstants;
import com.luck.common.core.constant.TokenConstants;
import com.luck.common.core.context.SecurityContextHolder;
import com.luck.common.core.utils.ServletUtils;
import com.luck.common.core.utils.StringUtils;
import com.luck.system.model.LoginUser;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.servlet.http.HttpServletRequest;
/**
*
*
* @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());
}
/**
* requesttoken
*/
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);
}
}

View File

@ -0,0 +1,5 @@
com.luck.common.security.config.WebMvcConfig
com.luck.common.security.service.TokenService
com.luck.common.security.aspect.PreAuthorizeAspect
com.luck.common.security.aspect.InnerAuthAspect
com.luck.common.security.handler.GlobalExceptionHandler