xmtrock
发布于 2021-07-15 / 365 阅读
0

微服务网关Zuul(配合Eureka)

–基本:

依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
</dependencies>

启动类

@EnableZuulProxy//开启zuul网关功能

yml

server:
  port: 8080 #端口
spring:
  application:
    name: api-zuul-server #服务名称
##路由配置
zuul:
  routes:
    #已商品微服务
    product-service: #路由id,随便写
      path: /product-service/** #映射路径  #localhost:8080/product-service/sxxssds
      url: http://localhost:9001 #映射路径对应的实际微服务url地址
      stripPrefix: false #这个是为了免得自动加前缀,避免/aaa/aaa这种方式访问

–更进:

依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

启动类

@EnableZuulProxy//开启zuul网关功能
@EnableDiscoveryClient//eureka的服务发现

yml

server:
  port: 8080 #端口
spring:
  application:
    name: api-zuul-server #服务名称
##路由配置
zuul:
  routes:
    #已商品微服务
    product-service: #路由id,随便写
      path: /product-service/** #映射路径  #localhost:8080/product-service/sxxssds
      url: http://localhost:9001 #映射路径对应的实际微服务url地址
      serviceId: service-product #配置转发的微服务的服务名称
#配置Eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册

这样就不需要可以配置uri,更换product的ip就不需要更换yml,只要你服务中心ip没变就好。

–再更进:

##路由配置
zuul:
  routes:
    product-service: #路由id,随便写
      #如果路由id 和 对应的微服务的serviceId一致的话
      service-product: /product-service/**
      #zuul中的默认路由配置
      #如果当前的微服务名称 service-product , 默认的请求映射路径 /service-product/**
      #  /service-order/

自定义过滤器:

20210715230331527

  1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  1. ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  2. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  3. ERROR:在其他阶段发生错误时执行该过滤器。

正常流程:

请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

异常流程:
整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕后,会将请求交给POST过滤器,最后返回给用户。
如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。
如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求不会再到达POST过滤器了。
不同过滤器的场景:
请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了
异常处理:一般会在error类型和post类型过滤器中结合来处理。
服务调用时长统计:pre和post结合使用。
过滤器(身份认证,重点!):

/**
 * 自定义的zuul过滤器 继承抽象父类
 */
@Component
public class LoginFilter extends ZuulFilter {
   /**
    * 定义过滤器类型
    *  pre、routing、post、error
    */
   public String filterType() { return "pre"; }

   /**
    *  指定过滤器的执行顺序,返回值越小,执行顺序越高
    */
   public int filterOrder() { return 1; }

   /**
    * 当前过滤器是否生效。true : 使用此过滤器、false : 不使用此过滤器
    */
   public boolean shouldFilter() { return true; }

   /**
    * 指定过滤器中的业务逻辑
    *  身份认证:
    *      1.所有的请求需要携带一个参数 : access-token
    *      2.获取request请求
    *      3.通过request获取参数access-token
    *      4.判断token是否为空
    *      4.1 token==null : 身份验证失败
    *      4.2 token!=null : 执行后续操作
    *  在zuul网关中,通过RequestContext的上下问对象,可以获取对象request对象
    */
   public Object run() throws ZuulException {
      //1.获取zuul提供的上下文对象RequestContext
      RequestContext ctx = RequestContext.getCurrentContext();
      //2.从RequestContext中获取request
      HttpServletRequest request = ctx.getRequest();
      //3.获取请求参数access-token
      String token = request.getParameter("access-token");
      //4.判断
      if (token ==null) {
         //4.1 如果token==null ,拦截请求,返回认证失败
         ctx.setSendZuulResponse(false); // 拦截请求
         ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
      }
      //4.2 如果token!=null ,继续后续操作
      return null;
   }
}

20210715231848586

自定义过滤器(Shiro+Redis):

/**
 * 自定义过滤器的
 */
@Component
public class LoginFilter extends ZuulFilter {
    /**
     * 定义过滤器类型:pre、routing、error、post
     * 本过滤器执行时刻:路由请求前、路由请求时、异常时、前面三者之后的收拾收尾
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 定义过滤器的优先级。数字越小,优先级越高的
     */
    @Override
    public int filterOrder() {
        return 2;
    }

    /**
     * boolean类型判断过滤器是否需要执行
     */
    @Override
    public boolean shouldFilter() {
        //假如不执行此过滤器(暂时作废)则false
        return true;
    }

    /**
     * 重点:具体业务在此完成
     */
    @Override
    public Object run() throws ZuulException {
        System.out.println("执行了loginFilter的run方法");
        return null;
    }
}

下面加深部分均在shiro配置包里声明配置,具体内容结合shiro配置来看

@Configuration
public class ShiroConfiguration {
    //创建Realm
    @Bean
    public IhrmRealm getRealm() {
        return new UserRealm();
    }

    //创建安全管理器
    @Bean(name = "securityManager")
    public SecurityManager getSecurityManager(IhrmRealm customRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(customRealm);

        //讲自定义的会话管理器注册到安全管理器
        securityManager.setSessionManager(redisResessionManager());
        //将自定义的redis缓存管理器注册到安全管理器中
        securityManager.setCacheManager(redisCacheManager());

        return securityManager;
    }

    //配置过滤器工厂
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        //web中,shiro进行权限控制全部是通过一组过滤器集合进行控制
        //创建过滤器工厂
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        // //预检请求拦截器,为了预防无法得到header
        // factoryBean.getFilters().put("authc", new ShiroHeaderFilter());
        //设置安全管理器
        factoryBean.setSecurityManager(securityManager);
        //通用配置(跳转登录页面,未授权跳转的页面)
        factoryBean.setLoginUrl("/autherror?code=1");
        factoryBean.setUnauthorizedUrl("/autherror?code=2");
        /**
         * 设置过滤器集合,有顺序的map
         * key=url拦截地址,value=过滤器类型
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/sys/login","anon");//此地址允许匿名
        filterMap.put("/autherror","anon");//此地址允许匿名
        filterMap.put("/sys/faceLogin/**","anon");//此地址允许匿名

        //swagger
        filterMap.put("/swagger-ui.html", "anon");
        filterMap.put("/swagger-resources/**", "anon");
        filterMap.put("/v2/**", "anon");
        filterMap.put("/webjars/**", "anon");

        filterMap.put("/**", "authc");//必须认证

        factoryBean.setFilterChainDefinitionMap(filterMap);
        return factoryBean;
    }

    //开启注解支持
    @Bean(name = "authorizationAttributeSourceAdvisor")
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean(name = "defaultAdvisorAutoProxyCreator")
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);//cglib方式
        return defaultAdvisorAutoProxyCreator;
    }

    //redis的控制器,操作控制器(jar里有,不用自己写的)
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("47.115.203.188");
        redisManager.setPort(6604);
        redisManager.setPassword("flowerpower");
        return redisManager;
    }

    //缓存管理器
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager cacheManager = new RedisCacheManager();
        cacheManager.setRedisManager(redisManager());
        return cacheManager;
    }

    //sessionDao
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }

    //自定义的会话管理器了
    public DefaultWebSessionManager redisResessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        //禁用cookie,禁用url重写(太难看?)
        sessionManager.setSessionIdCookieEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }
}
zuul:
  routes:
    ihrm-company:
      path: /company/**
      serviceId: ihrm-company
      stripPrefix: false #不删除请求前缀
      #处理敏感头信息
      sentiviteHeaders: #指定路由的敏感头设置为空
      customSensitiveHeaders: true #对指定路由开启自定义敏感头