Swagger怎么做免鉴权

前言

Swagger在API文档生成及测试方面非常方便,但是很多的API调用都需要用到token验证,然后经过Gateway网关,鉴权验证通过之后访问业务系统。为了方便后端开发自测接口,我们可以免去鉴权吗?答案是可以的!

一般鉴权方式

我们先看看如果需要鉴权,应该怎么做呢?
可以在swagger config类里边使用 globalOperationParameters() 方法来配置全局参数token:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
 
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(Arrays.asList(
                        new ParameterBuilder()
                                .name("token")
                                .description("授权")
                                .modelRef(new ModelRef("string"))
                                .parameterType("header")
                                .required(false)
                                .build()
                ))
                .apiInfo(apiInfo());
    }
 
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("接口文档")
                .description("接口文档")
                .version("1.0.0")
                .build();
    }
 }

也可以使用 securitySchemes() 方法来配置安全协议token:

@Configuration
@EnableSwagger2
public class SwaggerConfig {
 
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.demo.controller"))
                .paths(PathSelectors.any())
                .build()
                .securitySchemes(Arrays.asList(
                        new ApiKey("Bearer", "token", "header")
                ))
                .apiInfo(apiInfo());
    }
 
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("接口文档")
                .description("接口文档")
                .version("1.0.0")
                .build();
    }
 }

请求头有了token,一般项目的话,请求发到服务端做鉴权,微服务的话,访问服务端之前,需要在Gateway鉴权,我们先了解下什么是Gateway,他的作用又是什么呢。

Gateway是什么?

官方解释Gateway微服务中必不可少的一个组件,旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,具有参数校验、权限校验、流量监控、日志输出、协议转换、响应内容、响应头修改等功能。
微服务架构图

那我们想想,之前没有微服务的时候,这些功能不是在业务系统里面也能实现吗?为什么现在都要放在Gateway中来呢?没错,为了解耦!业务系统专注于系统本身,网关作为接收客户端请求和业务系统的中间层,这些”杂事“交给Gateway统一管理再合适不过了。

Gateway三大核心

1.Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
2.Predicate(断言):参考的是Java8的java.util.function.Predicate
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
3.Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前(pre)或者之后(post)对请求进行修改,目前开放出来的过滤参数有兴趣可以了解下,https://blog.csdn.net/m0_67402096/article/details/126496872。

Gateway配置示例

###########应用配置  start###########
server:
port: 8080

spring:
  cloud:
    gateway:
      routes:
        - id: 39-base
          uri: http://10.43.117.39:8001
          predicates:
            - Path=/39/projectName/**
          filters:
            - StripPrefix=2
            - name: AddUserName
              args:
                enabled: true
                shouldSkipUrls:
                  - /v2/api-docs/**
                  - /swagger-ui.html*
                  - /webjars/**
                  - /actuator/**
                  - /swagger-resources/**
                  - /i18n/**
                  - /login-free/**
                  - /cloud-test/findPrintList*
                  - /persons/resetPassword**
                  - /cloud-test/**
                  - /t-role-apply/apsPlanPageInfo
                  - /IAM-app-user/**
                  - /flotsam/dataTransferBPMPS
                  - /qrCode/**					
###########应用配置  end###########	

解读配置

现在有一个服务39-base部署在本机,地址和端口为10.43.117.39:8081,所以路由配置uri为http://10.43.117.39:8081
使用网关服务路由到此服务,predicates -Path=/39/projectName/**,网关服务的端口为8080,启动网关服务,访问10.43.117.39:8080/39/projectName,路由断言就会将请求路由到39-base。
直接访问39-base的接口10.43.117.39:8081/swagger-ui.html#/api/test,通过网关的访问地址则为10.43.117.39:8080/39/projectName/swagger-ui.html#/api/test,predicates配置将请求断言到此路由,filters-StripPrefix=1代表将地址中/后的第一个截取,所以39/projectName就截取掉了。

那我们知道了,鉴权其实就是一种过滤规则,或者说是校验规则。那怎么做到免鉴权的呢,其实就是在Filter中配置相应的参数,但是目前Gateway没有开放相应的参数,比较好的是,它支持自定义参数,具体是怎么做的呢,我们往下看:
1、配置yml,如上图所示,配置的shouldSkipUrls参数;
2、配置文件:NotAuthUrlPropertie;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.LinkedHashSet;

@Data
@Component
@ConfigurationProperties("agilepay.gateway")
public class NotAuthUrlProperties {

    private LinkedHashSet<String> shouldSkipUrls;
}

3、重写GlobalFilter.Filter方法。(Gateway提供GlobalFilter(实现过滤器业务)及Ordered(定义过滤器执行顺序)两个接口用来定义过滤器,我们自定义过滤器只需要实现这个两个接口即可。)

@Component
@Slf4j
public class WrapperRequestGlobalFilter implements GlobalFilter, Ordered {//, Ordered
    @Autowired
    private RsaKeyMapper rsaKeyMapper;

    @Resource
    private NotAuthUrlProperties notAuthUrlProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        String currentUrl = exchange.getRequest().getURI().getPath();

        //1:不需要认证的url,不过滤放行的url
        if (shouldSkip(currentUrl)) {
            log.info("==========已跳过url{}=====", currentUrl);
            return chain.filter(exchange);
        }
        log.info("===========网关请求解密开始==================");
        return exchange.getResponse().setComplete();
    }


    @Override
    public int getOrder() {
        return 0;
    }

    /**
     * 方法实现说明:不需要过滤的路径
     * <p>
     * //     * @param currentUrl 当前请求路径
     */
    private boolean shouldSkip(String currentUrl) {
        PathMatcher pathMatcher = new AntPathMatcher();
        for (String skipPath : notAuthUrlProperties.getShouldSkipUrls()) {
            if (pathMatcher.match(skipPath, currentUrl)) {
                return true;
            }
        }
        return false;
    }
}

