GatewaySecurityFilter.java 4.3 KB
Newer Older
1
package com.youlai.gateway.filter;
2 3

import cn.hutool.core.util.StrUtil;
4 5
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
6
import com.nimbusds.jose.JWSObject;
.  
有来技术 已提交
7
import com.youlai.common.constant.SecurityConstants;
8
import com.youlai.common.result.ResultCode;
H
haoxr 已提交
9
import com.youlai.gateway.util.ResponseUtils;
10
import lombok.RequiredArgsConstructor;
11 12
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
H
haoxr 已提交
13
import org.apache.logging.log4j.util.Strings;
H
haoxr 已提交
14
import org.springframework.beans.factory.annotation.Value;
15 16 17
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
18
import org.springframework.data.redis.core.RedisTemplate;
19
import org.springframework.http.server.reactive.ServerHttpRequest;
20
import org.springframework.http.server.reactive.ServerHttpResponse;
21 22 23 24
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

25 26
import java.net.URLEncoder;

27
/**
28
 * 安全拦截全局过滤器,非网关鉴权的逻辑
29
 * <p>
30
 * 在ResourceServerManager#check鉴权善后一些无关紧要的事宜(线上请求拦截、黑名单拦截)
31
 *
有来技术 已提交
32
 * @author <a href="mailto:xianrui0365@163.com">haoxr</a>
33
 * @date 2022/2/15
34 35 36
 */
@Component
@Slf4j
37
@RequiredArgsConstructor
38
public class GatewaySecurityFilter implements GlobalFilter, Ordered {
H
haoxr 已提交
39

40 41 42 43
    private final RedisTemplate redisTemplate;

    @Value("${spring.profiles.active}")
    private String env;
H
haoxr 已提交
44

45 46 47
    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
H
haoxr 已提交
48 49 50 51

        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

52 53 54 55 56 57 58
        // 线上环境请求拦截处理,实际请自行删除下面代码块
        {
            String requestPath = request.getPath().pathWithinApplication().value();
            if (env.equals("prod")) {
                String methodValue = request.getMethodValue();
                if (SecurityConstants.PROD_FORBID_METHODS.contains(methodValue)) { // PUT和DELETE方法禁止
                    // 是否需要放行的请求路径
59
                    boolean isPermitPath = SecurityConstants.PROD_PERMIT_PATHS.stream().anyMatch(permitPath -> permitPath.contains(requestPath));
60 61 62 63 64
                    if (!isPermitPath) {
                        return ResponseUtils.writeErrorInfo(response, ResultCode.FORBIDDEN_OPERATION);
                    }
                } else {
                    // 是否禁止放行的请求路径
65
                    boolean isForbidPath = SecurityConstants.PROD_FORBID_PATHS.stream().anyMatch(permitPath -> permitPath.contains(requestPath));
66 67 68
                    if (isForbidPath) {
                        return ResponseUtils.writeErrorInfo(response, ResultCode.FORBIDDEN_OPERATION);
                    }
69 70
                }
            }
H
haoxr 已提交
71 72
        }

73
        // 非JWT放行不做后续解析处理
.  
有来技术 已提交
74
        String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY);
75
        if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) {
76 77
            return chain.filter(exchange);
        }
H
haoxr 已提交
78

有来技术 已提交
79
        // 解析JWT获取jti,以jti为key判断redis的黑名单列表是否存在,存在则拦截访问
.  
有来技术 已提交
80
        token = StrUtil.replaceIgnoreCase(token, SecurityConstants.JWT_PREFIX, Strings.EMPTY);
81
        String payload = StrUtil.toString(JWSObject.parse(token).getPayload());
82
        JSONObject jsonObject = JSONUtil.parseObj(payload);
.  
有来技术 已提交
83 84
        String jti = jsonObject.getStr(SecurityConstants.JWT_JTI);
        Boolean isBlack = redisTemplate.hasKey(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti);
85
        if (isBlack) {
H
haoxr 已提交
86
            return ResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_ACCESS_FORBIDDEN);
87 88
        }

89
        // 存在token且不在黑名单中,request写入JWT的载体信息传递给微服务
H
haoxr 已提交
90
        request = exchange.getRequest().mutate()
.  
有来技术 已提交
91
                .header(SecurityConstants.JWT_PAYLOAD_KEY, URLEncoder.encode(payload, "UTF-8"))
H
haoxr 已提交
92
                .build();
93 94 95 96 97 98 99 100 101
        exchange = exchange.mutate().request(request).build();
        return chain.filter(exchange);
    }

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