I am using trying to implement swagger-ui using SecurityFilterChain in Spring-Boot.
I have tried various ways to get this working but everytime I am getting the below error.
Unauthorized error: Full authentication is required to access this resource
I am using springfox 3.0
Adding the code for swagger config and security config below.
SecurityConfig.java
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
//new BCryptPasswordEncoder();
}
#Bean
#Order(2)
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests((authorize) -> authorize
.antMatchers("/swagger-ui/**").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/swagger-ui.html").permitAll()
.anyRequest()
.authenticated());
http.cors().configurationSource(new CorsConfigurationSource() {
#Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedHeaders(Collections.singletonList("*"));
config.setAllowedMethods(Collections.singletonList("*"));
config.addAllowedOrigin("*");
config.setAllowCredentials(true);
return config;
}
});
http.authenticationProvider(authenticationProvider());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
SwaggerConfig.java
public class SwaggerConfig {
public static final String AUTHORIZATION_HEADER = "Authorization";
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
private ApiInfo apiInfo() {
return new ApiInfo("Spring Boot ", "Spring Boot Doc", "716",
"Terms of service", new Contact("Test", "www.xyz.com", "test#gmail.com"),
"License of API", "API license URL", Collections.emptyList());
}
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo())
.securityContexts(Arrays.asList(securityContext())).securitySchemes(Arrays.asList(apiKey())).select()
.apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build();
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
}
}
try web.ignoring()
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/proxy/**");
}
This is my code i have added to my webSecurityConfiguration
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/v2/api-docs/**", "/swagger-ui/**", "/swagger-resources/**", "/v2/api-docs/**");
}
I had the same problem.
It was fixed when I added the following.
...antMatchers("/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/swagger-resources", "/v3/api-docs/**", "/proxy/**").permitAll()...
SecurityFilterChain Bean in My Security Class
#Bean
public SecurityFilterChain configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/swagger-ui.html", "/swagger-ui/**", "/swagger-resources/**", "/swagger-resources", "/v3/api-docs/**", "/proxy/**").permitAll()
.anyRequest().authenticated()
.and().exceptionHandling()
.authenticationEntryPoint(authEntryPoint)
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(roleConverter);
return http.build();
}
springdoc dependency for Swagger-UI
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
If you have a different filter method(like OncePerRequestFilter) during the request, you should make sure you don't get an error there.
Try this in your SecurityConfig.java
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/**",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/swagger-ui/**",
"/webjars/**");
}
I have some issue regarding crossorigin and http security in my spring boot application.
I want to use http security when I annotate a method with #crossorigin in my controller class.
But it doesnt seams to work, the security is always triggered even if the method is not using #crosorigin.
Is it possible to fix this?
Jwtautoconfig class:
#ManagementContextConfiguration
#ConditionalOnProperty(name = {"af.security.active"}, havingValue = "true")
#Import({EnvironmentConfig.class, JwkRepository.class, JwtTokenUtil.class,
JwtAuthenticationProvider.class})
#EnableWebSecurity
#EnableConfigurationProperties(JwtSecurityProperties.class)
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class JwtAutoConfig extends WebSecurityConfigurerAdapter {
#Value("${af.security.jwt.white-list}")
private String[] ignoredPaths;
#Value("${af.security.job-seeker-role:arbetssökande}")
private String jobSeekerRole;
#Value("${af.security.officer-role:handläggare}")
private String officer;
#Bean(name = "jwtauthenticationentrypoint")
public JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint() {
return new JwtAuthenticationEntryPoint();
}
#Bean
public JwtSecurityHelper securityHelper(){
return new JwtSecurityHelper(jobSeekerRole, officer);
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManager());
authenticationTokenFilter.setAuthenticationSuccessHandler(new JwtAuthenticationSuccessHandler());
return authenticationTokenFilter;
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/**")
.authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable();
// Custom JWT based security filter
http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
http.headers().cacheControl();
}
#Override
public void configure(WebSecurity web) {
final String[] trimmedIgnoredPaths = Stream.of(ignoredPaths)
.map(String::trim)
.toArray(String[]::new);
web.ignoring()
.antMatchers(HttpMethod.OPTIONS,"/**")
.and()
.ignoring().antMatchers(trimmedIgnoredPaths);
}
private Config hazelCastConfig(){
Config config = new Config();
config.setInstanceName("app-cache")
.setNetworkConfig(new NetworkConfig()
.setJoin(new JoinConfig()
.setMulticastConfig(new MulticastConfig()
.setEnabled(false)
)
)
)
.addMapConfig(
new MapConfig()
.setName("object-cache")
.setMaxSizeConfig(new MaxSizeConfig(10, MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
.setEvictionPolicy(EvictionPolicy.LRU)
.setStatisticsEnabled(true)
.setTimeToLiveSeconds(14400));
return config;
}
#Bean(name="hazelcast")
public HazelcastInstance hazelcastInstance() {
HazelcastInstance hazelcastInstance = new HazelcastInstanceFactory(hazelCastConfig()).getHazelcastInstance();
return hazelcastInstance;
}
}
CorsConfig class:
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "DELETE", "GET" )
.allowCredentials(true);
}
};
}
}
And this is the method in my controller class:
#ApiOperation(value = "Hämtar alla frånvaron för en lista med användare")
#PostMapping(path= "/hamta-alla-franvaron", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<ExternalFranvaroDTO>> hamtaAllaFranvaron(
#ApiParam(value = "Identitet objekt som innehåller en lista av PISA_ID", required = true)
#Valid #RequestBody IdentitetForm identitet){
logger.info("MOTTAGET Rest-anrop (/hamta-alla-franvaron) Hamtar alla franvaron");
List<ExternalFranvaroDTO> externalFranvaroDTOLista = new ArrayList<>();
List<Franvaro> franvaron = franvaroService.hamtaAllaPagaendeOchNyaFriskskrivnaFranvaron(identitet.getPisaIds());
if(franvaron.isEmpty()) {
logger.debug("Inga pågende sjuk/vab anmälan");
return ResponseEntity.noContent().build();
}
franvaron.forEach( franvaro -> {
ExternalFranvaroDTO externalFranvaroDTO = transformeraTillExternalFranvaroDTO(franvaro);
externalFranvaroDTOLista.add(externalFranvaroDTO);
});
return ResponseEntity.ok().body(externalFranvaroDTOLista);
}
Now I want to only use http security when I use #crossorigin
I don't see any reason as to why you would like to combine it like this.
You should instead apply security to specific endpoints and configure a cors filter in spring security instead of setting it globally as you have done.
If you read the spring security documentation under HttpSecurity you can use the antMatcher and and match endpoints using ant syntax
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatcher( // Here you can define endpoints using ant matching
"**/foo/**",
"**/bar/**"
)
.authenticated()
)
... // rest of configuration
}
You can also define a CORS filter using spring security
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults())
...
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
You can even activate and use the built in jwt filter and customize that filter too with your own converter etc. etc.
protected void configure(HttpSecurity http) {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
}
// or add a custom converter
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
// adding a custom converter here
.jwtAuthenticationConverter(myConverter())
)
);
The spring security documentation is really, really, relly good and you should always use it first as a source of information.
I have created a Spring Boot 2 Application, integrated SpringFox Swagger 2.8.0 with Implicit Oauth2 Grant for Authentication and Authorization.
The Code is working fine but when I click Authorize button it redirects to the
http://localhost:8080/oauth/authorize?response_type=token&client_id=test-app-client-id&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fwebjars%2Fspringfox-swagger-ui%2Foauth2-redirect.html&scope=read&state=U3VuIE9jdCAxNCAyMDE4IDIwOjQyOjUwIEdNVCswNTMwIChJbmRpYSBTdGFuZGFyZCBUaW1lKQ%3D%3D
but shows Access Denied like as shown below.
My complete project is available in GitHub
MainApplication.java
#EnableSwagger2
#SpringBootApplication
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
#RestController
public class MainApplication /*extends WebMvcConfigurerAdapter*/
{
public static void main(String[] args)
{
SpringApplication.run(MainApplication.class, args);
}
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
#Bean
SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()//<19>
.clientId("test-app-client-id")
.build();
}
#Bean
SecurityScheme oauth() {
List<GrantType> grantTypes = new ArrayList<>();
ImplicitGrant implicitGrant = new ImplicitGrant(new LoginEndpoint("http://localhost:8080/oauth/authorize"),"access_code");
grantTypes.add(implicitGrant);
List<AuthorizationScope> scopes = new ArrayList<>();
scopes.add(new AuthorizationScope("read","Read access on the API"));
return new OAuthBuilder()
.name("SECURITY_SCHEME_OAUTH2")
.grantTypes(grantTypes)
.scopes(scopes)
.build();
}
#Bean
public Docket docket()
{
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(getClass().getPackage().getName()))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(oauth()))
.apiInfo(generateApiInfo());
}
private ApiInfo generateApiInfo()
{
return new ApiInfo("Sample Service", "This service is to check Sample Service.", "Version 1.0",
"Sample Service", "123#test.com", "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0");
}
}
Update 1
I have added the security and the passwordencoder configure suggested from #AlexanderPetrov. Things are working fine, when I add #EnableResourceServer my login screen is showing Full authentication is required to access this resource like as shown below
Can anyone please help me on this
You need to do the following changes in your code
Form login configuration is necessary for implicit flow.
Also if we use implicit flow token will be generated through authorization url instead of token url. So you need to change "/oauth/token" to "oauth/authorize". configure method below
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/oauth/authorize").authenticated()
.and()
.authorizeRequests().anyRequest().permitAll()
.and()
.formLogin().permitAll()
.and()
.csrf().disable();
}
Add password encoder in SecurityConfig class and invoke it to encode user password in globalUserDetails method. Encoder is necessary because you use in memory passwords. So without password encoder application fails with an error:
Encoded password does not look like BCrypt
Code fragment below
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
PasswordEncoder passwordEncoder = passwordEncoder();
auth.inMemoryAuthentication().passwordEncoder(passwordEncoder()).
withUser("bill").password(passwordEncoder.encode("abc123")).roles("ADMIN").and()
.withUser("$2a$10$TT7USzDvMxMZvf0HUVh9p.er1GGnjNQzlcGivj8CivnaZf9edaz6C")
.password("$2a$10$TT7USzDvMxMZvf0HUVh9p.er1GGnjNQzlcGivj8CivnaZf9edaz6C").roles("USER");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Hope it helps. I've created branch for your project but couldn't push it because of 403. So all necessary code is here in my answer.
When you enable a resource server, you need to configure the check_token URL, so that it can reach the OAuth2 authorization server and validate the given access_token.
You could do something like:
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class OAuth2ResourceServerConfig extends GlobalMethodSecurityConfiguration {
#Value("${oauth.url.internal}") // e.g. http://localhost:8082/oauth
private String oauthUrl;
#Value("${oauth.client}")
private String oauthClient;
#Value("${oauth.secret}")
private String oauthSecret;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
#Primary
#Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl(oauthUrl + "/check_token");
tokenService.setClientId(oauthClient);
tokenService.setClientSecret(oauthSecret);
return tokenService;
}
}
Besides this, you may want to ignore Swagger-specific endpoints:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**");
}
}
Just in case, this is the class I implemented for Swagger w/ OAuth2 authorization:
#EnableSwagger2
#Configuration
public class SwaggerConfig implements WebMvcConfigurer {
private static final String BASE_PACKAGE = "com.somepackage.api";
#Value("${oauth.url}") // Make sure this is an external URL, i.e. accessible from Swagger UI
private String oauthUrl;
#Value("${swagger.scopes}")
private String swaggerScopes;
#Value("${swagger.urls}")
private String swaggerUrls; // Your v2/api-docs URL accessible from the UI
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage(BASE_PACKAGE))
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securitySchema()))
.securityContexts(Collections.singletonList(securityContext()));
}
private OAuth securitySchema() {
List<AuthorizationScope> authorizationScopeList = new ArrayList<>();
authorizationScopeList.add(new AuthorizationScope(swaggerScopes, ""));
List<GrantType> grantTypes = new ArrayList<>();
GrantType creGrant = new ResourceOwnerPasswordCredentialsGrant(oauthUrl + "/token");
grantTypes.add(creGrant);
return new OAuth("oauth2schema", authorizationScopeList, grantTypes);
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.ant(swaggerUrls)).build();
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = new AuthorizationScope(swaggerScopes, "");
return Collections.singletonList(new SecurityReference("oauth2schema", authorizationScopes));
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Versions:
springSecurityVersion = '2.0.5.RELEASE'
swaggerVersion = '2.8.0'
springBootVersion = '2.0.5.RELEASE'
My project has Spring Security.
Main issue: Not able to access swagger URL at http://localhost:8080/api/v2/api-docs. It says Missing or invalid Authorization header.
Screenshot of the browser window
My pom.xml has the following entries
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.4.0</version>
</dependency>
SwaggerConfig :
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo("My REST API", "Some custom description of API.", "API TOS", "Terms of service", "myeaddress#company.com", "License of API", "API license URL");
return apiInfo;
}
AppConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.musigma.esp2" })
#Import(SwaggerConfig.class)
public class AppConfig extends WebMvcConfigurerAdapter {
// ========= Overrides ===========
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LocaleChangeInterceptor());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
web.xml entries:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.musigma.esp2.configuration.AppConfig
com.musigma.esp2.configuration.WebSecurityConfiguration
com.musigma.esp2.configuration.PersistenceConfig
com.musigma.esp2.configuration.ACLConfig
com.musigma.esp2.configuration.SwaggerConfig
</param-value>
</context-param>
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan(basePackages = { "com.musigma.esp2.service", "com.musigma.esp2.security" })
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(this.unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/login", "/auth/logout").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().authenticated();
// custom JSON based authentication by POST of {"username":"<name>","password":"<password>"} which sets the token header upon authentication
httpSecurity.addFilterBefore(loginFilter(), UsernamePasswordAuthenticationFilter.class);
// custom Token based authentication based on the header previously given to the client
httpSecurity.addFilterBefore(new StatelessTokenAuthenticationFilter(tokenAuthenticationService), UsernamePasswordAuthenticationFilter.class);
}
}
Adding this to your WebSecurityConfiguration class should do the trick.
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
}
I had the same problem using Spring Boot 2.0.0.M7 + Spring Security + Springfox 2.8.0. And I solved the problem using the following security configuration that allows public access to Swagger UI resources.
Answer updated in January 2021 : support Springfox 3
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
// -- Swagger UI v2
"/v2/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**",
// -- Swagger UI v3 (OpenAPI)
"/v3/api-docs/**",
"/swagger-ui/**"
// other public endpoints of your API may be appended to this array
};
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
// ... here goes your custom security configuration
authorizeRequests().
antMatchers(AUTH_WHITELIST).permitAll(). // whitelist Swagger UI resources
// ... here goes your custom security configuration
antMatchers("/**").authenticated(); // require authentication for any endpoint that's not whitelisted
}
}
I updated with /configuration/** and /swagger-resources/** and it worked for me.
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/**", "/swagger-ui.html", "/webjars/**");
}
For those who using a newer swagger 3 version org.springdoc:springdoc-openapi-ui
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**");
}
}
if your springfox version higher than 2.5, should be add WebSecurityConfiguration as below:
#Override
public void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
http.authorizeRequests()
.antMatchers("/v2/api-docs", "/swagger-resources/configuration/ui", "/swagger-resources", "/swagger-resources/configuration/security", "/swagger-ui.html", "/webjars/**").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
Some security config and you are ready with swagger open to all
For Swagger V2
#Configuration
#EnableWebSecurity
public class CabSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
// -- swagger ui
"/v2/api-docs",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**"
};
#Override
protected void configure(HttpSecurity http) throws Exception {
// ... here goes your custom security configuration
http.authorizeRequests().
antMatchers(AUTH_WHITELIST).permitAll(). // whitelist URL permitted
antMatchers("/**").authenticated(); // others need auth
}
}
For Swagger V3
#Configuration
#EnableWebSecurity
public class CabSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] AUTH_WHITELIST = {
// -- swagger ui
"/v2/api-docs",
"/v3/api-docs",
"/swagger-resources/**",
"/swagger-ui/**",
};
#Override
protected void configure(HttpSecurity http) throws Exception {
// ... here goes your custom security configuration
http.authorizeRequests().
antMatchers(AUTH_WHITELIST).permitAll(). // whitelist URL permitted
antMatchers("/**").authenticated(); // others need auth
}
}
More or less this page has answers but all are not at one place. I was dealing with the same issue and spent quite a good time on it. Now i have a better understanding and i would like to share it here:
I Enabling Swagger ui with Spring websecurity:
If you have enabled Spring Websecurity by default it will block all the requests to your application and returns 401. However for the swagger ui to load in the browser swagger-ui.html makes several calls to collect data. The best way to debug is open swagger-ui.html in a browser(like google chrome) and use developer options('F12' key ). You can see several calls made when the page loads and if the swagger-ui is not loading completely probably some of them are failing.
you may need to tell Spring websecurity to ignore authentication for several swagger path patterns.
I am using swagger-ui 2.9.2 and in my case below are the patterns that i had to ignore:
However if you are using a different version your's might change. you may have to figure out yours with developer option in your browser as i said before.
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui",
"/swagger-resources/**", "/configuration/**", "/swagger-ui.html"
, "/webjars/**", "/csrf", "/");
}
}
II Enabling swagger ui with interceptor
Generally you may not want to intercept requests that are made by swagger-ui.html. To exclude several patterns of swagger below is the code:
Most of the cases pattern for web security and interceptor will be same.
#Configuration
#EnableWebMvc
public class RetrieveCiamInterceptorConfiguration implements WebMvcConfigurer {
#Autowired
RetrieveInterceptor validationInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(validationInterceptor).addPathPatterns("/**")
.excludePathPatterns("/v2/api-docs", "/configuration/ui",
"/swagger-resources/**", "/configuration/**", "/swagger-ui.html"
, "/webjars/**", "/csrf", "/");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Since you may have to enable #EnableWebMvc to add interceptors you may also have to add resource handlers to swagger similar to i have done in the above code snippet.
Limiting only to Swagger related resources:
.antMatchers("/v2/api-docs", "/swagger-resources/**", "/swagger-ui.html", "/webjars/springfox-swagger-ui/**");
Here's a complete solution for Swagger with Spring Security. We probably want to only enable Swagger in our development and QA environment and disable it in the production environment. So, I am using a property (prop.swagger.enabled) as a flag to bypass spring security authentication for swagger-ui only in development/qa environment.
#Configuration
#EnableSwagger2
public class SwaggerConfiguration extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
#Value("${prop.swagger.enabled:false}")
private boolean enableSwagger;
#Bean
public Docket SwaggerConfig() {
return new Docket(DocumentationType.SWAGGER_2)
.enable(enableSwagger)
.select()
.apis(RequestHandlerSelectors.basePackage("com.your.controller"))
.paths(PathSelectors.any())
.build();
}
#Override
public void configure(WebSecurity web) throws Exception {
if (enableSwagger)
web.ignoring().antMatchers("/v2/api-docs",
"/configuration/ui",
"/swagger-resources/**",
"/configuration/security",
"/swagger-ui.html",
"/webjars/**");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (enableSwagger) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
}
Considering all of your API requests located with a url pattern of /api/.. you can tell spring to secure only this url pattern by using below configuration. Which means that you are telling spring what to secure instead of what to ignore.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.and()
.httpBasic().and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
I am using Spring Boot 5. I have this controller that I want an unauthenticated user to invoke.
//Builds a form to send to devices
#RequestMapping(value = "/{id}/ViewFormit", method = RequestMethod.GET)
#ResponseBody
String doFormIT(#PathVariable String id) {
try
{
//Get a list of forms applicable to the current user
FormService parent = new FormService();
Here is what i did in the configuuration.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(
"/registration**",
"/{^[\\\\d]$}/ViewFormit",
Hope this helps....
Just for enabling Swagger with Spring boot 2.5.4 and Springfox Swagger2:3.0.0 the following changes were sufficient for me:-
.authorizeRequests().antMatchers("/v2/api-docs", "/swagger-resources/**", "/swagger-ui/**").permitAll().and()
Thanks to everyone for their suggestions in this thread!
Add a Bean like this:
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange()
.pathMatchers(
"/v2/api-docs",
"/swagger-ui/**",
"/swagger-resources/**",
"/*/swagger-resources/**",
"/*/v2/api-docs")
.permitAll()
.and()
.authorizeExchange()
.anyExchange()
.permitAll();
http.httpBasic().disable();
http.csrf().disable();
return http.build();
}
For Spring Security without the WebSecurityConfigurerAdapter it looks like (springdoc-openapi):
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return web -> web.ignoring().requestMatchers("/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**");
}
See Configuring WebSecurity
If you use Spring Boot 3, you need to use: springdoc-openapi-starter-webmvc-ui as it is writen in the doc introduction.
And use security configuration, something like this:
#Configuration
#EnableWebSecurity
#EnableMethodSecurity(securedEnabled = true, prePostEnabled = true, jsr250Enabled = true)
#RequiredArgsConstructor
public class SecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf().disable()
.authorizeHttpRequests(a -> a
.requestMatchers("/v3/**", "/swagger-ui/**").permitAll()
.anyRequest().authenticated()
).build();
}
}
Swagger UI: http://{your host}:{your port}/swagger-ui/index.html
JSON: http://{your host}:{your port}/v3/api-docs
yaml: http://{your host}:{your port}/v3/api-docs.yaml
I have Swagger set up and working for all of the controllers listed in my application. However, I want it to pick up the Spring Security Logout Endpoint and I cannot find a way to get it to work. As you can see from code snippet below I am specifying a logoutUrl for a user to invalidate their session. I've tried class level annotation markings and method level, but no luck. Any ideas?
#Override
public void configure(HttpSecurity http) throws Exception {
http.addFilter(someFilter());
http.headers().and().csrf().disable()
.authorizeRequests()
.antMatchers("endpoint",
"endpoint",
"endpoint",
"endpoint",
"endpoint",
"endpoint",
"endpoint").permitAll()
.anyRequest().authenticated()
.and()
.logout().logoutUrl("endpoint/logout").invalidateHttpSession(true).logoutSuccessHandler(logoutSuccessHandler);
}
My Swagger Docket configuration is below:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(new ApiInfo("API",
"Provides API's",
"1.0",
null,
"someEmail#nowhere.com",
null,
null))
.useDefaultResponseMessages(false)
.pathProvider(new RelativePathProvider(servletContext) {
#Override
protected String applicationPath() {
return super.applicationPath() + "/path";
}
#Override
protected String getDocumentationPath() {
return super.getDocumentationPath() + "/path";
}
});
}
The Spring Fox plugin uses Spring beans to build the API documentation. Take a look at this answer: https://stackoverflow.com/a/42720435/439171