如何在Spring Boot Rest应用程序中使用Swagger ui配置带有密码流的oAuth2 [英] How to configure oAuth2 with password flow with Swagger ui in spring boot rest application

查看:99
本文介绍了如何在Spring Boot Rest应用程序中使用Swagger ui配置带有密码流的oAuth2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有使用另一台Spring Boot授权服务器的spring boot rest api(资源),我已经将Swagger配置添加到资源应用程序中,以获取一个不错的,快速的rest API文档/测试平台.我的Swagger配置看起来像这样:

  @Configuration@ EnableSwagger2公共类SwaggerConfig {@Autowired私人TypeResolver typeResolver;@Value("$ {app.client.id}")私有字符串clientId;@Value("$ {app.client.secret}")私有String clientSecret;@Value("$ {info.build.name}")私有字符串infoBuildName;public static final String securitySchemaOAuth2 ="oauth2";public static final String授权ScopeGlobal ="global";public static final StringauthorizationScopeGlobalDesc ="accessEverything";@豆角,扁豆公共Docket api(){列表< ResponseMessage>list = new java.util.ArrayList< ResponseMessage>();list.add(new ResponseMessageBuilder().code(500).message("500条消息").responseModel(new ModelRef("JSONResult«string»")).建造());list.add(new ResponseMessageBuilder().code(401).message(未经授权").responseModel(new ModelRef("JSONResult«string»")).建造());返回新的Docket(DocumentationType.SWAGGER_2).选择().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).建造().securitySchemes(Collections.singletonList(securitySchema())).securityContexts(Collections.singletonList(securityContext())).pathMapping("/").directModelSubstitute(LocalDate.class,String.class).genericModelSubstitutes(ResponseEntity.class).alternateTypeRules(newRule(typeResolver.resolve(DeferredResult.class,typeResolver.resolve(ResponseEntity.class,WildcardType.class)),typeResolver.resolve(WildcardType.class))).useDefaultResponseMessages(false).apiInfo(apiInfo()).globalResponseMessage(RequestMethod.GET,列表).globalResponseMessage(RequestMethod.POST,list);}私有OAuth securitySchema(){列表< AuthorizationScope>authorizationScopeList = newArrayList();AuthorizationScopeList.add(new AuthorizationScope("global",全部访问"));List< GrantType>grantTypes = newArrayList();最后的TokenRequestEndpoint tokenRequestEndpoint = new TokenRequestEndpoint("http://server:port/oauth/token",clientId,clientSecret);最终的TokenEndpoint tokenEndpoint =新的TokenEndpoint("http://server:port/oauth/token","access_token");AuthorizationCodeGrantauthorizationCodeGrant =新的AuthorizationCodeGrant(tokenRequestEndpoint,tokenEndpoint);grantTypes.add(authorizationCodeGrant);OAuth oAuth =新的OAuth("oauth",authorizationScopeList,grantTypes);返回oAuth;}私人SecurityContext securityContext(){返回SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.ant("/api/**")).build();}私有列表< SecurityReference>defaultAuth(){最终的AuthorizationScope授权范围=新的AuthorizationScope(authorizationScopeGlobal,authorizationScopeGlobalDesc);final AuthorizationScope []授权范围=新的AuthorizationScope [1];授权范围[0] =授权范围;返回收藏.singletonList(new SecurityReference(securitySchemaOAuth2,authorizationScopes));}私人ApiInfo apiInfo(){返回新的ApiInfoBuilder().title(我的其余API").description(描述在这里…").termsOfServiceUrl("https://www.example.com/").contact(新联系人("XXXX XXXX","http://www.example.com","xxxx@example.com")).license(此处的许可证").licenseUrl("https://www.example.com").version("1.0.0").建造();}} 

我从授权服务器获取访问令牌的方式是使用http POST到具有id的clientid/clientpass标头中的基本授权的链接:

  http://server:port/oauth/token?grant_type = password& username =<用户名>& password =<密码> 

响应类似于:

  {"access_token":"e3b98877-f225-45e2-add4-3c53eeb6e7a8","token_type":承载者","refresh_token":"58f34753-7695-4a71-c08a-d40241ec3dfb","expires_in":4499,"scope":读信任写"} 

在Swagger用户界面中,我可以看到一个授权"按钮,该按钮会打开一个对话框以发出授权请求,但该对话框无法正常工作,并将我定向到如下所示的链接,

  http://server:port/oauth/token?response_type = code& redirect_uri = http%3A%2F%2Fserver%3A8080%2Fwebjars%2Fspringfox-swagger-ui%2Fo2c.html& realm = undefined&client_id = undefined& scope = global%2CvendorExtensions& state = oauth 

我在这里想念什么?

解决方案

8个月后,终于在Swagger UI中支持了密码流,这是对我有用的最终代码和设置:

1)Swagger配置:

  package com.example.api;导入org.springframework.beans.factory.annotation.Value;导入org.springframework.context.annotation.Bean;导入org.springframework.context.annotation.Configuration;导入org.springframework.web.bind.annotation.RequestMethod;导入springfox.documentation.schema.ModelRef;导入springfox.documentation.service.ApiInfo;导入springfox.documentation.service.AuthorizationScope;导入springfox.documentation.service.Contact;导入springfox.documentation.service.GrantType;导入springfox.documentation.service.OAuth;导入springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant;导入springfox.documentation.service.ResponseMessage;导入springfox.documentation.service.SecurityReference;导入springfox.documentation.builders.ApiInfoBuilder;导入springfox.documentation.builders.PathSelectors;导入springfox.documentation.builders.RequestHandlerSelectors;导入springfox.documentation.builders.ResponseMessageBuilder;导入springfox.documentation.spi.DocumentationType;导入springfox.documentation.spi.service.contexts.SecurityContext;导入springfox.documentation.spring.web.plugins.Docket;导入springfox.documentation.swagger.web.ApiKeyVehicle;导入springfox.documentation.swagger.web.SecurityConfiguration;导入springfox.documentation.swagger2.annotations.EnableSwagger2;导入java.util.Collections;导入java.util.List;导入静态com.google.common.collect.Lists.*;@配置@ EnableSwagger2公共类SwaggerConfig {@Value("$ {app.client.id}")私有字符串clientId;@Value("$ {app.client.secret}")私有String clientSecret;@Value("$ {info.build.name}")私有字符串infoBuildName;@Value("$ {host.full.dns.auth.link}")私有字符串authLink;@豆角,扁豆公共Docket api(){列表< ResponseMessage>list = new java.util.ArrayList<>();list.add(新的ResponseMessageBuilder().code(500).message("500条消息").responseModel(new ModelRef("Result")).build());list.add(新的ResponseMessageBuilder().code(401).message(未经授权").responseModel(new ModelRef("Result")).build());list.add(new ResponseMessageBuilder().code(406).message(不可接受").responseModel(new ModelRef("Result")).build());返回新的Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build().securitySchemes(Collections.singletonList(securitySchema())).securityContexts(Collections.singletonList(securityContext())).pathMapping("/").useDefaultResponseMessages(false).apiInfo(apiInfo()).globalResponseMessage(RequestMethod.GET,列表).globalResponseMessage(RequestMethod.POST,list);}私有OAuth securitySchema(){列表< AuthorizationScope>authorizationScopeList = newArrayList();AuthorizationScopeList.add(new AuthorizationScope("read","read all")));AuthorizationScopeList.add(new AuthorizationScope("trust",全部信任"));AuthorizationScopeList.add(new AuthorizationScope("write","access all")));List< GrantType>grantTypes = newArrayList();GrantType creGrant = new ResourceOwnerPasswordCredentialsGrant(authLink +"/oauth/token");grantTypes.add(creGrant);返回新的OAuth("oauth2schema",authorizationScopeList,grantTypes);}私人SecurityContext securityContext(){返回SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.ant("/user/**")).建造();}私有列表< SecurityReference>defaultAuth(){最终的AuthorizationScope []授权范围=新的AuthorizationScope [3];authorizationScopes [0] =新的AuthorizationScope("read","read all");authorizationScopes [1] =新的AuthorizationScope("trust","trust all");authorizationScopes [2] =新的AuthorizationScope("write","write all");返回Collections.singletonList(new SecurityReference("oauth2schema",authorizationScopes));}@豆角,扁豆公共SecurityConfiguration securityInfo(){返回新的SecurityConfiguration(clientId,clientSecret,",",",ApiKeyVehicle.HEADER,",");}私人ApiInfo apiInfo(){返回新的ApiInfoBuilder().title(我的API标题").description(").termsOfServiceUrl("https://www.example.com/api").contact(new Contact("Hasson","http://www.example.com","hasson@example.com")).license("Open Source").licenseUrl("https://www.example.com").version("1.0.0").build();}} 

2)在POM中使用此Swagger UI版本2.7.0:

 < dependency>< groupId> io.springfox</groupId>< artifactId> springfox-swagger2</artifactId>< version> 2.7.0</version></dependency><依赖关系>< groupId> io.springfox</groupId>< artifactId> springfox-swagger-ui</artifactId>< version> 2.7.0</version></dependency><依赖关系>< groupId> io.springfox</groupId>< artifactId> springfox-bean-validators</artifactId>< version> 2.7.0</version></dependency> 

3)在application.properties中添加以下属性:

  host.full.dns.auth.link = http://oauthserver.example.com:8081app.client.id =测试客户端app.client.secret =客户端秘密auth.server.schem = http 

4)在授权服务器中添加CORS过滤器:

 程序包com.example.api.oauth2.oauth2server;导入org.slf4j.Logger;导入org.slf4j.LoggerFactory;导入org.springframework.stereotype.Component;导入javax.servlet.Filter;导入javax.servlet.FilterChain;导入javax.servlet.FilterConfig;导入javax.servlet.ServletException;导入javax.servlet.ServletRequest;导入javax.servlet.ServletResponse;导入javax.servlet.http.HttpServletResponse;导入java.io.IOException;/***允许跨源使用本地文件中的swagger-ui测试swagger文档* 系统*/@成分公共类CrossOriginFilter实现了过滤器{私有静态最终Logger日志= LoggerFactory.getLogger(CrossOriginFilter.class);@Override公共无效init(FilterConfig filterConfig)抛出ServletException {//由Web容器调用以指示过滤器正在//投入使用.//我们不想在这里做任何事情.}@Override公共无效doFilter(ServletRequest请求,ServletResponse响应,FilterChain链)引发IOException,ServletException {log.info(正在应用CORS过滤器");HttpServletResponse响应=(HttpServletResponse)resp;response.setHeader("Access-Control-Allow-Origin","*");response.setHeader("Access-Control-Allow-Methods","POST,GET,OPTIONS,DELETE");response.setHeader("Access-Control-Max-Age","0");chain.doFilter(req,resp);}@Override公共无效destroy(){//由Web容器调用以指示过滤器正在//退出服务.//我们不想在这里做任何事情.}} 

如果使用这些设置运行,您将在链接

然后,当您单击授权按钮时,您将获得以下对话框,添加您的用户名/密码以及客户端ID和客户端密码的数据,类型必须是请求正文,我不确定为什么,但这是适用于我的方法,尽管我认为应该是基本身份验证,因为这是发送客户端机密的方式,但是无论如何,这都是Swagger-ui与密码流一起工作的方式,并且您所有的API端点都可以再次工作.开心地大摇大摆!!!:)

I have spring boot rest api (resources) which uses another spring boot authorisation server, I have added Swagger config to the resource application to get a nice and quick documentation/test platform for the rest API. my Swagger config looks like this:

@Configuration
@EnableSwagger2
public class SwaggerConfig {    

    @Autowired
    private TypeResolver typeResolver;

    @Value("${app.client.id}")
    private String clientId;
    @Value("${app.client.secret}")
    private String clientSecret;
    @Value("${info.build.name}")
    private String infoBuildName;

    public static final String securitySchemaOAuth2 = "oauth2";
    public static final String authorizationScopeGlobal = "global";
    public static final String authorizationScopeGlobalDesc = "accessEverything";

    @Bean
    public Docket api() { 

        List<ResponseMessage> list = new java.util.ArrayList<ResponseMessage>();
        list.add(new ResponseMessageBuilder()
                .code(500)
                .message("500 message")
                .responseModel(new ModelRef("JSONResult«string»"))
                .build());
        list.add(new ResponseMessageBuilder()
                .code(401)
                .message("Unauthorized")
                .responseModel(new ModelRef("JSONResult«string»"))
                .build());


        return new Docket(DocumentationType.SWAGGER_2)  
          .select()                                  
          .apis(RequestHandlerSelectors.any())              
          .paths(PathSelectors.any())     
          .build()
          .securitySchemes(Collections.singletonList(securitySchema()))
          .securityContexts(Collections.singletonList(securityContext()))
          .pathMapping("/")
          .directModelSubstitute(LocalDate.class,String.class)
          .genericModelSubstitutes(ResponseEntity.class)
          .alternateTypeRules(
              newRule(typeResolver.resolve(DeferredResult.class,
                      typeResolver.resolve(ResponseEntity.class, WildcardType.class)),
                  typeResolver.resolve(WildcardType.class)))
          .useDefaultResponseMessages(false)
          .apiInfo(apiInfo())
          .globalResponseMessage(RequestMethod.GET,list)
          .globalResponseMessage(RequestMethod.POST,list);
    }


    private OAuth securitySchema() {

        List<AuthorizationScope> authorizationScopeList = newArrayList();
        authorizationScopeList.add(new AuthorizationScope("global", "access all"));

        List<GrantType> grantTypes = newArrayList();
        final TokenRequestEndpoint tokenRequestEndpoint = new TokenRequestEndpoint("http://server:port/oauth/token", clientId, clientSecret);
        final TokenEndpoint tokenEndpoint = new TokenEndpoint("http://server:port/oauth/token", "access_token");
        AuthorizationCodeGrant authorizationCodeGrant = new AuthorizationCodeGrant(tokenRequestEndpoint, tokenEndpoint);

        grantTypes.add(authorizationCodeGrant);

        OAuth oAuth = new OAuth("oauth", authorizationScopeList, grantTypes);

        return oAuth;
    }


    private SecurityContext securityContext() {
        return SecurityContext.builder().securityReferences(defaultAuth())
                .forPaths(PathSelectors.ant("/api/**")).build();
    }

    private List<SecurityReference> defaultAuth() {

        final AuthorizationScope authorizationScope =
                new AuthorizationScope(authorizationScopeGlobal, authorizationScopeGlobalDesc);
        final AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Collections
                .singletonList(new SecurityReference(securitySchemaOAuth2, authorizationScopes));
    }



    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("My rest API")
                .description(" description here … ")
                .termsOfServiceUrl("https://www.example.com/")
                .contact(new Contact("XXXX XXXX",
                                     "http://www.example.com", "xxxx@example.com"))
                .license("license here")
                .licenseUrl("https://www.example.com")
                .version("1.0.0")
                .build();
    }

}

The way I get the access token from the Authorisation server is by using http POST to this link with basic authorisation in the header for clientid/clientpass:

http://server:port/oauth/token?grant_type=password&username=<username>&password=<password>

the response is something like:

{
    "access_token": "e3b98877-f225-45e2-add4-3c53eeb6e7a8",
    "token_type": "bearer",
    "refresh_token": "58f34753-7695-4a71-c08a-d40241ec3dfb",
    "expires_in": 4499,
    "scope": "read trust write"
}

in Swagger UI I can see an Authorisation button, which opens a dialog to make the authorisation request, but it is not working and directing me to a link as following,

http://server:port/oauth/token?response_type=code&redirect_uri=http%3A%2F%2Fserver%3A8080%2Fwebjars%2Fspringfox-swagger-ui%2Fo2c.html&realm=undefined&client_id=undefined&scope=global%2CvendorExtensions&state=oauth

what I am missing here?

解决方案

After 8 months, finally the password flow is supported in Swagger UI, here is the final code and settings which works for me:

1) Swagger Config:

package com.example.api;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.GrantType;
import springfox.documentation.service.OAuth;
import springfox.documentation.service.ResourceOwnerPasswordCredentialsGrant;
import springfox.documentation.service.ResponseMessage;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.builders.ResponseMessageBuilder;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.ApiKeyVehicle;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.Collections;
import java.util.List;

import static com.google.common.collect.Lists.*;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Value("${app.client.id}")
    private String clientId;
    @Value("${app.client.secret}")
    private String clientSecret;
    @Value("${info.build.name}")
    private String infoBuildName;

    @Value("${host.full.dns.auth.link}")
    private String authLink;

    @Bean
    public Docket api() {

        List<ResponseMessage> list = new java.util.ArrayList<>();
        list.add(new ResponseMessageBuilder().code(500).message("500 message")
                .responseModel(new ModelRef("Result")).build());
        list.add(new ResponseMessageBuilder().code(401).message("Unauthorized")
                .responseModel(new ModelRef("Result")).build());
        list.add(new ResponseMessageBuilder().code(406).message("Not Acceptable")
                .responseModel(new ModelRef("Result")).build());

        return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any()).build().securitySchemes(Collections.singletonList(securitySchema()))
                .securityContexts(Collections.singletonList(securityContext())).pathMapping("/")
                .useDefaultResponseMessages(false).apiInfo(apiInfo()).globalResponseMessage(RequestMethod.GET, list)
                .globalResponseMessage(RequestMethod.POST, list);



    }

    private OAuth securitySchema() {

        List<AuthorizationScope> authorizationScopeList = newArrayList();
        authorizationScopeList.add(new AuthorizationScope("read", "read all"));
        authorizationScopeList.add(new AuthorizationScope("trust", "trust all"));
        authorizationScopeList.add(new AuthorizationScope("write", "access all"));

        List<GrantType> grantTypes = newArrayList();
        GrantType creGrant = new ResourceOwnerPasswordCredentialsGrant(authLink+"/oauth/token");

        grantTypes.add(creGrant);

        return new OAuth("oauth2schema", authorizationScopeList, grantTypes);

    }

    private SecurityContext securityContext() {
        return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.ant("/user/**"))
                .build();
    }

    private List<SecurityReference> defaultAuth() {

        final AuthorizationScope[] authorizationScopes = new AuthorizationScope[3];
        authorizationScopes[0] = new AuthorizationScope("read", "read all");
        authorizationScopes[1] = new AuthorizationScope("trust", "trust all");
        authorizationScopes[2] = new AuthorizationScope("write", "write all");

        return Collections.singletonList(new SecurityReference("oauth2schema", authorizationScopes));
    }

    @Bean
    public SecurityConfiguration securityInfo() {
        return new SecurityConfiguration(clientId, clientSecret, "", "", "", ApiKeyVehicle.HEADER, "", " ");
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("My API title").description("")
                .termsOfServiceUrl("https://www.example.com/api")
                .contact(new Contact("Hasson", "http://www.example.com", "hasson@example.com"))
                .license("Open Source").licenseUrl("https://www.example.com").version("1.0.0").build();
    }

}

