ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

整合SpringCloudAlibaba----Gateway注册Nacos,配置中心管理

2021-06-26 15:32:34  阅读:335  来源: 互联网

标签:return String exchange ip Nacos getRequest ---- token SpringCloudAlibaba


添加依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xxx</groupId>
    <artifactId>xxx-gateway</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
    <name>xxx-gateway</name>
    <description>网关服务 限流 鉴权 转发</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <maven.compiler.compilerVersion>11</maven.compiler.compilerVersion>
        <lombok.version>1.18.20</lombok.version>
        <fastjson.version>1.2.73</fastjson.version>
        <hutool.version>5.7.2</hutool.version>
        <spring-boot.version>2.3.2.RELEASE</spring-boot.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
        <spring-cloud-alibaba.version>2.2.5.RELEASE</spring-cloud-alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>

        <!-- gateway依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
             
        <!-- nacos注册中心依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- nacos配置中心依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
             
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>     
             
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool.version}</version>
        </dependency>
   
    </dependencies>
             
   <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

添加配置

server:
  port: 9001
  servlet:
    context-path: /
spring:
  profiles:
    active: dev
  application:
    name: gateway-server # 应用名称
  cloud:
    nacos:
      username:
      password:
	  #nacos注册地址
      discovery:
        server-addr: ${wfx.ip}:8848
      config:
		#配置文件后缀
        file-extension: yaml     
        #命名空间 默认public
        namespace:   
 		#组
        group: ${spring.application.name}-group     
        #配置中心地址
        server-addr: ${wfx.ip}:8848
    gateway:
	#处理跨越问题
      default-filters:
        - DedupeResponseHeader=Access-Control-Allow-Origin, RETAIN_UNIQUE
      globalcors:
        corsConfigurations:
          '[/**]':
            # 允许携带认证信息
            allow-credentials: true
            # 允许跨域的源(网站域名/ip),设置*为全部
            allowedOrigins: "*"
            # 允许跨域的method, 默认为GET和OPTIONS,设置*为全部
            allowedMethods: "*"
            # 允许跨域请求里的head字段,设置*为全部
            allowedHeaders: "*"
      discovery:
        locator:
          enabled: true
      routes:
		#搜索服务
        - id:  search-server
          # 重点!/info必须使用http进行转发,lb代表从注册中心获取服务
          uri: lb://search-server
          predicates:
            #IP白名单针对当前服务的
            - RemoteAddr=127.0.0.1
            # 重点!转发该路径!,/**,
            - Path=/search/**
          filters:
          #去掉第一个路径
            - StripPrefix=1  
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always
logging:
  level:
  	#日志级别
    org.springframework.cloud.gateway: info

#自定义配置
define:
  ev: ${spring.profiles.active}
 #放行url
  urlWhileList:
    # - /search/tesst/*
   #全局黑名单
  blackList:
   - 127.0.0.2

Nacos动态刷新自定义配置

@Configuration
@RefreshScope
@Data
@ConfigurationProperties(prefix = "define")
public class BaseBean {

    /**
     * 黑名单
     */
    private List<String> blackList;

    /**
     * 放行路径
     */
    private List<String> urlWhileList;

    /**
     * 当前环境
     */
    private String ev;

}

Gateway拦截器 公共token的校验 全局限流

