Swagger UI accessible only for authenticated users or user has authority (or role)
See original GitHub issueHi, I am pretty new with swagger and springfox.
Version of springfox-swagger2
and springfox-swagger-ui
is 2.8.0
.
Requirements:
- Secure Swagger UI and allow only specific role (Authority)
My application is currently using JWT Tokens for authentication and authorization and it is working fine. My Swagger UI is accessable publicly, which I do not want to expose when my application is deployed in production. Only specific users based on role/authority can access Swagger UI page.
I can now add JWT token via Authorize
button, then I can see the Authorize
icon button to change to Locked
. Once the icon is locked, Try it out
works and the JWT token is passed in the header as Bearer
access token. Once refreshed… the access token is reset.
Not sure if I can use the existing spring security configurations of my application and apply it to swagger-ui. I already did, but with no success (see configuration below). I can manage to set swagger-ui accessable to the public (using permitAll()
or web ignore configuration
), but setting it to allow authenticated user (very basic), it wont’t work… how much more if I wanted to allow specific user role or authority to access swagger-ui.
I would like to start with securing swagger-ui
to allow authenticated users (authenticated on my application) only, then later on specifying role/authority.
btw, how do I label this as help wanted
?
Thanks!
Dependency (pom.xml):
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>2.8.0</version>
</dependency>
Security Configuration class
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
@Override
protected void configure(HttpSecurity http) throws Exception {
// I am use JWT authentication and authorization to API
JWTAuthenticationFilter authenticationFilter =
new JWTAuthenticationFilter(authenticationManager(), appContext);
authenticationFilter.setFilterProcessesUrl("/auth/form");
JWTAuthorizationFilter authorizationFilter =
new JWTAuthorizationFilter(authenticationManager(), appContext);
http
.cors().and().csrf().disable() // no need CSRF since JWT based authentication
.authorizeRequests()
...
// If I use permitAll(), swagger-ui will be accessible,
// even web ignore configuration below is not used
//.antMatchers("/swagger-ui.html**").permitAll()
// This won't work even if I remove web ignore configuration below
// I want to start with allowing authenticated users only
// Using this will make swagger-ui not accessible
//.antMatchers("/swagger-ui.html**").authenticated()
// Later on, I want to allow specific role or authorization
//.antMatchers("/swagger-ui.html**").hasAnyAuthority(
// MyConstants.RoleType.ADMINISTRATOR.toString(),
// MyConstants.RoleType.DEVELOPER.toString())
...
.anyRequest().authenticated()
.and()
.addFilter(authenticationFilter)
.addFilter(authorizationFilter)
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().exceptionHandling().authenticationEntryPoint(new MyAuthenticationEntryPoint());
}
// This is what I used to make swagger be accessable publicly
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/", "/v2/api-docs", "/configuration/ui", "/swagger-resources/**",
"/configuration/**", "/swagger-ui.html", "/webjars/**");
}
...
}
MyAuthenticationEntryPoint
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger logger = LoggerFactory.getLogger(MyAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AuthenticationException e) {
logger.debug("Pre-authenticated entry point called. Rejecting access");
List<Message> errorMessagesList = Arrays.asList(new Message("Unauthorized access"));
CommonErrorResponse commonErrorResponse =
new CommonErrorResponse(errorMessagesList, HttpServletResponse.SC_UNAUTHORIZED);
try {
String json = Util.objectToJsonString(commonErrorResponse);
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpServletResponse.setContentType(MediaType.APPLICATION_JSON_VALUE);
httpServletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
httpServletResponse.getWriter().write(json);
} catch (Exception e1) {
logger.error("Unable to process json response: " + e1.getMessage());
}
}
}
Swagger Configuration
@EnableSwagger2
@Configuration
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(metadata())
.select()
.apis(RequestHandlerSelectors.basePackage("com.iyotbihagay.controller"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(apiKey()))
.securityContexts(Collections.singletonList(securityContext()))
.useDefaultResponseMessages(false);
}
@Bean
SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId(null)
.clientSecret(null)
.realm("My Realm")
.appName("My System")
.scopeSeparator(null)
.additionalQueryStringParams(null)
.useBasicAuthenticationWithAccessCodeGrant(false)
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Collections.singletonList(new SecurityReference("JWT Token", authorizationScopes));
}
private ApiInfo metadata() {
return new ApiInfoBuilder().title("Iyot Bihagay API Documentation")
.description("API documentation for Iyot Bihagay REST Services.").version("1.6.9").build();
}
private ApiKey apiKey() {
return new ApiKey("Authorization", "Authorization", "header");
}
private SecurityContext securityContext() {
// Optional swagger-ui security configuration for oauth and apiKey settings
return SecurityContext.builder()
.securityReferences(defaultAuth())
// Selector for the paths this security context applies to.
// Honestly, I don't understand this... should I place my API endpoints here? like `/api/users/**`
.forPaths(PathSelectors.regex("/anyPath.*"))
.build();
}
}
On every API (Controller), add authorizations = { @Authorization(value="JWT Token") }
like:
@ApiOperation(value = "Creates an inquiry type", authorizations = { @Authorization(value="JWT Token") })
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public Object create(@Valid @RequestBody InquiryType inquiryType, BindingResult bindingResult) throws Exception {
...
}
Issue Analytics
- State:
- Created 5 years ago
- Comments:9 (2 by maintainers)
Top GitHub Comments
@dilipkrish Any news how to handle this?
@dilipkrish thanks for the update. I figured out the issue was with the path regex that i was providing. I am not sure how that regex was evaluated. But i got it to work now after trying a couple of combinations. (y)