Spring security - Getting 404 error for REST call - spring

I have an application that I am securing using Spring Security. I enable it using the annotations:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
I then configure it by overriding the configure() method:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.and()
.addFilterAfter(new CsrfCookieGeneratorFilter(), CsrfFilter.class)
.exceptionHandling()
.accessDeniedHandler(new CustomAccessDeniedHandler())
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin()
.loginProcessingUrl("/api/authentication")
.successHandler(ajaxAuthenticationSuccessHandler)
.failureHandler(ajaxAuthenticationFailureHandler)
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/api/logout")
.logoutSuccessHandler(ajaxLogoutSuccessHandler)
.deleteCookies("JSESSIONID", "CSRF-TOKEN")
.permitAll()
.and()
.headers()
.frameOptions()
.disable()
.and()
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.antMatchers("/admin/**").hasAuthority(AuthoritiesConstants.ADMIN);
}
I'm getting redirected correctly to the login page when I try to get to the home page, but once I enter my credentials and try to login, I get the 404 not found error for /api/authentication REST call.
The loginProcessingUrl() method specifies the call to make. I don't have to implement this myself do I, as Spring should do that for me? Is there anything else I'm missing?

As far as I understood the login-processing-url, you have to handle the login process by yourself if you specify a special url to process the login.
Have you tried to just remove this line?:
.loginProcessingUrl("/api/authentication")
As you use springs default login form, you should just be able to remove the line and the generated login form will also change.
Maybe there's another way to solve your problem but this should also work.
If you're looking for an example on how to use custom login forms, this link might helo you.

