Swagger OpenAPI UI sending 403 response status for POST, PUT and DELETE Requests : Server hosted behind Nginx Proxy - spring-boot

Issue :
Get request for Swagger UI openAPI is working , whereas other method types giving 403 error.
Dependency :
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.6</version>
</dependency>
Swagger Configuration :
#Configuration
#OpenAPIDefinition(servers = {
#Server(url = "https://hostname")
})
#SecurityScheme(name = auth, type = SecuritySchemeType.HTTP, bearerFormat = "JWT", scheme = "bearer")
public class SwaggerConfig {
}
Security Configuration :
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/swagger-ui/**","/v3/api-docs/**");
}
}
We have also tried ignoring these paths : /swagger-resources/** , /webjars/** in WebSecurity, still its not working.
Post Request Error message 403
Original Edit : On some further research , found that's it may be because of the nginx proxy. Everything is working fine on my local but not working on other environments that are hosted behind the nginx proxy.

This is my solution described in the comments. I have a also a configuration for #Order(1) that is for the rest of my application.
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] WHITELIST = {
"/v2/api-docs",
"/v3/api-docs",
"/**/v3/api-docs",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html",
"**/swagger-ui.html",
"/**/swagger-ui.html**",
"/swagger-ui.html**",
"/webjars/**"
};
#Configuration
#Order(2)
public static class SwaggerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(WHITELIST).permitAll();
http.csrf().disable();
}
}
}

Related

How to secure #PostMapping endpoint by spring security without secure #GetMapping

I would like to add Spring Security to my app on few endpoints.
#RestController
#RequestMapping("/test")
#RequiredArgsConstructor
public class TestController {
#PostMapping
public ResponseEntity post() {
...
}
#GetMapping
public ResponseEntity get() {
...
}
In web security conifure adapter I know how to secure endpoit. I did:
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
private final MyUserDetailsService myUserDetailsService;
#Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.anyRequest().authenticated()
;
}
In endpoint "/test" I would like to add security in #PostMapping where only authenticated users will be able to post something. #GetMapping will be open for everyone.
EDIT
So I updated my MyWebSecurityConfig:
#Override
public void configure(HttpSecurity http) throws Exception {
http.httpBasic().and()
.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.antMatchers(HttpMethod.GET, "/shapes").permitAll()
.antMatchers(HttpMethod.GET, "/shapes/history").permitAll()
.anyRequest().authenticated()
;
}
but I can't still send Get on "/shapes" endpoit without authentication.
I am still getting 401 Unauthorized. What should I change?
There is an overrided version of antMatchers() that allow you to configure matching a HTTP method and the path together :
.authorizeRequests()
.antMatchers("/users", "/users/**").permitAll()
.antMatchers(HttpMethod.GET,"/test").permitAll()
.antMatchers(HttpMethod.POST, "/test").authenticated()

Spring boot (v2.6.7) security with JWT, web.ignoring() is working but not antMatchers permitAll()

