How to carry the token in the request header when testing the interface with swagger?

Time:2021-8-18

A question from a small partner on wechat:

How to carry the token in the request header when testing the interface with swagger?

Seeing this problem, brother song suddenly thought of the usage of spring boot + swagger I had written before:

  • Springboot integrates swagger2

The usage of oauth2 + JWT has also been written:

  • Want oauth2 and JWT to play happily together? Please watch brother song’s performance

However, the two have not been written together, so the partners have questions about this. Think about it. This is still a very common problem, because there are more and more scenarios using token login. In this case, if swagger is used to test the interface, how to carry the token in the request header? Today, brother song will come and have a chat with you.

1. Project planning

If the buddies brother did not read the OAuth2 series published by song Ge, they suggested that they should first look at the public (the official account of the southern part of the Yangtze River to get back to OAuth2 for the acquisition).

Here, SongGe builds an oauth2 + JWT environment for demonstration. There are two services:

service name port remarks
auth-server 8080 Authorization server
user-server 8081 Resource server

Let me explain a little:

  • Auth server is my resource server, which is used to issue JWT tokens.
  • User server is a resource server. To access the resources on user server, you need to carry a token.
  • Swagger is used to generate documents for the user server interface.

OK, this is a rough plan of our project.

2. Environmental construction

Next, let’s build the oauth2 test environment.

2.1 establishment of authorization server

First, we build an authorization service called auth server. When building, we select the following three dependencies:

  • Web
  • Spring Cloud Security
  • Spirng Cloud OAuth2

How to carry the token in the request header when testing the interface with swagger?

After the project is created, first provide a basic configuration of spring security:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("sang")
                .password(passwordEncoder().encode("123"))
                .roles("admin")
                .and()
                .withUser("javaboy")
                .password(passwordEncoder().encode("123"))
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().formLogin();
    }
}

In this code, in order to simplify the code, I will not save the spring security user in the database, but directly in memory.

Here I created a user named sang with the password of 123 and the role of admin. I also configured a form login.

The purpose of this configuration is actually to configure users. For example, if you want to log in to a third-party website with wechat, you have to log in to wechat first. To log in to wechat, you need your user name / password information. What we configure here is actually the user’s user name / password / role information.

It should be noted that in the current case, I will use the password mode in oauth2 to log in. Therefore, an AuthenticationManager bean needs to be explicitly provided here.

After the basic user information is configured, let’s configure the authorization server.

First, configure the tokenstore:

@Configuration
public class AccessTokenConfig {
    @Bean
    TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("javaboy");
        return converter;
    }
}
  1. Tokenstore we use the jwttokenstore instance. JWT and access are used_ In fact, tokens do not need to be stored (stateless login, and the server does not need to save information). Because all user information is in JWT, the jwttokenstore configured here is not stored in essence.
  2. In addition, we also provide a jwtaccesstokenconverter, which can convert user information into JWT string or extract user information from JWT string.
  3. In addition, when generating JWT strings, we need a signature, which needs to be saved by ourselves.

Next, configure the authorization server in detail:

@EnableAuthorizationServer
@Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
    @Autowired
    TokenStore tokenStore;
    @Autowired
    ClientDetailsService clientDetailsService;
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    JwtAccessTokenConverter jwtAccessTokenConverter;

    @Bean
    AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices services = new DefaultTokenServices();
        services.setClientDetailsService(clientDetailsService);
        services.setSupportRefreshToken(true);
        services.setTokenStore(tokenStore);
        services.setAccessTokenValiditySeconds(60 * 60 * 24 * 2);
        services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        services.setTokenEnhancer(tokenEnhancerChain);
        return services;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("javaboy")
                .secret(passwordEncoder.encode("123"))
                .resourceIds("res1")
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("all")
                .redirectUris("http://localhost:8082/index.html");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager)
                .tokenServices(tokenServices());
    }
}