@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApiGlobalFilter implements GlobalFilter, Ordered {

	
	//自定义的配置
    private final BaseBean baseBean;
    private final String TEST = "test";
    private final String DEV = "dev";
    //默认店铺id
    private final String SID = "0";
    //默认分区
    private final String DB_TAG = "fxcs";
    //默认用户id
    private final String UID = "0";

    /**
     * 拦截所有的请求头
     *
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpResponse response = exchange.getResponse();
        Route gatewayUrl = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        URI uri = gatewayUrl.getUri();
        baseBean.getEv();
        //全局黑名单ip
        if (!CollectionUtils.isEmpty(baseBean.getBlackList()) && baseBean.getBlackList().contains(IpUtil.getIp(exchange.getRequest()))) {
            log.error("系统环境:{},禁止访问=====Ip:{},请求服务名:{},url:{},路径:{}"
                    , baseBean.getEv()
                    , IpUtil.getIp(exchange.getRequest())
                    , uri.getAuthority()
                    , exchange.getRequest().getURI()
                    , exchange.getRequest().getPath());
            return authErr(response, ErrorEnum.SYSTEM_BLANK.code(), ErrorEnum.SYSTEM_BLANK.value());
        }
        //放行路径
        if (UrlResolverUtil.check(baseBean.getUrlWhileList(), exchange.getRequest().getURI().getPath())) {
            log.info("系统环境:{},请求======Ip:{},请求服务名:{},url:{},放行路径:{}"
                    , baseBean.getEv()
                    , IpUtil.getIp(exchange.getRequest())
                    , uri.getAuthority()
                    , exchange.getRequest().getURI()
                    , exchange.getRequest().getPath());
            return chain.filter(exchange);
        }

        //测试环境或者开发环境 不需要效验
        if (TEST.equals(baseBean.getEv()) || DEV.equals(baseBean.getEv())) {
            log.info("系统环境:{},请求======Ip:{},集群标识:{},SID:{},请求服务名:{},url:{},路径:{}"
                    , baseBean.getEv()
                    , IpUtil.getIp(exchange.getRequest())
                    , DB_TAG
                    , SID
                    , uri.getAuthority()
                    , exchange.getRequest().getURI()
                    , exchange.getRequest().getPath());
            Consumer<HttpHeaders> httpHeaders = httpHeader -> {
                httpHeader.set("SID", SID);
                httpHeader.set("DB_TAG", DB_TAG);
            };
            ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
            ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
            return chain.filter(build);
        }


        //认证
        String token = "";
        MultiValueMap<String, HttpCookie> cookies = exchange.getRequest().getCookies();
        //先从header获取
        List<String> list = exchange.getRequest().getHeaders().get("token");
        //入参获取
        String rToken = list == null ? exchange.getRequest().getQueryParams().getFirst("token") : list.get(0);
        //从cookie获取
        HttpCookie r_token = cookies.get("r_token") == null ? null : cookies.get("token").stream()
                .filter(a -> "r_token".equals(a.getName()))
                .findFirst()
                .orElse(null);
        token = r_token == null ? rToken : r_token.getValue();

        if (StrUtil.isBlank(token)) {
            log.error("系统环境:{},禁止访问=====Ip:{},请求服务名:{},url:{},路径:{}"
                    , baseBean.getEv()
                    , IpUtil.getIp(exchange.getRequest())
                    , uri.getAuthority()
                    , exchange.getRequest().getURI()
                    , exchange.getRequest().getPath());
            return authErr(response, ErrorEnum.SYSTEM_BLANK.code(), ErrorEnum.SYSTEM_BLANK.value());
        }
        JSONObject jsonObject;
        try {
            //检验token
            jsonObject = TokenUtil.verify(token);
        } catch (Exception e) {
            log.error("系统环境:{},禁止访问=====Ip:{},请求服务名:{},url:{},路径:{},异常:{}"
                    , baseBean.getEv()
                    , IpUtil.getIp(exchange.getRequest())
                    , uri.getAuthority()
                    , exchange.getRequest().getURI()
                    , exchange.getRequest().getPath()
                    , e);
            return authErr(response, ErrorEnum.SYSTEM_ILLEGAL_TOKEN.code(), ErrorEnum.SYSTEM_ILLEGAL_TOKEN.value());
        }

        JSONObject finalJsonObject = jsonObject;
        Consumer<HttpHeaders> httpHeaders = httpHeader -> {
            httpHeader.set("SID", finalJsonObject.getString("SID"));
            httpHeader.set("DB_TAG", finalJsonObject.getString("DB_TAG"));
            httpHeader.set("UID", finalJsonObject.getString("UID"));
        };
        ServerHttpRequest serverHttpRequest = exchange.getRequest().mutate().headers(httpHeaders).build();
        ServerWebExchange build = exchange.mutate().request(serverHttpRequest).build();
        return chain.filter(build);
    }


    private Mono<Void> authErr(ServerHttpResponse response, int code, String msg) {
        JSONObject message = new JSONObject();
        // 响应状态
        message.put("code", code);
        // 响应内容
        message.put("msg", msg);
        // 转换响应消息内容对象为字节
        byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
        DataBuffer buffer = response.bufferFactory().wrap(bits);
        // 设置响应对象状态码 200
        response.setStatusCode(HttpStatus.OK);
        // 设置响应对象内容并且指定编码,否则在浏览器中会中文乱码Content-Type:
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        // 返回响应对象
        return response.writeWith(Mono.just(buffer));
    }

	/**
	*负责filter的顺序,数字越小越优先,越靠前
	*/
    @Override
    public int getOrder() {
        return 0;
    }

用到的工具类

public class UrlResolverUtil {
    private final static PathMatcher MATCHER = new AntPathMatcher();

    /**
     * 验证url是否匹配,支持精确匹配和模糊匹配
     *
     * @param patternPaths
     * @param requestPath
     * @return
     */
    public static boolean check(List<String> patternPaths, String requestPath) {
        if (CollectionUtils.isEmpty(patternPaths)) {
            return false;
        }
        for (String i : patternPaths) {
            if (i.endsWith("*")) {
                i = i.substring(0, i.length() - 1);
                if (MATCHER.matchStart(requestPath, i)) {
                    return true;
                }
            }
            if (MATCHER.match(i, requestPath)) {
                return true;
            }
        }
        return false;
    }
}


public enum ErrorEnum {
    SYSTEM_RED("网络异常,稍后再试", 500),
    SYSTEM_SKIP("重定向", 301),
    SYSTEM_GREEN("非法参数", 400),
    SYSTEM_PARAM_NOT_EXIST("缺少参数", 402),
    SYSTEM_BLANK("没有权限!", 401),
    SYSTEM_ILLEGAL_TOKEN("非法token,请重新登录!", 403),
    SYSTEM_RESOURCE_NOT_FIND("资源不存在",410),
    SYSTEM_CONFLICT("数据冲突!", 409),
    SYSTEM_OFTEN("操作太频繁,稍后再试", 423)
    ;

    private final Integer code;
    private final String value;

    ErrorEnum(String value, Integer code) {
        this.code = code;
        this.value = value;
    }

    public Integer code() {
        return this.code;
    }

    public String value() {
        return this.value;
    }

    public static ErrorEnum getTypeByEnum(Integer code) {
        if (code==null) {
            return null;
        }
        for (ErrorEnum enums : ErrorEnum.values()) {
            if (enums.code().equals(code)) {
                return enums;
            }
        }
        return null;
    }

}

public class TokenUtil {
    /**
     * 秘钥
     */
    public static final String key = "自定义";

    /**
     * 有效期,默认2小时
     */
    public static final long expirationTimeInSecond = 60*60*2;


    /**
     * 生成token 无过期时间  默认算法是:HS256(HmacSHA256)
     * @param payload 荷载信息
     * @return String Token
     */
    public static String createToken( Map<String, Object> payload) {
        return JWT.create()
                .addPayloads(payload)
                .setKey(key.getBytes())
                .sign();
    }

    /**
     * 生成token  默认算法是:HS256(HmacSHA256)
     * @param payload    荷载信息
     * @param expiration 过期时间 单位秒  默认两小时
     * @return String Token
     */
    public static String createToken(Map<String, Object> payload, Long expiration) {
        //签发时间
        Date date = new Date();
        payload.put(JWTPayload.ISSUED_AT, date);
        //到期时间
        long l = DateUtil.timeToLong(date).longValue();
        long expirationTime = expiration == null ? l+expirationTimeInSecond : l+expiration.longValue();
        payload.put(JWTPayload.EXPIRES_AT, DateUtil.longToDate(expirationTime));
        return JWT.create()
                .addPayloads(payload)
                .setKey(key.getBytes())
                .sign();
    }


    /**
     * 验证JWT Token是否有效
     *
     * @param token token
     * @return JSONObject 荷载信息的JSONObject
     * @throws ValidateException 过期异常
     * @throws JWTException   非法token异常
     */
    public static JSONObject verify(String token) throws JWTException, ValidateException {
        JWT jwt = JWT.of(token).setKey(key.getBytes());
        JWTValidator jwtValidator = JWTValidator.of(jwt);
        jwtValidator.validateDate(new Date());
        return JSONObject.parseObject(jwt.getPayloads().toString());
    }
}


public class IpUtil {

    public static String getIp(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            if (ip.indexOf(",") != -1) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getAddress().getHostAddress();
        }
        return ip;
    }
}


