springcloud+gateway微服务整合swagger
单一的微服务集成swagger:
maven:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version> </dependency>
package fwz.cpjiang.ums.utils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.ParameterBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.schema.ModelRef; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.Parameter; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.util.ArrayList; import java.util.List; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi(){ ParameterBuilder tokenPar = new ParameterBuilder(); List<Parameter> pars = new ArrayList<Parameter>(); tokenPar.name("token").description("令牌").modelRef(new ModelRef("string")).parameterType("header").required(false).build(); pars.add(tokenPar.build()); return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()).build().globalOperationParameters(pars); } private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("用户管理 API Doc") .description("This is a restful api document of 用户管理.") .version("1.0") .build(); } }
此时swagger只是存在于单一的微服务,很不方便
gateway整合swagger:
所有的微服务均按上述配置swagger
然后在gateway微服务中,添加如下配置:
package fwz.cpjiang.gateway.utils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.documentation.swagger.web.SecurityConfiguration; import springfox.documentation.swagger.web.SecurityConfigurationBuilder; import springfox.documentation.swagger.web.UiConfiguration; import springfox.documentation.swagger.web.UiConfigurationBuilder; @Configuration public class SwaggerConfig { @Bean public SecurityConfiguration securityConfiguration() { return SecurityConfigurationBuilder.builder().build(); } @Bean public UiConfiguration uiConfiguration() { return UiConfigurationBuilder.builder().build(); } }
package fwz.cpjiang.gateway.utils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; import springfox.documentation.swagger.web.*; import java.util.List; import java.util.Optional; @RestController @RequestMapping("/swagger-resources") public class SwaggerHandler { private final SecurityConfiguration securityConfiguration; private final UiConfiguration uiConfiguration; private final SwaggerResourcesProvider swaggerResources; @Autowired public SwaggerHandler(SwaggerResourcesProvider swaggerResources, SecurityConfiguration securityConfiguration, UiConfiguration uiConfiguration) { this.swaggerResources = swaggerResources; this.securityConfiguration = securityConfiguration; this.uiConfiguration = uiConfiguration; } @GetMapping("/configuration/security") public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping("/configuration/ui") public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() { return Mono.just(new ResponseEntity<>( Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK)); } @GetMapping public Mono<ResponseEntity<List<SwaggerResource>>> swaggerResources() { return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK))); } }
package fwz.cpjiang.gateway.utils; import lombok.AllArgsConstructor; import org.springframework.cloud.gateway.config.GatewayProperties; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.support.NameUtils; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; import springfox.documentation.swagger.web.SwaggerResource; import springfox.documentation.swagger.web.SwaggerResourcesProvider; import java.util.ArrayList; import java.util.List; @Primary @Component @AllArgsConstructor public class SwaggerProvider implements SwaggerResourcesProvider { public static final String API_URI = "/v2/api-docs"; private final RouteLocator routeLocator; private final GatewayProperties gatewayProperties; @Override public List<SwaggerResource> get() { List<SwaggerResource> resources = new ArrayList<>(); List<String> routes = new ArrayList<>(); routeLocator.getRoutes().subscribe(route -> routes.add(route.getId())); gatewayProperties.getRoutes().stream() .filter(routeDefinition -> routes.contains(routeDefinition.getId())) .forEach(routeDefinition -> routeDefinition.getPredicates().stream() .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName())) .forEach(predicateDefinition -> resources .add(swaggerResource(routeDefinition.getId(), predicateDefinition.getArgs() .get(NameUtils.GENERATED_NAME_PREFIX + "0").replace("/**", API_URI))))); return resources; } private SwaggerResource swaggerResource(String name, String url) { SwaggerResource swaggerResource = new SwaggerResource(); swaggerResource.setName(name); swaggerResource.setUrl(url); swaggerResource.setSwaggerVersion("2.0"); return swaggerResource; } }
package fwz.cpjiang.gateway.utils; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import java.util.List; @Configuration @ConfigurationProperties(prefix = "system") public class UrlConfig { // 配置文件使用list接收 private List<String> whiteList; public List<String> getWhiteList() { return whiteList; } public void setWhiteList(List<String> whiteList) { this.whiteList = whiteList; } }
yml配置文件:
system:
whiteList:
- "/swagger-ui.html"
- "/swagger-resources/**"
- "/v2/api-docs"
- "/admin/login"
routes:
- id: hbistc-ums
uri: lb://hbistc-ums
predicates:
- Path=/ums/**
filters:
- StripPrefix=1
- id: hbistc-mall
uri: lb://hbistc-mall
predicates:
- Path=/mall/**
filters:
- StripPrefix=1
package fwz.cpjiang.gateway.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; import java.util.Date; import java.util.UUID; /** * JWT工具类 */ public class JwtUtils { //有效期为 public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000 一个小时 //设置秘钥明文 public static final String JWT_KEY = "joijgisdlwejjjfeawoGdeizeffsaz"; /** * 创建token * * @param id * @param subject * @param ttlMillis * @return */ public static String createJWT(String id, String subject, Long ttlMillis) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); if (ttlMillis == null) { ttlMillis = JwtUtils.JWT_TTL; } long expMillis = nowMillis + ttlMillis; Date expDate = new Date(expMillis); SecretKey secretKey = generalKey(); JwtBuilder builder = Jwts.builder() .setId(id) //唯一的ID .setSubject(subject) // 主题 可以是JSON数据 .setIssuer("admin") // 签发者 .setIssuedAt(now) // 签发时间 .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥 .setExpiration(expDate);// 设置过期时间 return builder.compact(); } /** * 生成加密后的秘钥 secretKey * * @return */ public static SecretKey generalKey() { byte[] encodedKey = Base64.getDecoder().decode(JwtUtils.JWT_KEY); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } /** * 解析 * * @param jwt * @return * @throws Exception */ public static Claims parseJWT(String jwt) throws Exception { SecretKey secretKey = generalKey(); return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwt) .getBody(); } public static void main(String[] args) throws Exception { String token = createJWT(UUID.randomUUID().toString(), "ggggggg", null); System.out.println(token); Claims c = parseJWT(token); String id = c.getId(); System.out.println(id); } }
package fwz.cpjiang.gateway.filter; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import fwz.cpjiang.gateway.utils.JwtUtils; import fwz.cpjiang.gateway.utils.UrlConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.netty.ByteBufFlux; import java.util.HashMap; import java.util.Map; /** * 鉴权过滤器 验证token */ @Component public class AuthorizeFilter implements GlobalFilter, Ordered { private static final String AUTHORIZE_TOKEN = "token"; @Autowired private UrlConfig urlConfig; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1. 获取请求 ServerHttpRequest request = exchange.getRequest(); //2. 则获取响应 ServerHttpResponse response = exchange.getResponse(); //3. 如果是登录请求则放行 for (int i = 0; i <urlConfig.getWhiteList().size() ; i++) { if (request.getURI().getPath().contains(urlConfig.getWhiteList().get(i))) { return chain.filter(exchange); } } //4. 获取请求头 HttpHeaders headers = request.getHeaders(); //5. 请求头中获取令牌 String token = headers.getFirst(AUTHORIZE_TOKEN); //6. 判断请求头中是否有令牌 if (StrUtil.isEmpty(token)) { //7. 响应中放入返回的状态吗, 没有权限访问 response.setStatusCode(HttpStatus.UNAUTHORIZED); //8. 返回 //return response.setComplete(); //设置响应头信息Content-Type类型 response.getHeaders().add("Content-Type","application/json"); //设置返回json数据 return response.writeAndFlushWith(Flux.just(ByteBufFlux.just(response.bufferFactory().wrap(getWrapData())))); } //9. 如果请求头中有令牌则解析令牌 try { JwtUtils.parseJWT(token); } catch (Exception e) { e.printStackTrace(); //10. 解析jwt令牌出错, 说明令牌过期或者伪造等不合法情况出现 response.setStatusCode(HttpStatus.UNAUTHORIZED); //11. 返回 //设置响应头信息Content-Type类型 response.getHeaders().add("Content-Type","application/json"); //设置返回json数据 return response.writeAndFlushWith(Flux.just(ByteBufFlux.just(response.bufferFactory().wrap(getWrapData())))); } //12. 放行 return chain.filter(exchange); } @Override public int getOrder() { return 0; } private byte[] getWrapData() { Map<String,String> map = new HashMap<>(); map.put("code","1"); map.put("msg","token is empty or illegal"); try { return new ObjectMapper().writeValueAsString(map).getBytes(); } catch (JsonProcessingException e) { // } return "".getBytes(); } }
进行白名单过滤
然后访问网关:
可以发现红框内出现了微服务切换的选项,至此整合完毕