使用gateway通过配置文件即可完成路由的配置,非常方便,我们只要充分的了解配置项的含义及规则就可以了;但是这些配置如果要修改则需要重启服务,重启网关服务会导致整个系统不可用,这一点是无法接受的,下面介绍如何通过Nacos实现动态路由。
使用nacos结合gateway-server实现动态路由,我们需要先部署一个nacos服务,可以使用docker部署或下载源码在本地启动,具体操作可以参考官方文档即可。

Nacos_Gateway配置

在这里插入图片描述
Nacos实现动态路由的方式核心就是通过Nacos配置监听,配置发生改变后执行网关相关api创建路由
在这里插入图片描述

Nacos配置监听

@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosDynamicRouteService.class);

@Autowired
private RouteDefinitionWriter routeDefinitionWriter;

private ApplicationEventPublisher applicationEventPublisher;

/** 路由id */
private static List<String> routeIds = Lists.newArrayList();

/**
 * 监听nacos路由配置,动态改变路由
 * @param configInfo
 */
@NacosConfigListener(dataId = "routes", groupId = "gateway-server")
public void routeConfigListener(String configInfo) {
    clearRoute();
    try {
        List<RouteDefinition> gatewayRouteDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
        for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
            addRoute(routeDefinition);
        }
        publish();
        LOGGER.info("Dynamic Routing Publish Success");
    } catch (Exception e) {
        LOGGER.error(e.getMessage(), e);
    }
    
}


/**
 * 清空路由
 */
private void clearRoute() {
    for (String id : routeIds) {
        routeDefinitionWriter.delete(Mono.just(id)).subscribe();
    }
    routeIds.clear();
}

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    this.applicationEventPublisher = applicationEventPublisher;
}

/**
 * 添加路由
 * 
 * @param definition
 */
private void addRoute(RouteDefinition definition) {
    try {
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        routeIds.add(definition.getId());
    } catch (Exception e) {
        LOGGER.error(e.getMessage(), e);
    }
}

/**
 * 发布路由、使路由生效
 */
private void publish() {
    this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
}
}

总结

Swagger免鉴权是重写了Gateway中GlobalFilter接口Filter方法,方法中用当前请求url和配置免鉴权的url做匹配,匹配到则免鉴权;整合Nacos及配置监听就可以实现动态路由了。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot是一个快速开发框架,Swagger是一个API文档生成工具,OAuth2是一个授权框架,可以用于保护API。 下面是Spring Boot集成Swagger和OAuth2的代码实现和原理解释: 1. 添加Swagger依赖 在pom.xml中添加Swagger依赖: ```xml <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> ``` 2. 添加Swagger配置 在Spring Boot的配置类中添加Swagger配置: ```java @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } } ``` 3. 添加OAuth2依赖 在pom.xml中添加OAuth2依赖: ```xml <dependency> <groupId>org.springframework.security.oauth.boot</groupId> <artifactId>spring-security-oauth2-autoconfigure</artifactId> <version>2.1.4.RELEASE</version> </dependency> ``` 4. 添加OAuth2配置 在Spring Boot的配置类中添加OAuth2配置: ```java @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests().anyRequest().authenticated(); } } ``` 5. 添加Token鉴权 添加Token鉴权的方式是在控制器方法上添加@PreAuthorize注解,指定需要的角色或权限: ```java @RestController @RequestMapping("/api") public class ApiController { @GetMapping("/hello") @PreAuthorize("hasRole('ADMIN')") public String hello() { return "Hello World!"; } } ``` 6. 请求Token 使用OAuth2的客户端工具请求Token: ```java public class OAuth2Client { public static void main(String[] args) { Base64.Encoder encoder = Base64.getEncoder(); String clientCredentials = "client_id:client_secret"; String encodedClientCredentials = encoder.encodeToString(clientCredentials.getBytes()); String url = "http://localhost:8080/oauth/token"; HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); headers.add("Authorization", "Basic " + encodedClientCredentials); MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); map.add("grant_type", "client_credentials"); HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class); System.out.println(response.getBody()); } } ``` 以上就是Spring Boot集成Swagger和OAuth2的代码实现和原理解释。其中Swagger用于生成API文档,OAuth2用于保护API,Token鉴权用于限制访问API的角色或权限。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值