API网关的实现--2 springboot使用zuul

配置pom

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.3.5.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Brixton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

zuul依赖

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

启用zuul

@SpringBootApplication
@EnableZuulProxy
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

重点一:路由注册

application.properties(或者application.yml  树状结构)

#在具体的开发dev、测试test、生产prod为后缀的文件中配置个性化的服务端口等信息
spring.profiles.active=dev

##超时设置
zuul.host.socket-timeout-millis=60000
zuul.host.connect-timeout-millis=10000

#文件上传大小配置
spring.http.multipart.maxFileSize=5Mb
spring.http.multipart.maxRequestSize=10Mb

#####################################
# 为了避免路由表出现在多个配置文件,现在将路由表统一移至这里
#####################################
#定义各个服务的路由名称 比如对于请求client系统url会用${server.client}标识
zuul.routes.client.path=/client-service/**
zuul.routes.client.url=${server.client}/
swagger.butler.resources.client.name=client-api

zuul.routes.uums.path=/uums-service/**
zuul.routes.uums.url=${server.uums}/
swagger.butler.resources.uums.name=uums-api
#####################################
#
# 登录及获取用户基本信息路由表(实际不需要路由)--登录登出等特殊的几个接口处理
#
#####################################
#用户登录  在拦截器中做了相关权限验证,转发
zuul.routes.login.path=/api/login/**
#获取用户基本信息
zuul.routes.getUserInfo.path=/api/secret/getUserInfo/**
#获取用户最新基本信息(用户身份信息变更场景)
zuul.routes.getLatestUserInfo.path=/api/secret/getLatestUserInfo/**
#用户登出
zuul.routes.logout.path=/api/secret/logout/**

#####################################
# 注册服务路由表
#####################################
zuul.routes.regcheckMobile.path=/api/open/reg/checkMobile/**
zuul.routes.regcheckMobile.url=${server.uums}/reg/checkMobile

zuul.routes.registerWithJdxt.path=/api/secret/reg/registerWithJdxt/**
zuul.routes.registerWithJdxt.url=${server.uums}/reg/registerWithJdxt
#也可以给一个系统或者一类应用做统一的匹配  它会将以/api/secret/order/**开始的接口转发到
#${server.order}/**系统中
zuul.routes.order.path=/api/secret/order/**
zuul.routes.order.url=${server.order}/

开发文件配置:

#服务器端口设置
server.port=8666
debug=false
#开启路由自动回调
#router.debug=true
router.debug.force=true
feign.httpclient.enabled=true

#服务器地址统一管理
server.uums=http://172.16.16.68:8088
server.admin=http://172.16.16.68:8086
server.client=http://172.16.16.68:8090
server.aces=http://172.16.16.68:8092
server.admin.lxxd=http://172.16.16.68:8096
server.order=http://172.16.16.45:8800
server.warning=http://172.16.16.45:8900
server.sxt=http://192.168.8.199:8070
##########################################################
#redis 相关设置
##########################################################
#spring-boot-redis 自动配置参数: RedisAutoConfiguration RedisProperties  
#注意:当前redis server 没有配置访问密码,因此,启动参数必须设置为非保护模式:
#./redis-server redis.conf --protected-mode no
spring.redis.protected-mode=no
spring.redis.database=0
spring.redis.url=
spring.redis.host=172.16.16.36
spring.redis.password=
spring.redis.port=6379
spring.redis.ssl=false
spring.redis.timeout=2000
spring.redis.pool.maxIdle=8
spring.redis.pool.minIdle=0
spring.redis.pool.maxActive=8
spring.redis.pool.maxWait=-1

关于listOfServers配置负载均衡以及ribbon.eureka等的配置有待学习

重点二:过滤器配置

配置filter(filter规则后面详细说明)

package ixinnuo.financial.gateway.router;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import ixinnuo.financial.gateway.utils.Util;
/**
*前置拦截器--权限校验(不需权限直接通过)
**/
@Component
public class RouterPreOpen extends ZuulFilter{  //需继承zuul提供的过滤器类

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {  //返回为true才会进入run()方法执行拦截
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        return (boolean)request.getAttribute("shouldFilter");
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        System.out.println("得到的url为:"+request.getRequestURL());
        String urlPath = Util.getUrlPath(request.getRequestURL().toString());

        if(urlPath.toLowerCase().startsWith("/api/open/")){
            logger.info("[进入API网关过滤器][开放接口,不需token,直接前往微服务层]");      //将拦截结果添加到请求中,若不需要走下面的过滤器,可赋值为false,反之为true
            request.setAttribute("shouldFilter", false);
            return null;
        }
        return null;
    }
    
}
package ixinnuo.financial.gateway.router;

