Why specifying full paths to secure when we have a base path? - spring-security

Say we have this configuration:
#RestController
#RequestMatching(("/private")
class PrivateProductController {
#GetMapping("/products")
Collection<Product> getProducts(){
....
}
#GetMapping("/category")
int getCategory(){
....
}
#GetMapping("/catalog")
Catalogue getCatalogue(){
....
}
}
And an associated security configuration:
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/private/**")
.and()
.authorizedRequests()
.antMatchers("/private/products").hasRole("USER")
.antMatchers("/private/category").authenticated()
.antMatchers("/private/catalog").hasAnyRole("USER","GUEST");
}
}
Since the base path is /private/** I tried to simplify my configuration like this (by removing /private from subpaths):
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/private/**")
.and()
.authorizedRequests()
.antMatchers("/products").hasRole("USER")
.antMatchers("/category").authenticated()
.antMatchers("/catalog").hasAnyRole("USER","GUEST");
}
}
But that simplification does not work. So the question is why do we need to add each time the full path? Since we declared this: http.requestMatchers().antMatchers("/private/**") I thought that all /private subpaths like /products or /category would be managed in this filters chain without prefixing them by /private.
I surelly misconfigured spring security, how to achieve what I would like to do? I mean defining a filters chain matching a base path where all subpaths are protected without needing to prefix them by the root path? Thanks

The pattern you passed to antMatcher method is matched by Spring Security with the request URI i.e. request.getServletPath()+request.getPathInfo()+request.getQueryString()
When you provide just /products instead of /private/products , the pattern doesn't match and it fails.

Related

using HttpSecurity.requestMatchers in class ResourceServerConfiguration.configure in spring oauth2

I have been struggling to understand how and when to use HttpSecurity.requestMatchers. Though I use HttpSecurity.requestMatchers but I have call authorizeRequests and antMatchers to specify the security rules.
When should I use
http.requestMatchers()
.antMatchers("/secure/**","/patients/**","/patient/**", "/hello/**")
.and()
.authorizeRequests().antMatchers("/secure/**","/books/**","/book/**", "/hello/**")
.hasAnyRole("ADMIN","USER");
over
http
.authorizeRequests().antMatchers("/secure/**","/books/**","/hello/**", "/hello/**")
.hasAnyRole("ADMIN","USER");
A scenario would help me to understand the use-case of HttpSecurity.requestMatchers
I did look into requestMatchers, but still not clear to me
If you need to configure multiple HttpSecurity in your application, than you would typically use HttpSecurity.requestMatchers() or one of the alternative (but similar) configuration options:
HttpSecurity.requestMatcher(RequestMatcher)
HttpSecurity.antMatcher(String)
HttpSecurity.mvcMatcher(String)
HttpSecurity.regexMatcher(String)
See the reference in 6.10 Multiple HttpSecurity
For example, if your application has a set of API's rooted at the base path /api and another category of endpoints for the admin section of the application rooted at the base path /admin, than you might define 2x WebSecurityConfigurerAdapter for your application as such:
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(1)
public static class ApiWebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/api/**")
.and()
.authorizeRequests()
.antMatchers("/api/endpoint1")
.hasRole("USER1");
}
}
#Configuration
public static class AdminWebSecurityConfig extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/admin/**")
.and()
.authorizeRequests()
.antMatchers("/admin/endpoint1")
.hasRole("ADMIN1");
}
}
}
However, if you only provide 1x WebSecurityConfigurerAdapter than you don't need to configure HttpSecurity.requestMatchers() (or any of the alternatives) because it will automatically default to HttpSecurity.requestMatcher(AnyRequestMatcher.INSTANCE). So for these configuration cases, this is sufficient:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(...
}
}
Hopefully, this makes sense?

Request method 'POST' not supported when #EnableOAuth2Sso

I am receiving a Request method 'POST' not supported error when I #EnableOAuth2Sso on my Spring Boot 1.5.9 / Angular 5 app.
GET requests work fine, and the JSESSIONID cookie looks like it's setting itself just fine on the front-end. Cookie is getting passed with all requests, and matches.
In the Response Header: Status Code: 405 Allow: GET, HEAD
This is my first Stack Overflow question, I've done all of my usual sleuthing and can't seem to get to the bottom of this one. I apologize in advance for any oversights in my asking / formatting of this question.
#SpringBootApplication
#EnableOAuth2Sso
#EnableOAuth2Client
public class CompanyApplication {
public static void main(String[] args) {
SpringApplication.run(CompanyApplication.class, args);
}
}
Relevant Controller
#RestController
#RequestMapping("api")
public class CompanyController {
#Autowired
CompanyRepository companyRepository;
#Autowired
ContactRepository contactRepository;
#PostMapping("companies")
public Company createCompany(#Valid #RequestBody Company company) {
logger.info("*** Starting POST request of company name: {}", company.getName());
company = updateContacts(company); // pass updated contact info into the Contact DB
companyRepository.save(company);
logger.info("*** Successful POST request of company: {}, ID: {},", company.getName(), company.getId());
return company;
}
Config settings:
security.oauth2.client.clientId=myID
security.oauth2.client.clientSecret=mySecret
security.oauth2.client.accessTokenUri=https://myserver.com/connect/token
security.oauth2.client.userAuthorizationUri=https://myserver.com/connect/authorize
security.oauth2.client.scope=openid,profile,email
security.oauth2.resource.userInfoUri=https://myserver.com/connect/userinfo
Angular service:
public updateCompany( companyData: Company ) {
return this.http.post(this.url, companyData);
}
Edit:
I followed the advice of #theLearner below, but still wanted to add CSRF (XSRF) protection. This is how I ended up doing it:
In app.module.ts add HttpClientXsrfModule to imports (I'm on Angular 5).
Remove #EnableOAuth2Sso from root CompanyApp class.
Config as follows:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
authorizeRequests().anyRequest().authenticated().
and().
csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
There are following things you need to do.
In application properties add this config:
security.oauth2.resource.filter-order=3
More info about this is here.
Since you have not posted your Spring security config yet, not sure how is it right now. But it should look like this:
#Configuration
#EnableOAuth2Sso
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() // or replace this with tour csrf token repository
.authorizeRequests()
.anyRequest().authenticated();
}
This SO post explains if #EnableOauth2Sso is not used carefully it can really mess up entire security configuratiin

Spring Security exclude URL on custom filter

#SuppressWarnings("SpringJavaAutowiringInspection")
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder
authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService);
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
return new JwtAuthenticationTokenFilter();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/test").permitAll()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated();
httpSecurity.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
}
I have a custom filter that runs before Spring Security. I want to be able to exclude some URLs (like /test) from the filter and Spring Security and others to be intercepted (like /api/**).
When using postman to test localhost/test it still goes through the filter even though I have antMatchers("/test").permitAll().
How do I bypass the filter?
You can disable the Spring Security filter chain for some URLs, see WebSecurity#ignoring:
Allows adding RequestMatcher instances that should that Spring Security should ignore. Web Security provided by Spring Security (including the SecurityContext) will not be available on HttpServletRequest that match. Typically the requests that are registered should be that of only static resources. For requests that are dynamic, consider mapping the request to allow all users instead.
Example Usage:
webSecurityBuilder.ignoring()
// ignore all URLs that start with /resources/ or /static/
.antMatchers("/resources/**", "/static/**");
Therefore, you can override WebSecurityConfigurerAdapter#configure:
Override this method to configure WebSecurity. For example, if you wish to ignore certain requests.
To ignore path /test you have to add following method to your configuration:
public void configure​(WebSecurity web)
webSecurityBuilder
.ignoring()
.antMatchers("/test");
}

How to ignore Spring Security config for every thing except a pattern

I have a rest webservice configured as a spring boot application.
All my rest urls have a base path "/api/...".
I am also serving static content from my application.
I need to configure security ONLY for the web service i.e., URLs that start with "/api/..." but give the other static content w/o applying security.
I've only seen examples where we filter some url patterns via:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/*");
}
but not otherwise ...
Use the antMatcher method of HttpSecurity class:
#Configuration
#EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**");
// add security constraints for /api/... here
}
/* rest of config */
}
Instead of antMatcher, you can you regexMatcher wich can be a negation pattern
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().regexMatchers(XXXXX);
}
Answer to your last comment, if you are using latest spring framework and spring security then define below class and security config as per the config standards.
package org.springframework.security.samples.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
Also, look at below URL if you still find it difficult to get started with spring security.
http://docs.spring.io/spring-security/site/docs/3.2.6.RELEASE/reference/htmlsingle/#hello-web-security-java-configuration

