Principal is null in controller when using http.csrf().disable() - spring

I have a controller and POJO that I want to test. The GET to the REST interface forces a login and returns a principal object and so all is good. I was able to extend WebSecurityConfigurerAdapter to enable and username and password for testing.
However, during testing, the Spring framework requires a CSRF token for a POST request. Since I have no UI and I am only testing the REST interface I want to disable it temporarily.
So I extended WebSecurityConfigurerAdapter as per the documentation:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
However this disabled the authentication. My controller receives a Principal object that is null. Here is my controller:
import java.security.Principal;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.neutech.model.ShoppingCart;
#Scope("session")
#RestController
#RequestMapping("/cart/api/v1")
public class SomeController {
#RequestMapping(value = "/thing", method = RequestMethod.POST)
public void create(#RequestBody String stuff,#AuthenticationPrincipal Principal user) {
// do stuff
}
I have tried various flavours of setting CSRF for specific URLs or HTTP verbs. All with the same result. The principal delivered to the controller is null.
After scouring the net for some kind of resolution to this I can come up with nothing. There are lots of examples tell me to do exactly what I am doing. However I only find only other similar type questions.
Can someone explain to me what I am doing wrong?

In order to enable authentication change your configure method, try this:
http
.csrf().disable()
.authorizeRequests()
.anyRequest()
.fullyAuthenticated();

If you use Spring Boot 1.5, you could disable CSRF by properties, see
Spring Boot Reference Guide:
security.enable-csrf=false # Enable Cross Site Request Forgery support.
If you use Spring Boot 2.0, you have to write a complete Spring Security configuration, see Spring Boot Security 2.0:
Custom Security
If you want to configure custom security for your application, you will need to add a WebSecurityConfigurerAdapter that adds all the bits that you want to configure. In order to avoid ordering issues with the WebSecurityConfigurerAdapter, Spring Boot auto-configuration will back off completely.
Example:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
}
}

Related

Spring security configuration does not apply with Spring SimpleUrlHandlerMapping

I am writing a spring boot application in which I am registering a URL to a bean via the SimpleUrlHandlerMapping configuration. Why am I not using the #Controller or #RequestMapping classes to do this ?!! Because I want to dynamically register URL's during runtime.
I am using the following code to register a simple URL to a controller
#Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/index", "myController");
mapping.setMappings(urlProperties);
return mapping;
}
The above code is working fine, I am able to hit the controller bean registered with the name "myController".
The issue appears when I use spring security. I introduced spring security and configured InMemoryAuthentication, and set my configuration as follows.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/index").permitAll()
.anyRequest()
.permitAll();
}
After doing this when I try to access /index path, it throws a 403, forbidden error. I have tried with permitAll() and fullyAuthenticated() configurations. It doesn't seem to work. However, any Controller class registered with the #Controller and #RequestMapping annotations are perfectly working fine with Security.
So, my assumption is that Spring Security is not aware of the dynamically registered URL's via the SimpleUrlHandlerMapping.
How do I solve this ? Is there a way I can tell spring security to include my dynamic URL registrations ? Unable to find any article on this online.
Suggestions and help much appreciated.
UPDATE:
Why csrf().disable() does works
CSRF stands for Cross Site Request Forgery
In simple words, it is one kind of token that is sent with the request to prevent the attacks. In order to use the Spring Security CSRF protection, we'll first need to make sure we use the proper HTTP methods for anything that modifies the state (PATCH, POST, PUT, and DELETE – not GET).
CSRF protection with Spring CookieCsrfTokenRepository works as follows:
The client makes a GET request to Server (Spring Boot Backend), e.g. request for the main page
Spring sends the response for GET request along with Set-cookie header which contains securely generated XSRF Token
The browser sets the cookie with XSRF Token
While sending a state-changing request (e.g. POST) the client (might be angular) copies the cookie value to the HTTP request header
The request is sent with both header and cookie (browser attaches the cookie automatically)
Spring compares the header and the cookie values, if they are the same the request is accepted, otherwise, 403 is returned to the client
The method withHttpOnlyFalse allows angular to read XSRF cookie. Make sure that Angular makes XHR request with withCreddentials flag set to true.
For more details, you may explore the following
Will Spring Security CSRF Token Repository Cookies Work for all Ajax Requests Automatically?
AJAX request with Spring Security gives 403 Forbidden
Basic CSRF Attack Simulation & Protection with Spring Security
Updated method configure(HttpSecurity http)
http
.csrf()
.ignoringAntMatchers("endpoint-to-be-ignored-for-csrf")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.authorizeRequests()
.antMatchers("/index").permitAll()
.anyRequest().authenticated();
Endpoint specified in antMatchers with permitAll() should not required authentication and antMatchers("/index").permitAll() should work fine.
Make sure your security configuration class is annotated with #EnableWebSecurity and #EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
The security configuration class is in follows the package structure and scanned by Spring. spring-component-scanning
You may find the minimal working example here
SecurityConfiguration.java
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// https://stackoverflow.com/a/56389047/10961238 -> WebSecurity vs HttpSecurity
// Add this method if .antMatchers("/index").permitAll() does not work
#Override
public void configure(WebSecurity web) throws Exception {
web.debug(true);
// web
// .ignoring()
// .antMatchers("/index");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/index").permitAll() //commenting this line will be results in 403
.anyRequest().authenticated();
}
}
SampleController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#Controller("myController")
public class SampleController extends AbstractController {
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("::::::::::::::::::::::::::::::::Controller:::::::::::::::::::::::::::::::::");
response.getWriter().print("Hello world!");
return null;
}
}
MainApplication.java
import com.example.mappings.controller.SampleController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import java.util.Properties;
#SpringBootApplication
public class MappingsApplication {
public static void main(String[] args) {
SpringApplication.run(MappingsApplication.class, args);
}
#Bean
public SimpleUrlHandlerMapping sampleServletMapping() {
System.out.println("::::::::::::::::::::::::::::::::SimpleUrlHandlerMapping:::::::::::::::::::::::::::::::::");
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(Integer.MAX_VALUE - 2);
Properties urlProperties = new Properties();
urlProperties.put("/index", sampleController());
mapping.setMappings(urlProperties);
return mapping;
}
#Bean
public SampleController sampleController() {
System.out.println("::::::::::::::::::::::::::::::::Setting SampleController:::::::::::::::::::::::::::::::::");
return new SampleController();
}
}
application.properties
spring.security.user.name = user
spring.security.user.password = user
spring.security.user.roles = ADMIN

