Spring Security - permit only requests with prefix - spring-boot

I need to secure all rest endpoints in our Resource Server except endpoints that start with /unsecured. So requests like the following should be permitted to everyone:
/unsecured/foo/bar
/unsecured
...
but requests like these:
/foo/unsecured/bar
/foo/bar
...
should require authentication.
#Configuration
#EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests(authorizeRequests -> {
authorizeRequests.antMatchers("unsecured/**").permitAll();
authorizeRequests.anyRequest().authenticated();
});
}
}
But in the configuration above, all endpoints require authentication.
This is response which I receive when I tried access to unsecured endpoint:
code 401
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}

premitAll() is what you are looking for. Looks like you were just missing the / before the URL
#Configuration
#EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity security) throws Exception {
security
.authorizeRequests(authorizeRequests - > {
authorizeRequests.antMatchers("/unsecured/**").permitAll();
authorizeRequests.anyRequest().authenticated();
});
}
}

Related

Spring Boot Security - How to disable security for Swagger UI

I have an application with only REST endpoints. I have enabled oauth2 token security via:
#Configuration
#EnableAuthorizationServer
public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("xxx").secret("xxx").accessTokenValiditySeconds(3600)
.authorizedGrantTypes("client_credentials")
.scopes("xxx", "xxx")
.and()
.withClient("xxx").secret("xxx").accessTokenValiditySeconds(3600)
.authorizedGrantTypes("password", "refresh_token")
.scopes("xxx", "xxx");
}
}
Now if I try to access any of my endpoints I get 401 Unauthorized, and I first have to get the access_token via the /oauth/token?grant_type=client_credentials or /oauth/token?grant_type=password calls. The REST endpoints work as expected if I add the proper Authorization header with the token returned in previous call.
However, I am unable to access the swagger-ui page. I have enabled swagger via:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select().apis(RequestHandlerSelectors.basePackage("com.xxx"))
.paths(PathSelectors.regex("/xxx/.*"))
.build();
}
}
If I go to localhost:8080/swagger-ui.html I get:
<oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>
So I added the following to be able to access Swagger:
#Configuration
public class ResourceServerConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/swagger-ui.html")
.antMatchers("/webjars/springfox-swagger-ui/**")
.antMatchers("/swagger-resources/**")
.antMatchers("/v2/api-docs");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
And in #EnableWebMvc class I added:
#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/");
}
Now I can access the Swagger UI page, but my security for the REST endpoints is messed up. By that I mean, the client_credentials endpoints no longer require a token, and the password endpoints give a 403 Forbidden no matter what I do.
I think my approach is wrong but I don't know what. Basically I want:
Oauth token security on all my REST endpoints (beginning with /api/* for example)
Swagger UI page should be accessible
The endpoints on the swagger page should have a way to specify the access_token
How do I achieve this?
This is how I fixed it. I removed the class that extends WebSecurityConfigurerAdapter (see above) and replaced with this:
#Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/xxx/**").authenticated();
http.authorizeRequests().anyRequest().permitAll();
http.csrf().disable();
}
}
To enable token authentication on the swagger page I followed this tutorial: http://www.baeldung.com/swagger-2-documentation-for-spring-rest-api

Unable to set up basic authentication with Spring Boot REST API

I'm trying to set up a RESTful API with Spring Boot and I'm trying to enable basic authentication. How come I keep hitting a 403 Access Denied error? I'm sending my credentials as a header in Postman (see image attached). If I remove .anyRequest.authenticated(), it works fine. I don't want to remove that though because I would like basic authentication for every endpoint. What am I doing wrong?
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/h2-console/*").permitAll()
.anyRequest().authenticated();
http.csrf().disable();
http.headers().frameOptions().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
Controller.java
#RestController
public class Controller {
#RequestMapping("/test")
public String index() {
return "Greetings from Spring Boot!";
}
}
After digging around in the Spring docs, it seems I understand what each of the chained method calls are for.
Anyway, the simple answer is that I needed .and().httpBasic() to enable Basic HTTP Authentication over my REST API.
.anyRequest().authenticated() simply mandates that every request is authenticated, but did not specify what method. Adding basic authentication means we can use basic auth to authenticate a user.
See more.

Spring ResourceServer using RemoteTokenServices with uri to check access_token

I have a configuration that enables web security and resource server as below
#EnableWebSecurity
#EnableResourceServer
public class SpringSecurityConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
I also added the following properties
security.oauth2.resource.user-info-uri: http://localhost:9090/oauth2/tokeninfo
security.oauth2.resource.token-info-uri: http://localhost:9090/oauth2/tokeninfo
But somehow the authentication manager in the created OAuth2AuthenticationProcessingFilter uses the DefaultTokenServices to loadAuthentication. How can I let it use the RemoteTokenServices by calling the uri I provided in the configuration to check the access_token sent by the client.

Spring Boot 1.3.3 #EnableResourceServer and #EnableOAuth2Sso at the same time

I want my server be a ResourceServer, which can accept a Bearer Access token
However, If such token doesn't exist, I want to use the OAuth2Server to authenticate my user.
I try to do like:
#Configuration
#EnableOAuth2Sso
#EnableResourceServer
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
}
However, in this case, only the #EnableResourceServer annotation works. It returns
Full authentication is required to access this resource
And do not redirect me to the login page
I mentioned that the #Order is important, if I add the #Order(0) annotation,
I will be redirect to the login page, however, I cannot access my resource with the access_token in Http header:
Authorization : Bearer 142042b2-342f-4f19-8f53-bea0bae061fc
How can I achieve my goal? I want it use Access token and SSO at the same time.
Thanks~
Using both configuration on same request would be ambiguous. There could be some solution for that, but more clear to define separate request groups:
OAuth2Sso: for users coming from a browser, we want to redirect them to the authentication provider for the token
ResourceServer: usually for api requests, coming with a token they got from somewhere (most probably from same authentication provider)
For achieving this, separate the configurations with request matcher:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Bean("resourceServerRequestMatcher")
public RequestMatcher resources() {
return new AntPathRequestMatcher("/resources/**");
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http
.requestMatcher(resources()).authorizeRequests()
.anyRequest().authenticated();
}
}
And exclude these from the sso filter chain:
#Configuration
#EnableOAuth2Sso
public class SsoSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("resourceServerRequestMatcher")
private RequestMatcher resources;
#Override
protected void configure(final HttpSecurity http) throws Exception {
RequestMatcher nonResoures = new NegatedRequestMatcher(resources);
http
.requestMatcher(nonResoures).authorizeRequests()
.anyRequest().authenticated();
}
}
And put all your resources under /resources/**
Of course in this case both will use the same oauth2 configuration (accessTokenUri, jwt.key-value, etc.)
UPDATE1:
Actually you can achieve your original goal by using this request matcher for the above configuration:
new RequestHeaderRequestMatcher("Authorization")
UPDATE2:
(Explanation of #sid-morad's comment)
Spring Security creates a filter chain for each configuration. The request matcher for each filter chain is evaluated in the order of the configurations.
WebSecurityConfigurerAdapter has default order 100, and ResourceServerConfiguration is ordered 3 by default. Which means ResourceServerConfiguration's request matcher evaluated first. This order can be overridden for these configurations like:
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration configuration;
#PostConstruct
public void setSecurityConfigurerOrder() {
configuration.setOrder(3);
}
...
}
#Configuration
#EnableOAuth2Sso
#Order(100)
public class SsoSecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
So yes, request matcher is not needed for SsoSecurityConfiguration in the above sample. But good to know the reasons behind :)

Spring OAuth 2: public access to a resource

How do I allow public access in an specific URL in a Spring Security OAuth-2 Rest application.
I have all URLs started with /rest/** secured, but would like to make /rest/about public, so I would not require the user to authenticate to access it. I tried using permitAll() but it still requires the token in the request. This is my HttpSecurity configuration:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/rest/about").permitAll()
.antMatchers("/rest/**").authenticated()
;
}
}
A GET request to /rest/about still returns 401 Unauthorized - "error":"unauthorized","error_description":"Full authentication is required to access this resource"
Found the answer. I just needed to add anonymous():
public void configure(HttpSecurity http) throws Exception {
http
.anonymous().and()
.authorizeRequests()
.antMatchers("/rest/about").permitAll()
.antMatchers("/rest/**").authenticated()
;
}
Got the answer from: https://stackoverflow.com/a/25280897/256245

Resources