This code is a little long. Let me explain it one by one:

  1. Create an authorizationserver class that inherits from the authorizationserverconfigureradapter to further configure the authorization server in detail. Remember to add the @ enableauthorizationserver annotation to the authorizationserver class to start the automatic configuration of the authorization server.
  2. In the authorizationserver class, we mainly override three configure methods.
  3. The authorization server securityconfigurer is used to configure the security constraints of the token endpoint, that is, who can access this endpoint and who cannot.
  4. Clientdetailsserviceconfigurer is used to configure the details of the client. In the previous article, brother Song told you that the authorization server needs to verify the client on the one hand and the user on the other. We have configured it earlier. Here is configuring the verification client. The client information can be stored in the database, which is actually easier. It is similar to the user information stored in the database. However, in order to simplify the code, I still store the client information in memory. Here, we configure the client ID, secret, resource ID, authorization type, authorization range and redirection URI respectively. In the previous article, I talked about four types of authorization, which do not include refresh_ Token is of this type, but in practice, refresh_ Token is also counted as a.
  5. Authorization server endpoints configurer is used here to configure the access endpoint of the token and the token service.
  6. The token services bean is mainly used to configure some basic information of the token, such as whether the token supports refresh, the storage location of the token, the validity period of the token and the validity period of the refresh token. It’s easy to understand the validity period of a token. Refresh the validity period of a token. When a token is about to expire, we need to get a new token. When we get a new token, we need a voucher information. This voucher information is not the old token, but another refresh_ Token, this refresh_ A token also has a valid period.

Well, after that, even if our authorization server is configured, let’s start the authorization server.

If the buddies brother are confused about the above configuration, they can reply to OAuth2 in the background of the official account. First learn the OAuth2 tutorial of the song systematically.

2.2 resource server setup

Next, we build a resource server. In the examples you can see on the Internet, most of the resource servers are placed together with the authorization server. If the project is relatively small, this is no problem, but if it is a large project, this is not appropriate.

The resource server is used to store users’ resources, such as your image, openid and other information on wechat. Users get access from the authorization server_ After token, you can use access_ Token to request data from the resource server.

We create a new spring boot project called user server as our resource server. When we create it, we add the following dependencies:

How to carry the token in the request header when testing the interface with swagger?

After the project is successfully created, copy the previous accesstokenconfig to the resource server, and then add the following configuration:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("res1").tokenStore(tokenStore);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("admin")
                .anyRequest().authenticated();
    }
}

This configuration code is very simple. Let me briefly say:

  1. First, configure the resource ID and tokenstore in the configure method. After configuration, jwtaccesstokenconverter will be automatically called to parse the JWT. The JWT contains the basic information of the user, so there is no need to remotely verify access_ Token.
  2. Finally, configure the resource interception rules, which is the basic writing method in spring security, and I won’t repeat it.

Next, we will configure two test interfaces:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
    @GetMapping("/admin/hello")
    public String admin() {
        return "admin";
    }
}

After that, our resource server is configured successfully.

2.3 testing

Start the authorization server and resource server respectively. First access the authorization server to obtain access_ token:

How to carry the token in the request header when testing the interface with swagger?

Reuse the access you get_ Token to access the resource server:

How to carry the token in the request header when testing the interface with swagger?

OK, no problem with the test.

3. Integrate swagger

Next, we add the swagger function to the user server. First, we add the swagger dependency:

 <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>

There are two dependencies added here, one is used to generate interface data, and the other swagger UI is used for data presentation.

3.1 certification mode I

Add parameters to the request header. Here are two kinds. Let’s look at the first one first.

First configure a docket instance as follows:

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("org.javaboy.oauth2.res.controller"))
                .paths(PathSelectors.any())
                .build()
                .securityContexts(Arrays.asList(securityContexts()))
                .securitySchemes(Arrays.asList(securitySchemes()))
                .apiInfo(new ApiInfoBuilder()
                        . description ("description information of interface document")
                        . title ("micro personnel project interface document")
                        .contact(new Contact("javaboy","http://www.javaboy.org","[email protected]"))
                        .version("v1.0")
                        .license("Apache2.0")
                        .build());
    }
    private SecurityScheme securitySchemes() {
        return new ApiKey("Authorization", "Authorization", "header");
    }

    private SecurityContext securityContexts() {
        return SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .forPaths(PathSelectors.any())
                        .build();
    }

    private List<SecurityReference> defaultAuth() {
        Authorizationscope authorizationscope = new authorizationscope ("XXX", "description");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return Arrays.asList(new SecurityReference("Authorization", authorizationScopes));
    }
}