How to support basic authentication and bearer authentication for the REST API project

I am new to Spring Security & still learning it so my question can be naive please bear with it.
I have Sprint Boot Rest API project which exposes certain APIs. I have already implemented the bearer token based authentication for all the APIs.
e.g /user , /resource, /appointment
Now for few apis for a particular controller I would like to have the basic authentication implemented. These Apis will be consumed by another service which is not exposed to public.
In order to have the security for the APIs I would like to have basic authentication in place for these apis.
e.g /internal/api1 , internal/api2 .. and so on
I am not able to distinguished between urls in the ResourceServerConfigurerAdapter & WebSecurityConfigurerAdapter. Also not sure which should be used for adding basicAuth() using the antmatchers
What you want, by reading your problem, is to have two authentication types (token and httpBasic) for two diffetent endpoints. It can be achieved by creating two different WebSecurityConfigurerAdapter beans. Spring Boot enables this and can be done like bellow:
#Order(1) - /resource|user|appointment/** protected by bearer token authentication.
#Order(2) - /internal/** protected by basic auth.
View docs for Spring Boot and sample code here.
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(1)
public class ApiSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/resource/**")
.antMatcher("/user/**")
.antMatcher("/appointment/**")
.authorizeRequests()
.anyRequest().authenticated()
.and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
#Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/internal/**")
.and()
.httpBasic();
}
}
}

How to disable actuator security without disabling it totally with Spring Boot 2

I'm using Spring Boot Security with OAuth2. I wan't to disable security for health endpoint.
I can totally disable security or write my own implementation of WebSecurityConfigurerAdapter and disable autoconfigured one.
But how to modify existing implementation of WebSecurityConfigurerAdapter (OAuth2SsoDefaultConfiguration)?
I tried to create my own configuration without disabling autoconfigured one, but it is impossible due to Order conflicts.
Here is the error message:
Caused by: java.lang.IllegalStateException: #Order on WebSecurityConfigurers must be unique.
Order of 100 was already used on SecurityConfiguration$$EnhancerBySpringCGLIB$$9505fc58#13f182b9,
so it cannot be used on
org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoDefaultConfiguration$$EnhancerBySpringCGLIB$$dc290e2b#5ee0cf64 too.
Also, I tried to explicitly set higher order for my own security configuration, but looks like autoconfigured one overrides mine.
So how to override specific security rules without reimplementing whole configuration?
You need to implement the following method in your
#SpringBootApplication class
#SpringBootApplication
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class BusinessLogicServiceApplication extends ResourceServerConfigurerAdapter {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext context =
SpringApplication.run(BusinessLogicServiceApplication.class, args);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/health").permitAll().anyRequest().authenticated();
}
}
#Configuration
#EnableOAuth2Sso
class MyConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/health")
.permitAll()
.anyRequest()
.authenticated();
}
}
Make sure you are using #EnableOAuth2Sso over a WebSecurityConfigurerAdapter class. It's important because it will include OAuth2SsoCustomConfiguration which basically copies the functionality of OAuth2SsoDefaultConfiguration#configure.
You might also want to show full health details:
management:
endpoint:
health:
show-details: always
Following are the possible checks.
Solution 1 :
Ensure that you are using
org.springframework.core.annotation.Order
instead of
org.apache.logging.log4j.core.config.Order
Since Spring didn't parse the correct annotations, it was assuming the default value 100 for both configurations.
Solution 2:
Maybe you have annotated another class with the #EnableWebSecurity annotation. Be aware that only one class can implement this annotation.
Solution 3 : Refer this https://stackoverflow.com/a/44076087/6572971
Solution 4 :
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class DemoConfigurer extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().antMatchers("/health").permitAll();
super.configure(http);
}
}
I think you could have your own implementation extending the one you use (OAuth2SsoDefaultConfiguration, if I got it right) and then extend the configure method to ignore your health endpoint. It would look more or less like this
#Override
public void configure(final HttpSecurity http) throws Exception {
http.regexMatchers("/health",)
.permitAll()
}
By the way about this
Also, I tried to explicitly set higher order for my own security configuration, but looks like autoconfigured one overrides mine.
The way #Order works, lower numbers have higher priority so it would explain why the autoconfigured was overriding yours. Doc here: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/Order.html
management.security.enabled: false is no longer valid in spring boot 2. we need to take ConfigurerAdapter way. Here is my code below when OAuth2 resource server is used.
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/**
* to disable security for acutator endpoints.
*
*/
#Configuration
public class ActuatorSecurityConfigurer extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/actuator").permitAll();
}
}
management.security.enabled: false
does not work with spring boot 2.x versions
You can also use
management.security.enabled: false
In your application.propeeties (or. yaml). It will automatically remove any security for actuator exposed endpoints

