场景描述

使用jeecg搭建SpringCloud微服务系统模块,各个系统模块单独创建了拦截器进行权限校验。结果发现跨微服务调用存在鉴权失败问题。不能正常跨微服务调用。

原因描述

单个微服务鉴权拦截器。

package org.jeecg.modules.taxation.inerceptor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 配置请求拦截器
 */
@Configuration
public class TaxAppInterceptorConfig implements WebMvcConfigurer {

    @Bean
    public TaxAppInterceptor appInterceptor() {
        return new TaxAppInterceptor();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        // 这里的拦截器是new出来的,在Spring框架中可以交给IOC进行依赖注入,直接使用@Autowired注入
        registry.addInterceptor(appInterceptor())
                //授权接口
                .addPathPatterns("/api/tax/**")

                //排除接口
                //用户
                .excludePathPatterns("/api/tax/member/**")
                //申报
                .excludePathPatterns("/api/tax/declare/**")
                //if


                .excludePathPatterns("/wx/callback/alipayCallback");


    }
}

小程序等用户请求,拦截器

/**
 *  小程序等用户请求,拦截器
 */
@Component
public class TaxAppInterceptor implements HandlerInterceptor {
    ... 省略代码
}

解决方案

方案1:更改FeignConfig,添加token传递,jeecg原生jar包,这个修改不了。
方案2:替换FeignConfig,添加token传递, 类冲突,无法替换, allow-circular-references 无效。
方案3:抽取每个服务的拦截器到网关统一验证。如果放行shiroConfig,用网关鉴权,可能存在单jar包,直接访问问题,
所以①不能放行shiroConfig,②更改shiro拦截代码,③添加网关统一校验。
方案4:服务端的filter区分客户端是feign还是前端浏览器,是feign就不鉴权了
方案5:feign能设置全局默认传的参数,可以加一些参数让服务端识别出是feign。

目前为了时间,暂时使用的方案2,构架层面应该使用方案3,统一网关鉴权。

配置FeignClient的configuration解决跨微服务token共享鉴权问题

在这里插入图片描述

调用端声明

package org.jeecg.modules.ccb.api;
import org.jeecg.common.constant.ServiceNameConstants;
import org.jeecg.modules.ccb.api.fallback.CcbHelloFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(value = ServiceNameConstants.SERVICE_TAXATION,configuration = FeignHelloConfiguration.class,fallbackFactory = CcbHelloFallback.class)
public interface TaxationApi {

    /**
     * taxation hello 微服务接口
     * @param
     * @return
     */
    @GetMapping(value = "/api/tax/hello")
    String callHello();
}

Feign配置类

重点是:

String token1 = request.getHeader(“token”);
requestTemplate.header(“token”, new String[]{token1});

package org.jeecg.modules.ccb.api;


import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import feign.Contract;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.config.mqtoken.UserTokenContext;
import org.jeecg.common.util.PathMatcherUtil;
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;

public class FeignHelloConfiguration {


    private static final Logger log = LoggerFactory.getLogger(FeignHelloConfiguration.class);
    public static final String X_ACCESS_TOKEN = "X-Access-Token";
    public static final String X_SIGN = "X-Sign";
    public static final String X_TIMESTAMP = "X-TIMESTAMP";
    public static final String TENANT_ID = "tenant-id";

    public FeignHelloConfiguration() {
    }

    @Bean
    public RequestInterceptor requestInterceptor() {
        return (requestTemplate) -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String token;
            String queryLine;
            String signUrls;
            if (null != attributes) {
                HttpServletRequest request = attributes.getRequest();



                //token传递
                String token1 = request.getHeader("token");
                requestTemplate.header("token", new String[]{token1});



                log.debug("Feign request: {}", request.getRequestURI());
                token = request.getHeader("X-Access-Token");
                if (token == null || "".equals(token)) {
                    token = request.getParameter("token");
                }

                log.info("Feign Login Request token: {}", token);
                requestTemplate.header("X-Access-Token", new String[]{token});
                queryLine = request.getHeader("tenant-id");
                if (queryLine == null || "".equals(queryLine)) {
                    queryLine = request.getParameter("tenant-id");
                }

                log.info("Feign Login Request tenantId: {}", queryLine);
                requestTemplate.header("tenant-id", new String[]{queryLine});
            } else {
                signUrls = UserTokenContext.getToken();
                log.info("Feign no Login token: {}", signUrls);
                requestTemplate.header("X-Access-Token", new String[]{signUrls});
                token = TenantContext.getTenant();
                log.info("Feign no Login tenantId: {}", token);
                requestTemplate.header("tenant-id", new String[]{token});
            }



        };
    }

    @Bean
    feign.Logger.Level feignLoggerLevel() {
        return feign.Logger.Level.FULL;
    }

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        return new SpringFormEncoder(new SpringEncoder(messageConverters));
    }

    @Bean
    public Encoder feignEncoder() {
        return new SpringEncoder(this.feignHttpMessageConverter());
    }

    @Bean
    public Decoder feignDecoder() {
        return new SpringDecoder(this.feignHttpMessageConverter());
    }

    private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
        HttpMessageConverters httpMessageConverters = new HttpMessageConverters(new HttpMessageConverter[]{this.getFastJsonConverter()});
        return () -> {
            return httpMessageConverters;
        };
    }

    private FastJsonHttpMessageConverter getFastJsonConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        List<MediaType> supportedMediaTypes = new ArrayList();
        MediaType mediaTypeJson = MediaType.valueOf("application/json");
        supportedMediaTypes.add(mediaTypeJson);
        converter.setSupportedMediaTypes(supportedMediaTypes);
        FastJsonConfig config = new FastJsonConfig();
        config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
        config.setSerializerFeatures(new SerializerFeature[]{SerializerFeature.DisableCircularReferenceDetect});
        converter.setFastJsonConfig(config);
        return converter;
    }
}

被调用端拦截器

重点是获取刚刚传递过来的token值

request.getHeader(“token”);

/**
 *  小程序等用户请求,拦截器
 */
@Component
public class TaxAppInterceptor implements HandlerInterceptor {

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value("${jwt.secret}")
    private String jwtSecret;

    /**
     * 查数据库登录验证拦截
     *
     * @param request
     * @param response
     * @param o
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        String requestURI = request.getRequestURI();
        String token = request.getHeader("token");
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