import java.io.UnsupportedEncodingException;
import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;

import ixinnuo.financial.gateway.framework.Code;
import ixinnuo.financial.gateway.framework.RedisDao;
import ixinnuo.financial.gateway.framework.ReturnData;
import ixinnuo.financial.gateway.utils.HttpClientUtil;
import ixinnuo.financial.gateway.utils.Util;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/**
*前置过滤器--需要权限的校验
**/
@Component
public class RouterPreSecret extends ZuulFilter{

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    private final String TOKEN_PREFIX= "API_GATEWAY_ACCESS_TOKEN:";
    private final int ONE_MINUTE= 60;
    
@Value(value="${server.uums}")
    String uums_url;
    @Autowired
    private RedisDao redisDao;  //存储用户信息,用做有效时长内用户权限的检验
    
    @Override
    public String filterType() {
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 2;
    }

    @Override
    public boolean shouldFilter() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        return (boolean)request.getAttribute("shouldFilter");
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String urlPath = Util.getUrlPath(request.getRequestURL().toString());
  //针对secret需要相关权限验证的请求做相关判断      
  if(urlPath.toLowerCase().startsWith("/api/secret/")){
            
            Enumeration<String> headerNames = request.getHeaderNames();
            String accessToken = "";
            while (headerNames.hasMoreElements()) {
                String key = headerNames.nextElement();
                String value = request.getHeader(key);
                logger.debug("[Headers][{}][{}]",key,value);
                if("token".equalsIgnoreCase(key)){
                    accessToken = value; 
                    break;
                }
            }

            if(StringUtils.isBlank(accessToken)) {//说明认证信息不全,不予通过权限
                ReturnData retData = new ReturnData(Code.TOKEN_EXPIRE_OR_INVALID);
                JSONObject jo = JSONObject.fromObject(retData);
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(200);
                ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");  
                try {
                    ctx.getResponse().getWriter().write(jo.toString());
                }catch (Exception e) {}
                logger.info("[没有携带Token,拒绝访问]");
                request.setAttribute("shouldFilter", false);
                return null;
            }

            String userInfoString = redisDao.getStringValue(TOKEN_PREFIX+accessToken);
            if(StringUtils.isBlank(userInfoString)){
                ReturnData retData = new ReturnData(Code.TOKEN_EXPIRE_OR_INVALID);
                JSONObject jo = JSONObject.fromObject(retData);
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(200);
                ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");  
                try {
                    ctx.getResponse().getWriter().write(jo.toString());
                }catch (Exception e){}
                logger.info("[用Token换取的用户基本信息为空,拒绝访问]");
                request.setAttribute("shouldFilter", false);
                return null;
            }

            if(urlPath.toLowerCase().startsWith("/api/secret/getuserinfo")){
                
                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(200);
                ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");  
                try {
                    ctx.getResponse().getWriter().write(userInfoString);
                }catch (Exception e){}
                logger.info("[获取用户基本信息成功: {}]",userInfoString);
                request.setAttribute("shouldFilter", false);
                return null;

            }else if(urlPath.toLowerCase().startsWith("/api/secret/logout")){
                redisDao.deleteKey(TOKEN_PREFIX+accessToken);

                ReturnData retData = new ReturnData(Code.OK);
                JSONObject jo = JSONObject.fromObject(retData);

                ctx.setSendZuulResponse(false);
                ctx.setResponseStatusCode(200);
                ctx.getResponse().setHeader("Content-type", "application/json;charset=UTF-8");  
                try {
                    ctx.getResponse().getWriter().write(jo.toString());
                }catch (Exception e){}
                logger.info("[用户登出成功,Token: {}]",accessToken);
                request.setAttribute("shouldFilter", false);
                return null;
            }
            byte[] bytes = null;
            try {
                bytes = userInfoString.getBytes("utf-8");
            }catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            @SuppressWarnings("restriction")
            String base64 = new sun.misc.BASE64Encoder().encode(bytes);
            base64 = base64.replaceAll("\r|\n", "");
            ctx.addZuulRequestHeader("loginInfo", base64);
            logger.info("[即将前往微服务层,携带经过base64后的loginInfo: {}]",userInfoString);
            request.setAttribute("shouldFilter", false);
            return null;
            
        }

        request.setAttribute("shouldFilter", true);
        return null;

    }
    
}

 此外,还需要登录拦截器,匹配到登录的路由,进行登录请求,取得用户信息,并存储在redis中对应的时长,以做后面权限的校验。

猜你喜欢

转载自blog.csdn.net/happyAliceYu/article/details/79045472