美文网首页
springCloud gateway网关拦截修改

springCloud gateway网关拦截修改

作者: 飞鹰雪玉 | 来源:发表于2022-03-25 16:32 被阅读0次

1、springcloud网关底层使用的是响应式+ServerHttpRequest。

2、网关主要作用是鉴权和转发。

3、一般拦截使用的都是实现GlobalFilter,重写它的filter方法。

4、对请求的header、param修改或者添加或者删除时用反射的方式实现。

比如如下代码

package org.springblade.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springblade.core.jwt.JwtUtil;
import org.springblade.core.jwt.props.JwtProperties;
import org.springblade.core.launch.constant.TokenConstant;
import org.springblade.gateway.entity.RespRoleVO;
import org.springblade.gateway.entity.RespUserVO;
import org.springblade.gateway.props.AuthProperties;
import org.springblade.gateway.provider.AuthProvider;
import org.springblade.gateway.provider.RequestProvider;
import org.springblade.gateway.provider.ResponseProvider;
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.core.io.buffer.DataBuffer;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;


/**
 * 鉴权认证
 *
 * @author Base
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class AuthFilter implements GlobalFilter, Ordered {

    private final AuthProperties authProperties;
    private final ObjectMapper objectMapper;
    private final JwtProperties jwtProperties;

    @Value(value = "${avatar.role.platform}")
    private String roleCode;
    @Value("${avatar.role.checkUrl}")
    private String checkUrl;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();


    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //校验 Token 放行
        String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
        String path = exchange.getRequest().getURI().getPath();
        // 判断是否是/oauth/token接口,是的话需要做avatar权限校验,获取用户
        if ("/oauth/avatar/token".equals(path)) {
            String avatarToken = exchange.getRequest().getQueryParams().getFirst("avatarToken");
            // 取到avatarToken
            if (StringUtils.isNotBlank(avatarToken)) {

                // 通过avatarToken获取用户数据
                RespUserVO userVO = avatarAuth(avatarToken);

                ServerHttpResponse resp = exchange.getResponse();
                if (null == userVO || null == userVO.getAccountId()) {
                    return unAuth(resp, "token失效,请重新传输!");
                }
                return avatarAuth(resp, userVO);
                // 以反射的形式将用户名和密码存储到params中
                MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
                Field targetMap = params.getClass().getDeclaredField("targetMap");
                targetMap.setAccessible(true);
                Map<String, String> newMap = new HashMap((Map) targetMap.get(params));
                newMap.put("username", userVO.getAccountId());
                newMap.put("password", md5DigestAsHex(DEFAULT_PASSWORD.getBytes(StandardCharsets.UTF_8))); 
                newMap.remove("avatarToken");
                targetMap.set(params, newMap);
            }
        }
        if (isSkip(path) || isSkip(originalRequestUrl)) {
            return chain.filter(exchange);
        }
        //校验 Token 合法性
        ServerHttpResponse resp = exchange.getResponse();
        String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
        String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
        if (StringUtils.isBlank(headerToken) && StringUtils.isBlank(paramToken)) {
            return unAuth(resp, "缺失令牌,鉴权失败");
        }
        String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
        String token = JwtUtil.getToken(auth);
        Claims claims = JwtUtil.parseJWT(token);
        if (token == null || claims == null) {
            return unAuth(resp, "请求未授权");
        }
        // 鉴权
        String avatarToken = exchange.getRequest().getHeaders().getFirst(TokenConstant.AVATAR_TOKEN);
        RespUserVO userVO = avatarAuth(avatarToken);
        // 角色判断
        if (!judgementRole(userVO)) {
            return unAuth(resp, "鉴权失败!请重新申请授权");
        }
        //判断 Token 状态
        if (jwtProperties.getState()) {
            String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID));
            String userId = String.valueOf(claims.get(TokenConstant.USER_ID));
            String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
            if (!token.equalsIgnoreCase(accessToken)) {
                return unAuth(resp, "令牌已失效");
            }
        }
        return chain.filter(exchange);
    }

    /**
     * 鉴权认证 通过avatarToken返回用户
     *
     * @param token 第三方JWTtoken
     * @return boolean
     */
    private RespUserVO avatarAuth(String token) {
        String url = checkUrl + token;
        RestTemplate template = new RestTemplate();
        ResponseEntity<JSONObject> entity = template.exchange(url, HttpMethod.GET, null, JSONObject.class);

        if (null != entity.getBody()) {
            JSONObject object = entity.getBody();
            HashMap<String, Object> mapUser = (HashMap<String, Object>) object.get("result");
            RespUserVO respUserVO = new RespUserVO();
            respUserVO.setAccountId((String) mapUser.get("accountId"));
            respUserVO.setRoleVOList((List<RespRoleVO>) mapUser.get("roleVOList"));
            return (null != respUserVO.getAccountId()) ? respUserVO : null;
        }
        return null;
    }

    /**
     * 判断当前用户角色是否有平台权限
     *
     * @param userVO 用户
     * @return boolean
     */
    private Boolean judgementRole(RespUserVO userVO) {
        if (null == userVO) {
            return false;
        }
        List<RespRoleVO> voList = userVO.getRoleVOList();
        if (null != voList) {
            for (RespRoleVO role : voList) {
                if (roleCode.equals(role.getRoleCode())) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean isSkip(String path) {
        return AuthProvider.getDefaultSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path))
            || authProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
    }

    private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
        resp.setStatusCode(HttpStatus.UNAUTHORIZED);
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String result = "";
        try {
            result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg));
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }

    /**
     * 返回avatar用户的用户名
     *
     * @param resp       响应
     * @param respUserVO 用户
     */
    private Mono<Void> avatarAuth(ServerHttpResponse resp, RespUserVO respUserVO) {
        resp.setStatusCode(HttpStatus.OK);
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String result = "";
        try {
            result = objectMapper.writeValueAsString(ResponseProvider.success(respUserVO.getAccountId()));
        } catch (JsonProcessingException e) {
            log.error(e.getMessage(), e);
        }
        DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Flux.just(buffer));
    }


    @Override
    public int getOrder() {
        return -100;
    }
}

获取header和param

获取param:  exchange.getRequest().getQueryParams().getFirst("avatarToken");
获取header:  exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);

修改param

                // 以反射的形式将用户名和密码存储到params中
                MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
                Field targetMap = params.getClass().getDeclaredField("targetMap");
                targetMap.setAccessible(true);
                Map<String, String> newMap = new HashMap((Map) targetMap.get(params));
                newMap.put("username", userVO.getAccountId());
                newMap.put("password", md5DigestAsHex(DEFAULT_PASSWORD.getBytes(StandardCharsets.UTF_8))); 
                newMap.remove("avatarToken");
                targetMap.set(params, newMap);

5、网关的返回参数是Mono<Void>类型。一般返回的都是鉴权失败等msg。

相关文章

网友评论

      本文标题:springCloud gateway网关拦截修改

      本文链接:https://www.haomeiwen.com/subject/urrdjrtx.html