The configuration here is a little long. Let me explain to you:

  • First, enable swagger2 through the @ enableswagger2 annotation.
  • Configure a docket bean. In this bean, configure the mapping path and the location of the interface to be scanned.
  • In apiinfo, mainly configure the swagger2 document website information, such as website title, website description, contact information, protocol used, etc.
  • Configure the global parameters through securityschemes. The configuration here is a request header called authorization (the request header to be carried in oauth2).
  • Securitycontexts is used to configure which requests need to carry tokens. Here we configure all requests.

After configuration, we also need to release swagger UI, otherwise the static resources related to swagger UI will be intercepted by spring security:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/swagger-ui.html")
                .antMatchers("/webjars/**")
                .antMatchers("/v2/**")
                .antMatchers("/swagger-resources/**");
    }
}

After configuration, restart the user server and enter http://localhost:8081/swagger -Ui.html, the result is as follows:

How to carry the token in the request header when testing the interface with swagger?

You can see that there is an authorize button in the page. Click this button and enterBearer ${token}, as follows:

How to carry the token in the request header when testing the interface with swagger?

After input, click the authorize button to complete the authentication. Next, various interfaces in the user server can directly call the test.

The above method is more general. It is not only applicable to oauth2, but also applicable to other user-defined token login methods.

However, this method requires developers to obtain access through other ways first_ Token, some people will find it a little troublesome. Is there a better way? Look at mode 2.

3.2 certification mode II

The second authentication method is to directly fill in the authentication information in swagger, so there is no need to obtain access from the outside_ Token. The effect is as follows:

How to carry the token in the request header when testing the interface with swagger?
How to carry the token in the request header when testing the interface with swagger?

Let’s see how this is configured.

Due to swagger to request/oauth/tokenThe interface will cross domains, so we must first modify auth server to support cross domains:

There are mainly two modifications. The first is to configure corsfilter to allow cross domain, as follows:

@Configuration
public class GlobalCorsConfiguration {
    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }
}

Then enable cross domain support in securityconfig:

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
    ...
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .requestMatchers().antMatchers(HttpMethod.OPTIONS, "/oauth/**")
                .and()
                .csrf().disable().formLogin()
                .and()
                .cors();
    }
}

After these two steps of configuration, the cross domain support of the server is enabled.

Next, we modify the definition of docket bean in user server:

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("org.javaboy.oauth2.res.controller"))
                .paths(PathSelectors.any())
                .build()
                .securityContexts(Arrays.asList(securityContext()))
                .securitySchemes(Arrays.asList(securityScheme()))
                .apiInfo(new ApiInfoBuilder()
                        . description ("description information of interface document")
                        . title ("micro personnel project interface document")
                        .contact(new Contact("javaboy","http://www.javaboy.org","[email protected]"))
                        .version("v1.0")
                        .license("Apache2.0")
                        .build());
    }

    private AuthorizationScope[] scopes() {
        return new AuthorizationScope[]{
                new AuthorizationScope("all", "all scope")
        };
    }

    private SecurityScheme securityScheme() {
        GrantType grant = new ResourceOwnerPasswordCredentialsGrant("http://localhost:8080/oauth/token");
        return new OAuthBuilder().name("OAuth2")
                .grantTypes(Arrays.asList(grant))
                .scopes(Arrays.asList(scopes()))
                .build();
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(Arrays.asList(new SecurityReference("OAuth2", scopes())))
                .forPaths(PathSelectors.any())
                .build();
    }
}

This configuration is similar to the previous one, mainly because the securityscheme is different. Oauthbuilder is used to build here, and the access address of token must be configured during construction.

OK, after the configuration is completed, restart auth server and user server for testing. The test effect is the picture given by brother song. I won’t repeat it.

The biggest advantage of this method is that there is no need to obtain access through other ways_ Token, you can directly enter the authentication parameters of password mode on the swagger UI page. Very convenient, limited to oauth2 mode.

4. Summary

Well, today I’ll introduce to my friends how to modify the request header in the swagger request. Interested friends can come down and have a try

Download address of this case: https://github.com/lenve/spring-security-samples

Well, if you feel you have something to gain, remember to watch and encourage brother song

Recommended Today

Heavy! With the release of JDK 17, Oracle announced that JDK 17 is officially free..

Previous edition:JDK 16 was officially released and 17 new features were released at one time… No! JDK 17 officially released + free It’s awesome. JDK 16 has just been released for half a year (2021 / 03 / 16), and JDK 17 arrives as scheduled (2021 / 09 / 14). At this time, it’s awesome. […]