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.
Related
i have backend urls for some service to access, and frontend urls for website login to access, my situation is:
/backend/**: HTTPS two-way authentication
/frontend/**: HTTPS one-way authentication and token authentication
I don't want to start two different springboot process.
I have found this answer but springboot not allow to disable client-auth for specific urls:
Spring Boot: Disable Client Auth for specific URL
server:
ssl:
client-auth: need
and this answer maybe helpful, but i don't know how to mix two authentication method in my situation.
How set up Spring Boot to run HTTPS / HTTP ports
please help.
For Spring-Boot 2.7.0 this should be as simple as defining 2 instances of SecurityFilterChain, idealy you'd want one of them to be the default (remove the http.mvcMatcher line) and give the other #Order(1).
Incase of older implementations i'm not 100% sure, for further research you probably find better results looking for a way to support 2 login method depending on endpoint than looking into how to disable certain elements.
#Configuration
public class WebSecurityConfig
{
#Bean
public SecurityFilterChain frontendFilterChain(HttpSecurity http) throws Exception
{
//#formatter:on
http
.mvcMatcher("/frontend/**")
.authorizeRequests(auth -> auth.anyRequest().permitAll());
//Extend with needed authentication
//#formatter:off
return http.build();
}
#Bean
#Order(1)
public SecurityFilterChain backendFilterChain(HttpSecurity http) throws Exception
{
//#formatter:on
http
.mvcMatcher("/backend/**")
.authorizeRequests(auth -> auth.anyRequest().permitAll());
//Extend with needed authentication
//#formatter:off
return http.build();
}
}
```
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 {}
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).
Below is my SecurityConfiguration class, I am using.
#SpringBootApplication
#RestController
public class WebMvcConfig {
#Configuration
protected static class SecurityConfiguration extends
WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
After startup, as soon as I hit my URL (http://localhost:8080/TestApp/), it takes me to the default login page and when I enter the default user Id (user) and password (printed on the console), it take me to my index.html page mapped by the "/" URL via my AngularJS routing. I am able to navigate through the UI but as soon as I submit any $http request (I am trying with a POST request), it gives me 403 on the browser console with my $http request URL.
Can someone help?
Error 403 means that you are forbidden from accessing all the resources.
If you inspect the error details, more probably you will have a message such as Expected CSRF token not found.
From v4 onwards, spring security enables by default csrf protection. This is a good practice as csrf attacks force an end user to execute unwanted actions on a web application in which they’re currently authenticated.
So in a dev environment, adding http.csrf().disable(); will solve your problem. But you should consider adding a csrf token when you want to move to a prod env.
I am using Spring Security to secure an application using OAuth. I wanted to make it stateless so I have a custom authentication filter that authenticates based on a token from the client. My configuration for Spring Security is as follows:
#Override
protected void configure(HttpSecurity http) throws Exception {
// require https
http.requiresChannel().antMatchers("/**").requiresSecure().and().portMapper().http(80).mapsTo(443);
http.antMatcher("/**").authorizeRequests().anyRequest().authenticated().and()
.cors().and()
.csrf().disable()
.addFilterBefore(customOAuth2Filter(), BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
As you can see, I am using SessionCreationPolicy.STATELESS which should mean that Spring Security never uses a session. However, if I login and then try to access a protected endpoint without a token(say, if I close the tab and then open a protected endpoint in a new tab, or if I just refresh the page), the application will give me the data at the endpoint instead of asking me to login again.
So it seems like a session is being used even though I am using SessionCreationPolicy.STATELESS. However, I did notice that the JSESSIONID of each request being sent does not match the JSESSIONID in the set-cookie header of the previous response. Not sure why that is or where the new JSESSIONID is coming from.
How do I make it so subsequent requests without the token will ask for login again? Any insight into what is happening here would be appreciated, thanks.