Currently in my SecurityConfig.java class file where I define my KeycloakWebSecurityConfigurerAdapter I want to define so that every GET request can be done by two different roles. But only one role can do the other types of HTTP requests (POST, PUT, PATCH etc). How can this be achieved in my code below:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers(HttpMethod.GET).hasAnyRole("user", "admin")
.anyRequest().hasRole("admin");
}
What happens is that when trying to do POST request I get access denied 403. GET requests works fine. Any ideas?
You should disable csrf on your configure method :
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable().authorizeRequests().anyRequest().authenticated();
}
}
You should not use KeycloakWebSecurityConfigurerAdapter nor anything else from Keycloak libs for Spring, it is deprecated.
Instead, you can follow this tutorial which proposes two solutions based on:
spring-boot-starter-oauth2-resource-server which requires quite some Java conf
spring-addons-webmvc-jwt-resource-server which enables to configure most of security from properties (way simpler than preceding)
All tutorials linked here show how to map Keycloak roles to spring-security authorities (and will keep CSRF protection enabled, even for stateless resource-servers).
Related
I have a Spring Boot REST API. Due to a security policy I need to have CSRF protection enabled for endpoints accessed by the browser. However, this API will also be accessed by non-browsers. Is there a way I can create two sets of endpoints, one accessible by browsers only with CSRF enabled and the other accessible by non-browsers only with CSRF disabled?
When you configure your CSRF protection using the DSL, like this http.csrf()... you can tell which requests you want the CSRF protection to be applied by passing a RequestMatcher, like so:
http.csrf(csrf -> csrf.requireCsrfProtectionMatcher(new MyBrowserRequestMatcher()));
And your implementation of RequestMatcher could verify if the HttpServletRequest contains the header X-Requested-With: XMLHttpRequest or check the User-Agent.
Just keep in mind that the headers can be changed and you have no guarantee that the request actually come from a browser or non-browser app.
I think you could have separate URL bases for the browser requests and API requests.
For example, you could have all the endpoints that are to be queried by non-browsers under /api/... and in your SpringBootSecurityConfiguration class and configure(HttpSecurity http) method you could conditionally disable CSRF with http.csrf().disable(); if the pattern matches (great tutorial can be found here)
Edit: here is another answer that might be useful.
As #ferrouskid said, I created two URL one for browsers and other for non-browsers:
In spring security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().ignoringAntMatchers("/withoutCsrf/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.cors().disable();
//complete your configuration }
In controller:
#Controller
#RequestMapping({"books","withoutCsrf/books"})
public class BookController {}
I am using spring boot and very new to spring security, but I wanted basic security to my web application. What I did was add on my pom.xml the needed dependencies and added this java class to my project:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**","/event/**","/ticket/**")
.hasRole("USER")
.and()
.formLogin();
}
}
After running my web application, I run into the login page, where I put user/password and then it goes to my web application. However, the commands don't work. I am pushing some buttons that should send signals to my MySql database, but nothing happens. It's like the front-end isn't connected to the back-end anymore. I am using AngularJS for front-end and a View Controller that navigates between pages. Rest of the application is REST-based. Any idea why this might happen?
Later Edit: Now I understand, the problem that I have is that after authenticating, I get 403 status codes on my end-points. Any idea how I might fix it?
Later Editv2: Looks like I don't get authorized on my POST requests, my GET ones work fine...here are some of my POST end-points: /event/buy_ticket/{id} , /ticket//cancel_ticket/{id}
angular.min.js:101 POST http://localhost:8080/event/buy_ticket/2 403 ()
I even tried to explicitly say it to permit it, but I still get 403...
http.authorizeRequests()
.antMatchers("/**","/event/**","/ticket/**","/event/buy_ticket/2")
.permitAll()
.and()
.formLogin();
Later later later edit:
Disabling csrf worked
Getting 403 Forbidden error codes means that Spring is receiving your requests but choosing to stop processing them. From the Wiki page on HTTP 403:
Authentication was provided, but the authenticated user is not
permitted to perform the requested operation.
If I had to wager, I would say the problem is that you have not specified what resources and endpoints should be accessible and how. If memory serves me right, Spring Security will, by default, lock down everything super tightly so you need to explicitly tell it what to leave open. Below is a sample from my own security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // require authorization
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // for the CORS preflight check
.antMatchers("/login", "/api/open/**", "/resources/**").permitAll() // the open API endpoints and resources
.antMatchers("/logout", "/api/secured/**").authenticated(); // lock down these endpoints
...additional configurations...
}
All endpoints that should be freely available are prefaced with "/api/open/" while all endpoints that should be protected by Spring Security are prefaced with "/api/secured/". The exceptions are the logout and login endpoints, since those tie into Spring Security directly.
Here's a great blog post - and the related repo - that shows off how to implement Spring Security that plays nice with AngularJS, even as a Single Page Application, which are notoriously annoying to secure.
Edit: You might be running up against CSRF protection, which is enabled by default in Spring Security - see this post. CSRF will allow HTTP "safe" methods like GET, HEAD, and OPTION but block "unsafe" methods like PUT, POST, DELETE, etc if those methods do not provide the proper token (since no CSRF was configured, those request don't have a token -> blocked). For testing purposes, you can disable it by adding http.csrf().disable() to the configure() method.
I'm using Spring Boot to create my microservices and I'm enabling the OAuth2 to add security to my services.
However, there are some methods that I can not understand what are the differences between then. For example, I have the following code:
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(STATELESS);
http.authorizeRequests()
.antMatchers(POST, "/v1/files/").access("#oauth2.clientHasRole('ROLE_CLIENT')");
}
In this example, I used the access method to check if the system that is going to access my services has the ROLE_CLIENT role.
The question is : what are the main differences between the following methods:
hasRole
hasAuthority
access
hasRole(NAME) checks that client has ROLE_NAME whether hasAuthority(NAME) checks only NAME role.
hasRole("CLIENT") is equivalent to hasAuthority("ROLE_CLIENT")
I am trying to secure my webapp such that all requests need to be made using https. I am using a Java based configuration, in particular I have a class which extends WebSecurityConfigurerAdapter. I have attempted to configure my security using the #Override protected void configure(HttpSecurity http) {} method as detailed here and here.
I've tried both of those and many variants of configuring the HttpSecurity object, as well as several variants of authentication. In almost all cases, I come up with the following issue:
[nio-8080-exec-2] o.a.coyote.http11.Http11NioProcessor : Error parsing HTTP request header
I tried doing some reading into this but a lot of the search results turned out to be dead ends for me. My assumption was that the solution that was hinted at in the two posts I referenced would give me roughly the correct answer, but is there any additional configuration I need to do before I can get https working? If so what is it, and if not, what am I missing here?
Below is my current configuration, which replicates the error described above (it is basically the default one plus the https channel):
#Configuration
#EnableWebSecurity
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
// Specify the authentication mechanisms that will allow user access to the site.
#Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.inMemoryAuthentication()
.withUser("user").password("password").roles("ROLES_USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic()
.and()
.requiresChannel().anyRequest().requiresSecure();
}
}
On the front end I am getting an SSL Connection Error : Unable to make a secure connection to the server. This may be a problem with the server, or it may be requiring a client authentication certificate that you don't have.
As #M.Deinum mentioned in comments, the difficulty was in the fact that I had not configured SSL in my application.properties. Creating one akin to the following:
server.port=9090
server.ssl.key-store=classpath:keystore.jks
server.ssl.key-store-password={password}
security.require-ssl=true
Resolved the issue.
I was trying to enable csrf filter for some specific api calling and for the others no need of csrf filter.What I have done is
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().antMatchers("/public/**").permitAll();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.csrf().disable().addFilterBefore(new StatelessCSRFFilter(), CsrfFilter.class).authorizeRequests().antMatchers("/rest/**").permitAll();
}
The problem is when I was calling localhost:8080/public/hello
An error was showing
"message": "Missing or non-matching CSRF-token"
I am using Spring boot and Spring Security.
Thanks for any help.
http.antMatcher("/public/**").authorizeRequests().anyRequest().permitAll();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.antMatcher("/rest/**").addFilterBefore(new StatelessCSRFFilter(), CsrfFilter.class).csrf().disable();
Or you can do like this.I think both will be working.
http.antMatcher("/public/**").csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.antMatcher("/rest/**").addFilterBefore(new StatelessCSRFFilter(), CsrfFilter.class).csrf().disable();
Try changing http.csrf().disable() to http.antMatcher("/public/**").csrf().disable() and http.antMatcher("/rest/**").csrf().disable(). You will likely have to put those two lines each in their own WebSecurityConfigurerAdapter.
That will tell Spring-Security to create multiple HTTP Security Filter Chains (akin to having multiple <http/> blocks in the XML model).