• AuthorityFilter



    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONArray;
    import com.fasterxml.jackson.annotation.JsonInclude.Include;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.google.common.collect.Lists;
    import com.huawei.it.gts.ls.cache.CacheManagerHelper;
    import com.huawei.it.gts.ls.common.domain.LSResponse;
    import com.huawei.it.gts.ls.constants.GlobalConstants;
    import com.huawei.it.gts.ls.constants.GlobalErrorCode;
    import com.huawei.it.gts.ls.security.autoconfig.SdkConfig;
    import com.huawei.it.gts.ls.security.drools.DataScopeRuleEngine;
    import com.huawei.it.gts.ls.security.drools.DataScopeRuleEngineImpl;
    import com.huawei.it.gts.ls.session.Authority;
    import com.huawei.it.gts.ls.session.Realm;
    import com.huawei.it.gts.ls.session.Role;
    import com.huawei.it.gts.ls.session.SessionManagerHolder;
    import com.huawei.it.gts.ls.session.SessionUserPrincipal;
    import lombok.Data;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;

    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.nio.charset.Charset;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Locale;
    import java.util.Map;
    import java.util.Set;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;


    @Data
    @Component
    @ConditionalOnProperty(prefix = "security.config.authority.filter", name = "enabled", havingValue = "true")
    public class AuthorityFilter implements GlobalFilter, Ordered {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthorityFilter.class);
    private final String prefix = "[/\w]+/v[0-9]+[\.0-9]+";// URL验证正则表达式前缀部分,如:/api_gateway/uams_msa/v0.1
    private final String replaceRegex = "\{\w+\}";// 权限资源URL正则匹配模式,如:将"{id}",替换掉java正则表达式
    private final String replacement = "[\\w-%.]+";// java正则表达式

    private final String WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol"; //5gstart协议头

    @Autowired
    private SdkConfig sdkConfig;

    /**
    * @param exchange
    * @param chain
    * @return
    */
    @SuppressWarnings("unchecked")
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    LOGGER.debug("------------Spring Cloud Gateway AuthorityFilter init()----------");
    ServerHttpRequest request = exchange.getRequest();
    ServerHttpResponse response = exchange.getResponse();
    String uri = request.getPath().toString();
    LOGGER.info("Request URI:{}", uri);

    String requestMethod = request.getMethod().toString();
    HttpHeaders headers = request.getHeaders();
    String sessionId = headers.getFirst(GlobalConstants.SESSION_ID);
    if (StringUtils.isBlank(sessionId)){// sessionId为空说明是访客角色,非访客角色不可能为空
    LOGGER.info("GuestAuthorityCacheKey: {}", sdkConfig.getGuestAuthorityCacheKey());
    List<String> guestAuthorityList;
    try {
    if (CacheManagerHelper.getCacheManager().hasKey(sdkConfig.getGuestAuthorityCacheKey())) {
    // 查询缓存中guest角色对应的权限url
    guestAuthorityList = (List<String>) CacheManagerHelper.getCacheManager().get(sdkConfig.getGuestAuthorityCacheKey());
    //如果缓存中的白名单数据为空,则从配置文件中获取
    if (CollectionUtils.isEmpty(guestAuthorityList)){
    LOGGER.info("从缓存中获取的白名单资源为空{},开始从配置文件中获取!",guestAuthorityList);
    guestAuthorityList = getGuestUrlFromConfig();
    }
    }else {
    LOGGER.info("从缓存中获取的白名单资源key不存在{},开始从配置文件中获取!",sdkConfig.getGuestAuthorityCacheKey());
    guestAuthorityList = getGuestUrlFromConfig();
    }
    }catch (Exception e){
    LOGGER.error("获取白名单资源异常,开始从配置文件中获取:",e);
    //当读取缓存出现异常时,从配置文件中获取白名单接口资源
    guestAuthorityList = getGuestUrlFromConfig();
    }
    if (CollectionUtils.isNotEmpty(guestAuthorityList)) {
    for (String guestUrl : guestAuthorityList) {
    String[] splitUrls = guestUrl.split(":");// 将http
    // request请求方法和URL分割;如:GET:/apps/v0.1/action
    String newUrl = splitUrls[1].replaceAll(replaceRegex, replacement) + "$";
    if (!StringUtils.startsWith(newUrl, "/")) {
    newUrl = "/" + newUrl;
    }
    String regex = prefix + newUrl;// 将正则前缀部分+后续部分拼接成完整的URI校验正则表达式
    Matcher matcher = Pattern.compile(regex).matcher(uri);
    if (matcher.matches()) {
    if (StringUtils.equalsIgnoreCase(splitUrls[0],requestMethod)){
    return chain.filter(exchange.mutate().request(exchange.getRequest()).build());
    }
    }
    }
    }
    }

    String tenantId = headers.getFirst(GlobalConstants.TENANT_ID);
    // 尝试获取WebSocket请求头Protocol
    String wsheaders = headers.getFirst(WEBSOCKET_PROTOCOL);
    if (wsheaders != null) {
    tenantId = wsheaders.split(",")[0];
    }
    // 判断租户ID
    if (StringUtils.isBlank(tenantId)) {
    LOGGER.error("-----请求参数[tenantId]缺失!");
    return outResponse(response, GlobalErrorCode.E1001_002);
    }

    SessionUserPrincipal principal = (SessionUserPrincipal) SessionManagerHolder.getPrincipal(sessionId);
    String uId = principal.getUid();
    // 判断Session是否过期
    if (StringUtils.isBlank(uId)) {
    LOGGER.error("-----用户会话已过期!");
    return outResponse(response, GlobalErrorCode.E1001_012);
    }
    Map<String, Object> objectMap = new HashMap<>();
    objectMap.put("userIdCache", uId);
    LOGGER.info("userIdCache = {}", uId);

    String principalTenantId = principal.getTenantId();
    if (!StringUtils.equalsIgnoreCase(tenantId,principalTenantId)) {
    LOGGER.error("-----用户[{}]关联的租户信息异常,请求头中租户信息[{}],缓存中租户信息[{}]!",
    principal.getUid(),tenantId,principalTenantId);
    return outResponse(response, GlobalErrorCode.E1001_002);
    }

    final String realmId = headers.getFirst(GlobalConstants.REALM_ID);
    MultiValueMap<String, String> queryParams = request.getQueryParams();
    Set<String> paramsSet = queryParams.keySet();
    for (String param : paramsSet) {
    objectMap.put(param + "Param", request.getQueryParams().getFirst(param));
    }
    String contentType = headers.getFirst("Content-Type");
    // 只考虑contentType是json格式的
    if (contentType!=null && contentType.contains("application/json")) {
    Object cachedRequestBodyObject = exchange.getAttribute("cachedRequestBodyObject");
    if (null != cachedRequestBodyObject) {
    String body = cachedRequestBodyObject.toString();
    if (body.startsWith("[")) {
    List<Map<String, Object>> mapList = (List<Map<String, Object>>) cachedRequestBodyObject;
    String jsonStr = JSON.toJSONString(mapList);
    JSONArray jsonArray = JSONArray.parseArray(jsonStr);
    objectMap.put("jsonArray", jsonArray);
    } else {
    Map<String, Object> requestBodylist = (Map<String, Object>) cachedRequestBodyObject;
    objectMap.putAll(requestBodylist);
    }
    }
    }
    Map<String,Object> respMap = verifyURI(tenantId, realmId,sessionId, uri, requestMethod, objectMap);
    // boolean matches = verifyURI(tenantId, realmId,sessionId, uri, requestMethod, objectMap);
    boolean matches = (boolean) respMap.get("flag");
    if (!matches) {
    LOGGER.info("-----来自租户[{}]下的用户[{}]没有分配相关权限!", tenantId, principal.getUid());
    // exchange.mutate().response(getRespDec(response,
    // GlobalErrorCode.E1001_001)).build();
    return outResponse(response, (String) respMap.get("status"));
    } else {
    LOGGER.info("租户信息为[{}],请求的uri是[{}],请求方法是[{}],请求的参数是[{}]。", tenantId, uri, requestMethod, objectMap);
    // exchange.mutate().request(exchange.getRequest()).build();
    return chain.filter(exchange);
    }
    }


    private synchronized Map<String,Object> verifyURI(String tenantId, String realmId,String sessionId,
    String requestURL, String method, Map<String, Object> objectMap) {
    Map<String,Object> respMap = new HashMap<>(4);
    Map<String, Object> resultMap = new HashMap<>(objectMap);
    String uri = requestURL;
    List<Role> roles = Lists.newArrayList();
    List<Authority> resources = Lists.newArrayList();
    if (StringUtils.isNotBlank(realmId)){
    List<Realm> realms = SessionManagerHolder.getRealm(sessionId);// 获取领域列表
    Realm realm = realms.stream().filter(s->s.getRealmId().equals(realmId)).findAny().orElse(null);
    if (null != realm){
    roles = realm.getRoles();
    resources = realm.getAuthorities();
    }else {
    LOGGER.info("-----该领域[{}]下没有分配任何角色-----", realmId);
    }
    }else {
    roles = SessionManagerHolder.getRole(sessionId);
    resources = SessionManagerHolder.getAuthority(sessionId);
    }
    // 该用户没有关联角色,则无权访问
    if (CollectionUtils.isEmpty(roles)) {
    LOGGER.info("-----错误码[{}],该租户[{}]没有分配相关角色权限!-----", GlobalErrorCode.E1001_003, tenantId);
    respMap.put("status",GlobalErrorCode.E1001_003);
    respMap.put("flag",false);
    return respMap;
    }
    List<String> roleIds = roles.stream().map(Role::getRoleId).collect(Collectors.toList());
    resultMap.put("roleIdsCache", roleIds);

    if (CollectionUtils.isEmpty(resources)) {
    LOGGER.info("-----错误码[{}],该租户[{}]没有分配相关权限资源!-----", GlobalErrorCode.E1001_004, tenantId);
    respMap.put("status",GlobalErrorCode.E1001_004);
    respMap.put("flag",false);
    return respMap;
    // return false;
    }

    // 将所有的权限资源URL取出
    List<String> urls = resources.stream().map(Authority::getResourceUrl).collect(Collectors.toList());
    for (String url : urls) {
    if (StringUtils.isBlank(url) || !StringUtils.startsWith(url, method.toUpperCase(Locale.US))) {
    // URL为空或者不以request method为前缀的URL都跳过,排除非API接口权限资源
    continue;
    }
    String[] splitUrls = url.split(":");// 将http
    // request请求方法和URL分割;如:GET:/apps/v0.1/action
    // 将权限资源URL进行替换成java正则的后缀部分
    String suffix = splitUrls[1].replaceAll(replaceRegex, replacement) + "$";
    if (!StringUtils.startsWith(suffix, "/")) {
    suffix = "/" + suffix;
    }
    String regex = prefix + suffix;// 将正则前缀部分+后续部分拼接成完整的URI校验正则表达式
    Matcher matcher = Pattern.compile(regex).matcher(uri);
    if (matcher.matches()) {
    LOGGER.debug("请求URL:{},匹配到正则表达式:{}", uri, regex);
    Map<String, Object> map = getUrlParam(url, uri);
    resultMap.putAll(map);
    List<Authority> authority = resources.stream()
    .filter(o -> (StringUtils.isNotBlank(o.getResourceUrl()) && o.getResourceUrl().equals(url)))
    .collect(Collectors.toList());
    List<String> scopeList = authority.stream().filter(o -> StringUtils.isNotBlank(o.getDataScope()))
    .collect(Collectors.toList()).stream().map(Authority::getDataScope)
    .collect(Collectors.toList());
    LOGGER.info("---本次校验的参数是[{}]---", resultMap);
    boolean flag = droolsHandle(scopeList, resultMap);
    // 没有匹配到数据范围,判定当前请求参数范围没有权限
    if (!flag) {
    LOGGER.info("-----错误码[{}],本次请求[{}]没有对应数据范围权限!-----", GlobalErrorCode.E1001_005, requestURL);
    respMap.put("status",GlobalErrorCode.E1001_005);
    respMap.put("flag",false);
    return respMap;
    // return false;
    }
    respMap.put("flag",true);
    return respMap;
    // return true;
    }
    }
    LOGGER.info("-----本次请求[{}]在租户[{}]下没有匹配相关权限资源!-----", requestURL, tenantId);
    respMap.put("flag",false);
    respMap.put("status",GlobalErrorCode.E1001_004);
    return respMap;
    // return false;
    }

    private Map<String, Object> getUrlParam(String url, String uri) {
    Map<String, Object> map = new HashMap<>();
    String[] splitResourceUrls = url.split("/");
    String[] splitUris = uri.split("/");
    for (int i = 0; i < splitResourceUrls.length; i++) {
    Matcher m1 = Pattern.compile(replaceRegex).matcher(splitResourceUrls[i]);
    if (m1.matches()) {
    String afterStr = splitResourceUrls[i - 1];
    for (int j = 0; j < splitUris.length; j++) {
    if (afterStr.equals(splitUris[j])) {
    String key = m1.group().substring(1, m1.group().length() - 1) + "Path";
    map.put(key, splitUris[j + 1]);
    }
    }
    }
    }
    return map;
    }


    private List<Role> getRolesByTenant(String realmId,String sessionId) {
    List<Role> roles = null;
    if (StringUtils.isBlank(realmId)) {// 前端没有上报领域id
    roles = SessionManagerHolder.getRole(sessionId);// 获取租户下默认领域的所有角色列表
    } else {
    List<Realm> realms = SessionManagerHolder.getRealm(sessionId);// 获取领域列表
    roles = getRolesByRealm(realms, realmId);// 获取相应领域下的角色列表
    }
    return roles;
    }


    * @Desc 获取领域下相应角色列表. <br/>

    private List<Role> getRolesByRealm(List<Realm> realms, String realmId) {

    for (Realm realm : realms) {
    if (realmId.equals(realm.getRealmId())) {
    return realm.getRoles();
    }
    }
    LOGGER.info("-----该领域[{}]下没有分配任何角色-----", realmId);
    return Lists.newArrayList();
    }

    private void outResponse(HttpServletResponse response, String status) throws IOException {
    LSResponse<Object> lsResponse = new LSResponse<>();
    lsResponse.setFaild(status);
    lsResponse.setStatus(Integer.parseInt(status));
    response.setStatus(401);
    response.setHeader("content-type", "text/html;charset=UTF-8");
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(Include.NON_NULL);
    IOUtils.write(mapper.writeValueAsString(lsResponse), response.getOutputStream(), Charset.defaultCharset());
    }

    private Mono<Void> outResponse(ServerHttpResponse response, String status) {
    try {
    LSResponse<Object> lsResponse = new LSResponse<>();
    lsResponse.setFaild(status);
    lsResponse.setStatus(Integer.parseInt(status));
    // 对象转JSON转byte数组
    ObjectMapper objectMapper = new ObjectMapper();
    byte[] data = objectMapper.writeValueAsBytes(lsResponse);
    // 写入响应头
    DataBuffer buffer = response.bufferFactory().wrap(data);
    response.setStatusCode(HttpStatus.UNAUTHORIZED);
    response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
    return response.writeWith(Mono.just(buffer));
    } catch (JsonProcessingException e) {
    LOGGER.error("lsResponse.parse.jsonException");
    }
    return null;
    }

    /**
    * 执行drools引擎,匹配参数
    *
    * @param scopeList
    * @param parameterMap
    */
    private Boolean droolsHandle(List<String> scopeList, Map<String, Object> parameterMap) {
    DataScopeRuleEngine ruleEngine = new DataScopeRuleEngineImpl();
    if (CollectionUtils.isEmpty(scopeList)) {
    return true;
    }
    boolean flag = false;
    for (String scope : scopeList) {
    ruleEngine.initEngine(scope);
    boolean f = ruleEngine.executeRuleEngine(parameterMap);
    flag = f || flag;
    }
    return flag;
    }

    /**
    * 从配置文件中获取白名单资源
    * @return
    */
    private List<String> getGuestUrlFromConfig(){
    String guestUrl = sdkConfig.getGuestUrl();
    String[] strings = guestUrl.split(",");
    return Arrays.asList(strings);
    }

    @Override
    public int getOrder() {
    return 150;
    }

    }
  • 相关阅读:
    设计模式之抽象工厂模式
    MQ任意延时消息(三)基于服务端实现
    MQ任意延时消息(二)基于客户端实现
    MQ任意延时消息(一)实现原理概述
    sqlyog报错2058
    base标签的作用
    相对路径和绝对路径的解释
    自定义Tomcat部署目录
    常用正则表达式
    接口的结构定义
  • 原文地址:https://www.cnblogs.com/lhh-boke/p/14203472.html
Copyright © 2020-2023  润新知