2) in POM use this Swagger UI version 2.7.0:

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.7.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.7.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-bean-validators</artifactId>
        <version>2.7.0</version>
    </dependency>

3) in the application.properties add the following properties:

host.full.dns.auth.link=http://oauthserver.example.com:8081
app.client.id=test-client
app.client.secret=clientSecret
auth.server.schem=http

4) in the Authorisation server add a CORS filter:

package com.example.api.oauth2.oauth2server;

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

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Allows cross origin for testing swagger docs using swagger-ui from local file
 * system
 */
@Component
public class CrossOriginFilter implements Filter {
    private static final Logger log = LoggerFactory.getLogger(CrossOriginFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        // Called by the web container to indicate to a filter that it is being
        // placed into service.
        // We do not want to do anything here.
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        log.info("Applying CORS filter");
        HttpServletResponse response = (HttpServletResponse) resp;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "0");
        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() {

        // Called by the web container to indicate to a filter that it is being
        // taken out of service.
        // We do not want to do anything here.
    }
}

If you run with these settings you will get the authorize button in the link http://apiServer.example.com:8080/swagger-ui.html#/ (if you run on 8080) as follows:

Then when you click on the authorize button you will get the following dialogue, add the data for your username/password and the client id and the client secret, the type has to be request body, I am not sure why but this is what works with me, although I thought it should be basic auth as this is how the client secret is sent, anyway this is how Swagger-ui works with password flow and all your API endpoints are working again. Happy swaggering!!! :)

这篇关于如何在Spring Boot Rest应用程序中使用Swagger ui配置带有密码流的oAuth2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