I migrated Spring boot from 2.1.5 to 2.6.7. Now the security configuration is throwing a warning about antMatchers in web.ignoring in the WebSecurity configuration lines, although it works correctly. Upon moving the web.ignoring to antMatchers permitAll() in httpConfiguration, the warning goes away but the urls are no longer permitted.
I am also using JWT and it looks like the JWT filters are kicking in and not honoring the httpConfiguration. I am really confused as to how to configure this. I am including the before and after here.
WORKS WITH WARNING:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
#Override
public void init(WebSecurity web) throws Exception {
super.init(web);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
LOGGER.debug("**********************SECURITY INITALIZE NORMAL#######################################");
http.cors().and().csrf().disable().authorizeRequests()
.anyRequest().authenticated().and()
.addFilter(new JWTAuthorizationFilter(authenticationManager(), getApplicationContext()))
.addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/v2/api-docs/**");
}
}
DOES NOT WORK:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfiguration.class);
#Override
public void init(WebSecurity web) throws Exception {
super.init(web);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
LOGGER.debug("**********************SECURITY INITALIZE NORMAL#######################################");
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers("/v2/api-docs/**").permitAll() // ADDED LINE
.anyRequest().authenticated().and()
.addFilter(new JWTAuthorizationFilter(authenticationManager(), getApplicationContext()))
.addFilter(new JWTAuthenticationFilter(authenticationManager(), getApplicationContext()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(WebSecurity web) throws Exception {
//web.ignoring().antMatchers("/v2/api-docs/**");
}
}
Looks like somehow the configuration breaks when using http security versus web security. I have tried many of the suggestions but nothing seems to work.
Can someone familiar with Spring Boot Security in 2.6.7 with JWT filters please suggest what I am doing wrong?

Cannot access to unsecured endpoints in Spring Boot

In my controller I have two endpoints where one is secured and one is public:
#GetMapping("/public")
public String getPublic() {
return "public";
}
#PreAuthorize("hasRole('USER')")
#GetMapping("/private")
public String getPrivate() {
return "public";
}
Secured endpoint works only when I am logged and token with right role is placed in request header. But when I want access to public endpoint without token I always got status 401 with error
Full authentication is required to access this resource
Here is my security configuration:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.csrf().disable();
}
}
and authorization server config:
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private final UserDetailsService appUserDetailService;
private final AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancer())
.authenticationManager(authenticationManager)
.userDetailsService(appUserDetailService);
}
}
I also tried change .authorizeRequests().anyRequest().authenticated() to this : .authorizeRequests().anyRequest().permitAll() with no change. My preferred way is handle security with annotations. Thank you.
You have two options, can go with either.
Option 1: In your endpoint, change like this.
#PreAuthorize("permitAll()")
#GetMapping("/public")
public String getPublic() {
return "public";
}
And change your configure(HttpSecurity http) method, do like this.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().permitAll()
.and()
.csrf().disable();
}
Option 2: In your configure(HttpSecurity http) method, just do like this.
#Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/public").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
antMatchers() will do the trick. We use it a lot. It is also better to have insecured endpoints in different class and control security on class level through request mapping.
antMatchers("/public").permitAll()
Link to spring security api - https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/web/builders/HttpSecurity.html#antMatcher-java.lang.String-

Is it a Spring `Dalston` bug? [duplicate]

I am trying to upgrade a sample Spring Boot and Spring Cloud Security with OAuth from Spring Boot 1.4.1 + Brixton.RELEASE to Spring Boot 1.5.3+ Dalston.RELEASE. However, it has been a long hard try without any success.
It seems for some reason the resource server security filter chain is not getting fired. As a result the call to "/me" or "/user" is being handled by default security filter chain. I am thinking if this is a problem with order. But tried to explicitly set the order of the security filter chains as follows
Auth Server 6
Web Default 5
Resource server 3 (hard coded ??)
Since the default filter chain is handling the request, it is always going to the login page, which generates HTML and the SSO client (server side thymeleaf web UI) fails.
The source code is below
Authorization server
#SpringBootApplication
public class MyAuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(MyAuthServerApplication.class, args);
}
}
Then the authorization server configuration
#Configuration
#EnableAuthorizationServer
#Order(6)
public class AuthorizationServerConfigurer extends A
uthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws
Exception {
clients.inMemory()
.withClient("myauthserver")
.secret("verysecretpassword")
.redirectUris("http://localhost:8080/")
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("myscope")
.autoApprove(true);
}
}
Then the resource server class
#Configuration
#EnableResourceServer
public class ResourceServerConfigurer extends
ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user")
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
The web MVC configuration
#Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("login").setViewName("login");
}
}
The default spring security configuration
#Configuration
#EnableWebSecurity
#Order(9)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and().csrf()
.and().formLogin().loginPage("/login");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("admin").roles("USER", "ADMIN");
}
}
The resource controller
#RestController
public class ResourceController {
#RequestMapping(value = { "/user" }, produces = "application/json")
public Map<String, Object> user(OAuth2Authentication user) {
Map<String, Object> userDetails = new HashMap<>();
userDetails.put("user", user.getUserAuthentication().getPrincipal());
userDetails.put("authorities",
AuthorityUtils.
authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userDetails;
}
}
Finally the configuration - application.yml for the auth server
server:
port: 9090
contextPath: /auth
logging:
level:
org.springframework: INFO
org.springframework.security: DEBUG
The login.html Thymeleaf template is not shown here.
OAUTH 2 SSO Client Web App
#SpringBootApplication
public class MyWebsiteApplication {
public static void main(String[] args) {
SpringApplication.run(MyWebsiteApplication.class, args);
}
}
The web security configuration
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll() // Allow navigating to index
page,
.anyRequest().authenticated(); // but secure all the other URLs
}
}
The web controller
#Controller
public class MyWebsiteController {
/**
* Default index page to verify that our application works.
*/
#RequestMapping("/")
#ResponseBody
public String helloWorld() {
return "Hello world!";
}
/**
* Return a ModelAndView which will cause the
'src/main/resources/templates/time.html' template to be rendered,
* with the current time.
*/
#RequestMapping("/time")
public ModelAndView time() {
ModelAndView mav = new ModelAndView("time");
mav.addObject("currentTime", getCurrentTime());
return mav;
}
private String getCurrentTime() {
return LocalTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
}
}
The application configuration - application.yml for the client web app
server:
port: 8080
contextPath: /
security:
oauth2:
client:
accessTokenUri: http://localhost:9090/auth/oauth/token
userAuthorizationUri: http://localhost:9090/auth/oauth/authorize
clientId: myauthserver
clientSecret: verysecretpassword
resource:
userInfoUri: http://localhost:9090/auth/user
The Thymeleaf template for the time.html page is not shown here.
There must be some subtle little configuration thats wrong or some bug I do not know. Any help or ideas highly appreciated.
Solution
Guess was right the ordering of the security filter chain got was changed. Here is the link to the
Spring 1.5.x release note
Modified code is here and will upload it to Github later. All changes on the auth server configuration.
The Spring security configuration - remove the #Order annotation.
#Configuration
#EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and().csrf()
.and().formLogin().loginPage("/login");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("admin").roles("USER", "ADMIN");
}
}
Then change the application.yml as follows
server:
port: 9090
contextPath: /auth
logging:
level:
org.springframework: INFO
org.springframework.security: DEBUG
security:
oauth2:
resource:
filter-order : 3
That's it then the time is displayed on the client application /time url after authentication on the auth server.