Spring Boot JS App wont work after securing rest-api with Spring Security

I created a simple Spring Boot/ JS App. In a next step I tried to implement an usermanagement feature to handle multiple users.
So I implemented a usermodel and controller and secured all rest-api calls via authentication of spring security.
#Configuration
#EnableWebSecurity
#ComponentScan("package.packagename")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception{
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select email, password, active from accounts where email=?")
.authoritiesByUsernameQuery("select email, role from account_roles where email=?");
}
#Override
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll()
.and()
.authorizeRequests()
.antMatchers("/index.html", "/").permitAll()
.anyRequest().authenticated()
.and()
.logout();
}
}
Additionally to this file I have the SecurityWebApplicationInitializer
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
My problem now is the fact, that if I start the application and try to access it via localhost:8080 I face an 404 Error.
Usually the app should work even without login and it seems that with springsecurity enabled the app is not able to load the js stuff in resources/public directory.
Reading the logs showed the following:
No mapping found for HTTP request with URI [/] in DispatcherServlet with name 'dispatcherServlet'
If I start the App without spring security it works without any problems.
The securing of the api-calls works like a charm - I'm able to login, receive a cookie and use this cookie to authenticate against the api-functions which are secured by springsecurity.
I hope you can help me to resolve my (hopefully small) problem.
Thanks in advance
When you use .formLogin() you need to define the login page or use .httpBasic() auth. So you can use something like this:
.formLogin()
.and()
.httpBasic();
or
.formLogin()
.loginPage("/login.html")
You can read more here
http://docs.spring.io/spring-security/site/docs/3.2.x/guides/form.html
http://www.baeldung.com/spring-security-login
I figured out that I have to add a WebConfig.java class like this:
import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
#ComponentScan
public class WebConfig extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter {
}
Now Spring is able to resolve the / call properly. I just have to make sure to open the access to all files in public for all users (permitall() function).
Anyway thanks for your help :-)

API key Authentication with spring security in spring data rest api

Am using spring-data-rest for developing my API and I have to use spring security to authenticate a request. When I pass a authentication details, I have to generate an API+secret key and store it in client side and sent with all further requests from that subject. And have to check it in every request and if they logout I have to regenerate key when they login again. I have implemented security as like this,
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("test").password("test").roles("test");
}
#Override protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.antMatchers("/**").hasRole("test")
.anyRequest().anonymous()
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
}
and registered filter via java config.
and I have look at this tutorial which is exactly the same am looking for. I am not good with spring and so I need to know whether am moving correctly.
Am going to implement facebook login and no registration in my site, everyone will login with facebook to access site or my app. So i wont have password too. Guide me in a proper way that how I could progress to achieve this. Thanks in advance.

Resources