Enabling Spring Security makes Swagger output text/plain instead of HTML

Swagger works! I can interact with http://localhost:8090/sdoc.jsp and everything is fine.
I add the following to pom.xml...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
I also add the following two files:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if( !Authenticate.authenticate(name, password) )
return null;
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
and
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeRequests()
.anyRequest().permitAll()
.antMatchers("/**").authenticated().and()
.formLogin().loginPage("/login").permitAll().and()
.httpBasic()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider());
}
}
At this point if I visit the same URL that was previously working I now instead get a response type of "text/plain" and instead of a pretty HTML looking browser I see source code.
If I revert the change and remove the two files from project and remove JAR file it works again.
How do I get Spring Security and Swagger to play nice? What am I doing wrong.
I suspect this is due to Spring-Security's effect on the content-type headers (http://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/headers.html#headers-content-type-options).
From the docs -
Historically browsers, including Internet Explorer, would try to guess the content type of a request using content sniffing. This allowed browsers to improve the user experience by guessing the content type on resources that had not specified the content type. For example, if a browser encountered a JavaScript file that did not have the content type specified, it would be able to guess the content type and then execute it.
The problem with content sniffing is that this allowed malicious users to use polyglots (i.e. a file that is valid as multiple content types) to execute XSS attacks. For example, some sites may allow users to submit a valid postscript document to a website and view it. A malicious user might create a postscript document that is also a valid JavaScript file and execute a XSS attack with it.
Again, from the docs, in order to override the default -
#EnableWebSecurity
#Configuration
public class WebSecurityConfig extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// ...
.headers()
.contentTypeOptions();
}
}
Wow, I figured it was something along these lines. Thanks so much
When I tried this and it started working
.headers()
.disable()
I narrowed the default contentTypeOptions down to..
.headers()
//.contentTypeOptions() // If this is uncommented it fails.
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.frameOptions()
.and()

Resources