Gateway全局异常处理错误

@Slf4j
@Order(-1)
@RequiredArgsConstructor
@Configuration
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
    private final ObjectMapper objectMapper;
    private final BaseBean baseBean;
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        log.error("系统环境:{},请求======Ip:{},url:{},路径:{},Gateway全局异常处理:{}"
                ,baseBean.getEv()
                , IpUtil.getIp(exchange.getRequest())
                , exchange.getRequest().getURI()
                , exchange.getRequest().getPath()
                , ex);
        ServerHttpResponse response = exchange.getResponse();
        if (response.isCommitted()) {
            return Mono.error(ex);
        }
        Map map = new HashMap<>(2);
        // header set
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        if (ex instanceof ResponseStatusException) {
            HttpStatus status = ((ResponseStatusException) ex).getStatus();
            response.setStatusCode(status);
            map.put("code",status.value());
        }else {
            map.put("code", ErrorEnum.SYSTEM_RED.code());
        }
        map.put("msg",ex.getMessage());
        map.put("data","");
        return response
                .writeWith(Mono.fromSupplier(() -> {
                    DataBufferFactory bufferFactory = response.bufferFactory();
                    try {
                        return bufferFactory.wrap(objectMapper.writeValueAsBytes(map));
                    } catch (JsonProcessingException e) {
                        log.error("Gateway全局异常处理错误", ex);
                        return bufferFactory.wrap(new byte[0]);
                    }
                }));
    }
}

下期分享整合SpringCloudAlibaba----Gateway与Sentinel

标签:return,String,exchange,ip,Nacos,getRequest,----,token,SpringCloudAlibaba
来源: https://blog.csdn.net/weixin_44565642/article/details/118248366

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有