Pre Merge pull request !291 from 天天向上/gateway-auth

pull/291/MERGE
天天向上 1 year ago committed by Gitee
commit 6af48adade
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F

@ -46,4 +46,19 @@ public class SecurityConstants
*
*/
public static final String ROLE_PERMISSION = "role_permission";
/**
* redishash
*/
public static final String PATH_PERMISSION_MAP = "path_permission_map";
/**
*
*/
public static final String ROLE_PREFIX = "ROLE_";
/**
*
*/
public static final String ROLE_ANON = "ROLE_ANON";
}

@ -1,16 +1,18 @@
package com.ruoyi.common.security.aspect;
import java.lang.reflect.Method;
import com.ruoyi.common.security.annotation.RequiresLogin;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.annotation.RequiresRoles;
import com.ruoyi.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.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import com.ruoyi.common.security.annotation.RequiresLogin;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.annotation.RequiresRoles;
import com.ruoyi.common.security.auth.AuthUtil;
import java.lang.reflect.Method;
/**
* Spring Aop
@ -19,6 +21,7 @@ import com.ruoyi.common.security.auth.AuthUtil;
*/
@Aspect
@Component
@ConditionalOnProperty(prefix = "security.annotation", name = "enabled", havingValue = "true", matchIfMissing = true)
public class PreAuthorizeAspect
{
/**

@ -0,0 +1,110 @@
package com.ruoyi.common.security.config;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.annotation.RequiresPermissions;
import com.ruoyi.common.security.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.annotation.PostConstruct;
import java.util.*;
/**
* =====================================使=======================================
*
*
* 使
* 1security.annotation.enabled: false
* 2pathPrefix: : /auth
* 3security.gateway.enabled: true
* <p>
* redis便
*/
@ConditionalOnProperty(prefix = "security.annotation", name = "enabled", havingValue = "false")
public class PathPermissionMappingConfig
{
/**
* predicatesPath /system
*/
@Value("${pathPrefix}")
private String pathPrefix;
@PostConstruct
public PathPermissionMappingConfig execute()
{
RedisService redisService = SpringUtils.getBean(RedisService.class);
RequestMappingHandlerMapping bean = SpringUtils.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethods = bean.getHandlerMethods();
/**
* -> /user/list_GET->system:user:list
*/
Map<String, String> pathPermsMap = new TreeMap<>();
handlerMethods.forEach((k, v) ->
{
RequiresRoles requiresRoles = v.getMethodAnnotation(RequiresRoles.class);
RequiresPermissions requiresPermissions = v.getMethodAnnotation(RequiresPermissions.class);
Set<RequestMethod> methods = k.getMethodsCondition().getMethods();
Set<String> patternValues = k.getPatternValues();
/**
* @RequestMapping
*/
if (methods.isEmpty())
{
methods = new HashSet<>();
methods.addAll(Arrays.asList(RequestMethod.GET, RequestMethod.POST));
}
if (requiresPermissions == null && requiresRoles == null)
{
addPathPermsMap(SecurityConstants.ROLE_ANON, pathPermsMap, methods, patternValues);
}
if (requiresPermissions != null)
{
for (String perms : requiresPermissions.value())
{
addPathPermsMap(perms, pathPermsMap, methods, patternValues);
}
}
if (requiresRoles != null)
{
for (String role : requiresRoles.value())
{
addPathPermsMap(SecurityConstants.ROLE_PREFIX + role, pathPermsMap, methods, patternValues);
}
}
});
System.out.println("pathPermsMap = " + pathPermsMap);
redisService.setCacheMap(SecurityConstants.PATH_PERMISSION_MAP, pathPermsMap);
return this;
}
/**
* pathperms
*
* @param perms
* @param pathPermsMap
* @param methods
* @param patternValues
*/
private void addPathPermsMap(String perms, Map<String, String> pathPermsMap, Set<RequestMethod> methods, Set<String> patternValues)
{
for (RequestMethod method : methods)
{
for (String patternValue : patternValues)
{
String key = pathPrefix + patternValue + "_" + method.name();
pathPermsMap.put(key, perms);
}
}
}
}

@ -3,3 +3,4 @@ com.ruoyi.common.security.service.TokenService
com.ruoyi.common.security.aspect.PreAuthorizeAspect
com.ruoyi.common.security.aspect.InnerAuthAspect
com.ruoyi.common.security.handler.GlobalExceptionHandler
com.ruoyi.common.security.config.PathPermissionMappingConfig

@ -1,14 +1,6 @@
package com.ruoyi.gateway.filter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.constant.SecurityConstants;
@ -19,8 +11,24 @@ import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.gateway.config.properties.IgnoreWhiteProperties;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
*
*
@ -35,9 +43,13 @@ public class AuthFilter implements GlobalFilter, Ordered
@Autowired
private IgnoreWhiteProperties ignoreWhite;
@Autowired
@Resource
private RedisService redisService;
@Value("${security.gateway.enabled:false}")
private boolean gatewayAuth;
private AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
@ -73,16 +85,91 @@ public class AuthFilter implements GlobalFilter, Ordered
{
return unauthorizedResponse(exchange, "令牌验证失败");
}
// 设置用户信息到请求
addHeader(mutate, SecurityConstants.USER_KEY, userkey);
addHeader(mutate, SecurityConstants.DETAILS_USER_ID, userid);
addHeader(mutate, SecurityConstants.DETAILS_USERNAME, username);
// 内部请求来源参数清除
removeHeader(mutate, SecurityConstants.FROM_SOURCE);
// 通过网关鉴权
if (gatewayAuth)
{
// admin不需要鉴权
if (isAdmin(userid))
{
return chain.filter(exchange.mutate().request(mutate.build()).build());
}
// 网关验证权限
String api = url + "_" + request.getMethod().name();
if (!hasPermission(api, userkey))
{
log.warn("无权访问:{}", api);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), "无权访问", HttpStatus.FORBIDDEN);
}
}
return chain.filter(exchange.mutate().request(mutate.build()).build());
}
private boolean isAdmin(String userid)
{
return "1".equals(userid);
}
private boolean hasPermission(String api, String token)
{
// 使用JSONObject接收避免导入依赖
JSONObject loginUser = redisService.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + token);
// 获取登录用户的资源列表
Set<String> permissions = (Set<String>) loginUser.get("permissions");
// 获取登录用的角色列表
Set<String> roles = (Set<String>) loginUser.get("roles");
// 获取系统所有控制器路径与权限对应的map
Map<String, String> pathPermsMap = redisService.getCacheMap(SecurityConstants.PATH_PERMISSION_MAP);
Set<String> matchedPerms = pathPermsMap.entrySet().stream()
.filter(entry -> match(entry.getKey(), api))
.map(entry -> entry.getValue())
.collect(Collectors.toSet());
if (!matchedPerms.isEmpty())
{
// 所有角色权限
Set<String> rolePerms = matchedPerms.stream().filter(item -> item.startsWith("ROLE_")).collect(Collectors.toSet());
// 所有资源权限
matchedPerms.removeAll(rolePerms);
if (!rolePerms.isEmpty())
{
if (rolePerms.contains(SecurityConstants.ROLE_ANON))
{
log.debug("允许访问公共权限:{}{}", api, rolePerms);
return true;
}
rolePerms = rolePerms.stream().map(item -> item.substring(SecurityConstants.ROLE_PREFIX.length())).collect(Collectors.toSet());
// 求交集
rolePerms.retainAll(roles);
if (!rolePerms.isEmpty())
{
log.debug("允许访问角色权限:{} {}", api, rolePerms);
return true;
}
}
// 求交集
matchedPerms.retainAll(permissions);
if (!matchedPerms.isEmpty())
{
log.debug("允许访问资源权限:{}{}", api, matchedPerms);
return true;
}
}
log.info("没有找到匹配的权限:{}, {}", api, matchedPerms);
return false;
}
private boolean match(String pattern, String api)
{
return antPathMatcher.match(pattern, api);
}
private void addHeader(ServerHttpRequest.Builder mutate, String name, Object value)
{
if (value == null)
@ -101,7 +188,7 @@ public class AuthFilter implements GlobalFilter, Ordered
private Mono<Void> unauthorizedResponse(ServerWebExchange exchange, String msg)
{
log.error("[鉴权异常处理]请求路径:{}", exchange.getRequest().getPath());
log.error("[鉴权异常处理]请求路径:{}, {}", exchange.getRequest().getPath(), msg);
return ServletUtils.webFluxResponseWriter(exchange.getResponse(), msg, HttpStatus.UNAUTHORIZED);
}

Loading…
Cancel
Save