I figured it out, it was a couple of things I needed to change:
I didn't have an Initializer class. After I added the following class, I went from a 404 not found error, to a 403 error:
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
Note: you can also add the corresponding filter in your web.xml (read more here: http://websystique.com/spring-security/spring-security-4-hello-world-annotation-xml-example/)
The above change registers security with the Spring container. But I still needed to disable CSRF, as I wasn't sending the proper tokens from the client side. For my application, I don't need CSRF just yet so I have disabled it for now. However, this is not recommended so make sure sure you know what you are doing before making this change:
http.csrf().disable()

Related

Spring Security and i18n

I have problem with accessing when I change language on page.
Without .antMatchers("/login*").permitAll() doesn't work but with /login* is almost ok but when I provide in URL 'http://localhost:8080/login?lang=estest123' or sth not related with i18n after login I receive ??language.change_estest123??.
I tried add .regexMatchers("^login\\?lang=[a-zA-Z]{2}|^login\\?lang=_{1}[a-zA-Z]{2}").permitAll() but same result as /login*.
Is any possible to accessing only for i18n ? in other case providing error? Or maybe another way of configuration for i18n which allows in easy way to provide this?
My web security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/registration").permitAll()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
Does the presence of parameters affect the url parsing for Security? Just use /login and it should pass /login and /login?lang=xxx.
But if you want to use direct mapping in your config you can use both of them: .antMatchers("/login", "/login*").permitAll()

Login page needs to be prompted if user is not authorized to access specific controller or URL in spring security. How to achieve that?

I'm using spring-boot, spring-security and JSP. If I click on a button it should go to a controller if user is logged in. Otherwise, it should first ask user to login and then get back to that page. In short, user should see the page if he is logged in. How can I achieve this?
I think filters/antmatchers might be used but I am wondering how the user will get back to that particular page/controller after logging in?
Try using something like this to allow users access to certain pages and then set the default success url accordingly. You can have a home page as I use here represented by "/" and once a user logs in they are redirected to your /welcome page.
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Public access to login, landing, and error pages
http.authorizeRequests().antMatchers("/", "/login", "/errorpage").permitAll();
// Static resource permissions
http.authorizeRequests()
.antMatchers("/css/**", "/fonts/**", "/images/**", "/webfonts/**", "/js/**", "/webjars/**", "/messages/**")
.permitAll();
// Login specifications
http.formLogin().loginPage("/login").defaultSuccessUrl("/welcome", true);
// Logout specifications
http
.logout()
.deleteCookies("remove")
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/")
.permitAll();
}
}
Inside WebSecurityConfigurerAdapter implementation, you need to inform a formLogin and specify the loginPage.
That's just enough to Spring to use the endpoint /login this way.
If you try to access a page without logged, for example /profile, you will be redirected to /login, and after logged, you'll be redirected to /profile
And in this example, you have 3 pages accessible without authentication / ,/homeand/info`
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home", "/info" ).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
...
}

Spring Boot Security - Multiple configurations

I'm working (and struggling a little bit) on an example using spring-boot with spring security.
My system is using a web app and also provide an REST-API, so i would like to have form based security (web) and basic auth (resp api).
As the spring documentation recommend (https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#multiple-httpsecurity), I need to create a multi http web security configuration.
The main code works, but if I use Postman for the test of my RestApi following use-case does not work.
All GET-requests to /restapi/ working without authentication (statuscode 200)
All POST-requests to /restapi/ without the BASIC Auth Header are working (statuscode 401)
All POST-requests to /restapi/ with a correct BASIC Auth Header are work (statuscode 200)
BUT all requests with a wrong BASIC Auth header (f.e. user1/1234567) are returning the HTML-Loginpage defined in the first WebSecurityConfigurerAdapter (FormWebSecurityConfigurerAdapter)
Does anyone has an idea - what is wrong with my configuration?
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Autowired
private static RestAuthenticationAccessDeniedHandler restAccessDeniedHandler;
#Autowired
public void configureAuth(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication()
.withUser("admin").password("{noop}12345678").roles("ADMIN").and()
.withUser("user").password("{noop}12345678").roles("USER");
}
#Configuration
#Order(1)
public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/restapi/**")
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/restapi/**").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(new HttpStatusEntryPoint(UNAUTHORIZED))
.and()
.exceptionHandling().accessDeniedHandler(restAccessDeniedHandler) ;
}
}
/*
Ensures that any request to our application requires the user to be authenticated (execpt home page)
Requests matched against "/css/**", "/img/**", "/js/**", "/index.html", "/" are fully accessible
Allows users to authenticate with HTTP Form Based authentication
Configure logout with redirect to homepage
*/
#Configuration
public static class FormWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/img/**", "/js/**", "/index.html", "/").permitAll()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/index.html")
.permitAll();
}
}
}
I know it is a question from some time ago but I still want to share the answer for people who are struggling with this issue.
After a lot of searching I found out that the /error endpoint in spring boot 2.x is now secured by default. What I mean to say is in the past the /error was a endpoint what had no security at all (or didn't exist). The solution to this issue is quite straight forward.
antMatchers('/error').permitAll()
within your web security adapter configuration(s).
What happens if you don't do this, the security will check the endpoint against your configuration and if it cannot find this endpoint (/error) it will redirect to the standard login form, hence the 302.

Spring Boot, Spring Security specify redirect login url

At my Spring Boot application I need to implement a following scenario:
Anonymous User visits the following page: http://example.com/product-a.html
This User wants to ask a question about this product. It can be done at another page, located by the following address: http://example.com/product-a/ask. User press Ask Question button at the http://example.com/product-a.html and login/registration popup is shown. After successful login User should be automatically redirected to http://example.com/product-a/ask (but currently with a default Spring Security implementation User are redirecting back to the page originator http://example.com/product-a.html)
How to properly with Spring Boot/Spring Security implement/configure this redirect ?
UPDATED
This is my web security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);
http
.csrf().ignoringAntMatchers("/v1.0/**", "/logout")
.and()
.authorizeRequests()
.antMatchers("/oauth/authorize").authenticated()
//Anyone can access the urls
.antMatchers("/images/**").permitAll()
.antMatchers("/signin/**").permitAll()
.antMatchers("/v1.0/**").permitAll()
.antMatchers("/auth/**").permitAll()
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/**").hasAuthority(Permission.READ_ACTUATOR_DATA)
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.failureUrl("/login?error=true")
.usernameParameter("username")
.passwordParameter("password")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl(logoutSuccessUrl)
.permitAll();
// #formatter:on
}
I use OAuth2/JWT + Implicit Flow for AngularJS client
I think you should not use spring's default /login processing url, but create your own e.g /mylogin in a controller. And then you can inject the HttpServletRequest in the method and take the action based on the context of the request for example:
#PostMapping("/mylogin")
public ResponseEntity<> processingLogingURL(HttpServletRequest request){
// switch bases on the request URL after checking security off course
switch(request.getRequestURL()){
//redirect based on the caller URL
}
}
and finally change your .loginProcessingUrl("/login") to .loginProcessingUrl("/mylogin")

Mix HttpBasic and FormLogin in Spring Security with Spring-boot-starter

I use spring-boot-starter 0.5.0.M6 with spring security to build my application which contains:
"/admin/"**: should be accessible to anyone have role ADMIN, form-based login
"/api/"**: should be accessible to anyone have role API, http basic login
My first attempt was:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin()
.defaultSuccessUrl("/admin/home")
.loginPage("/login")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET"))
.permitAll();
http
.authorizeRequests()
.antMatchers("/api/**").hasRole("API")
.and()
.httpBasic();
}
With this approach:
all the "/admin/" and "/api/" can authentication use both basic & form-based login. This is not a critical issue.
when any security issue occurred, eg: authentication failed, or authorization failed, the login form is shown. This is a critical issue, I want if /api/** get authentication failed or authorization failed, it show the basic authentication popup with 401/403 status code.
Then I try with the solution from https://github.com/spring-projects/spring-security-javaconfig/blob/master/samples-web.md#sample-multi-http-web-configuration, but I only able to secure either /api/** or /admin/** but not both, depends on which one I annotated with #Order.
Please give me a hand.
Thanks much
For your api part, use the following. Note the first ant matcher that limits the scope of what is filtered by this security configuration. That was the part I did not understand at first from your reference.
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
// the ant matcher is what limits the scope of this configuration.
http.antMatcher("/api/**").authorizeRequests()
.antMatchers("/api/**").authenticated()
.and().httpBasic().realmName("Sourcing API");
}
}

Resources