Spring Boot and Spring Cloud Security OAUTH 2 SSO Failing with latest releases

I am trying to upgrade a sample Spring Boot and Spring Cloud Security with OAuth from Spring Boot 1.4.1 + Brixton.RELEASE to Spring Boot 1.5.3+ Dalston.RELEASE. However, it has been a long hard try without any success.
It seems for some reason the resource server security filter chain is not getting fired. As a result the call to "/me" or "/user" is being handled by default security filter chain. I am thinking if this is a problem with order. But tried to explicitly set the order of the security filter chains as follows
Auth Server 6
Web Default 5
Resource server 3 (hard coded ??)
Since the default filter chain is handling the request, it is always going to the login page, which generates HTML and the SSO client (server side thymeleaf web UI) fails.
The source code is below
Authorization server
#SpringBootApplication
public class MyAuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(MyAuthServerApplication.class, args);
}
}
Then the authorization server configuration
#Configuration
#EnableAuthorizationServer
#Order(6)
public class AuthorizationServerConfigurer extends A
uthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws
Exception {
clients.inMemory()
.withClient("myauthserver")
.secret("verysecretpassword")
.redirectUris("http://localhost:8080/")
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("myscope")
.autoApprove(true);
}
}
Then the resource server class
#Configuration
#EnableResourceServer
public class ResourceServerConfigurer extends
ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/user")
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
The web MVC configuration
#Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("login").setViewName("login");
}
}
The default spring security configuration
#Configuration
#EnableWebSecurity
#Order(9)
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and().csrf()
.and().formLogin().loginPage("/login");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("admin").roles("USER", "ADMIN");
}
}
The resource controller
#RestController
public class ResourceController {
#RequestMapping(value = { "/user" }, produces = "application/json")
public Map<String, Object> user(OAuth2Authentication user) {
Map<String, Object> userDetails = new HashMap<>();
userDetails.put("user", user.getUserAuthentication().getPrincipal());
userDetails.put("authorities",
AuthorityUtils.
authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userDetails;
}
}
Finally the configuration - application.yml for the auth server
server:
port: 9090
contextPath: /auth
logging:
level:
org.springframework: INFO
org.springframework.security: DEBUG
The login.html Thymeleaf template is not shown here.
OAUTH 2 SSO Client Web App
#SpringBootApplication
public class MyWebsiteApplication {
public static void main(String[] args) {
SpringApplication.run(MyWebsiteApplication.class, args);
}
}
The web security configuration
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll() // Allow navigating to index
page,
.anyRequest().authenticated(); // but secure all the other URLs
}
}
The web controller
#Controller
public class MyWebsiteController {
/**
* Default index page to verify that our application works.
*/
#RequestMapping("/")
#ResponseBody
public String helloWorld() {
return "Hello world!";
}
/**
* Return a ModelAndView which will cause the
'src/main/resources/templates/time.html' template to be rendered,
* with the current time.
*/
#RequestMapping("/time")
public ModelAndView time() {
ModelAndView mav = new ModelAndView("time");
mav.addObject("currentTime", getCurrentTime());
return mav;
}
private String getCurrentTime() {
return LocalTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME);
}
}
The application configuration - application.yml for the client web app
server:
port: 8080
contextPath: /
security:
oauth2:
client:
accessTokenUri: http://localhost:9090/auth/oauth/token
userAuthorizationUri: http://localhost:9090/auth/oauth/authorize
clientId: myauthserver
clientSecret: verysecretpassword
resource:
userInfoUri: http://localhost:9090/auth/user
The Thymeleaf template for the time.html page is not shown here.
There must be some subtle little configuration thats wrong or some bug I do not know. Any help or ideas highly appreciated.
Solution
Guess was right the ordering of the security filter chain got was changed. Here is the link to the
Spring 1.5.x release note
Modified code is here and will upload it to Github later. All changes on the auth server configuration.
The Spring security configuration - remove the #Order annotation.
#Configuration
#EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and().csrf()
.and().formLogin().loginPage("/login");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
.and()
.withUser("admin").password("admin").roles("USER", "ADMIN");
}
}
Then change the application.yml as follows
server:
port: 9090
contextPath: /auth
logging:
level:
org.springframework: INFO
org.springframework.security: DEBUG
security:
oauth2:
resource:
filter-order : 3
That's it then the time is displayed on the client application /time url after authentication on the auth server.

Resources