Spring Security, secured and none secured access - java

I'm doing a little application that requires to login first. But for some 3rd party tool, I want to provide an API that doesn't require login. The login itself works fine, the API itself works, but I can't figure out how to tell Spring Security, that the API can be accessed without the need of authentication. I checked several topics here and on other websites and tried different versions, but none worked. Everytime I try to access the API, I get forwarded to the login form and have to login first.
My Code Looks like this so far, inside my Spring Security config:
/**
* configuration of spring security, defining access to the website
*
* #param http
* #throws Exception
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/open**").permitAll()
.antMatchers("/login**").permitAll()
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.defaultSuccessUrl("/dashboard")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/login?logout")
.and()
.csrf();
}
And my controller:
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class PredictionOpenRestController {
#RequestMapping("/rest/open/prediction")
public String getPrediction() {
return "First Try!";
}
}
Somehow I have to feeling to miss something.

See Spring Security Reference:
Our examples have only required users to be authenticated and have done so for every URL in our application. We can specify custom requirements for our URLs by adding multiple children to our http.authorizeRequests() method. For example:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/signup", "/about").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.anyRequest().authenticated()
.and()
// ...
.formLogin();
}
1 There are multiple children to the http.authorizeRequests() method each matcher is considered in the order they were declared.
2
We specified multiple URL patterns that any user can access. Specifically, any user can access a request if the URL starts with "/resources/", equals "/signup", or equals "/about".
3
Any URL that starts with "/admin/" will be resticted to users who have the role "ROLE_ADMIN". You will notice that since we are invoking the hasRole method we do not need to specify the "ROLE_" prefix.
4
Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA". You will notice that since we are using the hasRole expression we do not need to specify the "ROLE_" prefix.
5
Any URL that has not already been matched on only requires that the user be authenticated
Your second use of .authorizeRequests() overrides the first one.
Also see AntPathMatcher:
The mapping matches URLs using the following rules:
? matches one character
* matches zero or more characters
** matches zero or more directories in a path
Examples
com/t?st.jsp — matches com/test.jsp but also com/tast.jsp or com/txst.jsp
com/*.jsp — matches all .jsp files in the com directory
com/**/test.jsp — matches all test.jsp files underneath the com path
org/springframework/**/*.jsp — matches all .jsp files underneath the org/springframework path
org/**/servlet/bla.jsp — matches org/springframework/servlet/bla.jsp but also org/springframework/testing/servlet/bla.jsp and org/servlet/bla.jsp
Your modified code:
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/rest/open/**").permitAll()
.antMatchers("/login/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.defaultSuccessUrl("/dashboard")
.loginProcessingUrl("/j_spring_security_check")
.usernameParameter("username")
.passwordParameter("password")
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/login?logout")
.and()
.csrf();
}

Related

Spring Security Not doing it's job

Atm I have a problem where the login page basically doesn't do anything because if you insert the url of a page, you can skip the login.
I'm using this
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").hasAnyRole("Administrator" , "Member")
//.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error")
.successHandler(authenticationSuccessHandler)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.logoutSuccessHandler(logoutSuccessHandler)
.and()
.csrf().disable();
}
Note that //.anyRequest().authenticated() is commented. This line seems to protect my website from accessing through the URL, it is redirecting to the login page.
But if I have it I can't see the css in my page and I get
Refused to apply style from 'http://localhost:8080/login' because its
MIME type ('text/html') is not a supported stylesheet MIME type, and
strict MIME checking is enabled.
How do I protect my website from knowing the URL but also see the CSS in the login page?
Decomment the anyRequest.authenticated
And then you must authorize the access to your static resources.
antMatcher("/css/**").permitAll()
Do the same for javascript and others static resources.
Full config :
http.authorizeRequests().antMatchers("/").hasAnyRole("Administrator" , "Member")
.anyRequest().authenticated()
.and()
.authorizeRequests()
.antMatchers("/css/**").permitAll()
.and()
.formLogin().permitAll()
.loginPage("/login").permitAll()
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error")
.successHandler(authenticationSuccessHandler)
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.logoutSuccessHandler(logoutSuccessHandler)
.and()
.csrf().disable();
I usually configure the access for my resources overriding another configuration, in adition to the one you described:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
It seems more organized for me. You can add this in your Spring configuration class and change the 'resources' by you css. I usually pefer open the 'resources' and put my css inside (in a subdirectory) because it works for all kinds of resources, like images, css, and etc.

Spring boot security add parameter to login

I've a problem, I use spring boot and there I've implemented security. In securityConfig class (below method source code) I've provided username and password parameters to be checked during login process. I need also to check if account is activated, in db I've a column 'activated' with boolean value. And how to provide my own parameter with account activated to login process ?
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/investors/**").hasAnyRole("ADMIN")
.antMatchers("/search**").authenticated()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login")
.usernameParameter("username") < -
.passwordParameter("password") < -
.permitAll()
.and()
.logout()
Thanks for help

Spring Security 404 page for unauthenticated users

I'm using Spring Boot and Thymeleaf. I have a custom 404 template page defined in src/main/resources/templates/error/404.html
This works properly when users are logged in.
However, when they are logged out, they do not get any type of 404 page, they just get redirected back to /login.
I'm thinking my security configuration needs to change but not sure what.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/register*","/resetPassword","/forgotPassword","/login","/404").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
.authenticated().and().formLogin().loginPage("/login").failureUrl("/login?error")
.defaultSuccessUrl("/dashboard").successHandler(successHandler)
.usernameParameter("email").passwordParameter("password")
.and().logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login?logout").and()
.exceptionHandling().accessDeniedPage("/access-denied");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/error**","/resources/**", "/static/**", "/css/**", "/js/**", "/img/**");
}
First of all I encourage you to use indentation when using java config to configure your security for your spring application. It helps with readability.
Note all top level methods on the first indentation (authRequest,formLogin,logout) all configure/update the HTTP object it self. All these elements are from the org.springframework.security.config.annotation.web.builders.HttpSecurity class.
The children of these classes further refine the HTTP security configuration.
http
.authorizeRequests()
.antMatchers("/","/register*","/resetPassword","/forgotPassword","/login","/404")
.permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated() // <--------
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error")
.defaultSuccessUrl("/dashboard")
.usernameParameter("email").passwordParameter("password")
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling()
.accessDeniedPage("/access-denied");
Note .anyRequest().authenticated() is specifically stating that any request must be authenticated. So when you attempt to goto any missing url on your domain it will ask you to login rather than goto the 404 page.
So if you remove that statement it and then try an goto a missing url page it will redirect you to a 404 page.
If you remove .anyRequest().Authenticated() then you can log in without authenticated.
Therefore, do not try to delete. For example, if you go to the address "http://localhost:8080/user", then you will be taken to the authorization page. And if you try to enter the page "http://localhost:8080/user/" then you will be taken to the user page. Please note that links differ only by the forward slash at the end. Of course if you remove ".anyRequest().Authenticated()" in this case you need to add more parameters to antMatchers like "/user" and "/user/"
Therefore, be careful and attentive.

X-CSRF-TOKEN is not generated by Spring Boot

I followed the guide here: http://spring.io/guides/gs/rest-service/ to build my rest service example and now I am trying to enable the CSRF protection. I read that it should be enabled by default, so if I DON'T include:
http.csrf().disable()
in my WebSecurityConfigurerAdapter configuration, the CSRF protectection should be enabled by default, but it does not seem to to be the case. The problem is that the X-CSRF-TOKEN is not generated and not included in my HTTP response in any way.
What am I expected to do, to have the x-csrf-token generated and included in the response and, of course, the csrf protection fully working?
I noticed that, with a similar spring mvc configuration, I get the x-csrf-token generated simply including:
< security:csrf disabled="false"/>
in my security configuration file. But, with spring boot maybe I am getting something wrong and there is no way to have the csrf token generated. Can anybody help me, perhaps pointing me to a working example? My security configuration is:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
// .csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.formLogin()
.successHandler(new RestAuthenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutSuccessHandler(new RestLogoutSuccessHandler());
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.userDetailsService(restUserDetailService);
}
To include the CSRF Token in your csrf protection, you can include CSRFTokenRepository to generate tokens. To illustrate in your case adding a simple line is enough:
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) //HERE ! Defaults XSRF-TOKEN as cookie name and X-XSRF-TOKEN as header name
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.formLogin()
.successHandler(new RestAuthenticationSuccessHandler())
.failureHandler(new SimpleUrlAuthenticationFailureHandler())
.and()
.logout()
.logoutSuccessHandler(new RestLogoutSuccessHandler());}
Using Spring security 5.3.0.Final, one of the ways you can generate the CSRF token is by setting it in the cookie using the following code below.
http.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
You also need to include the generated CSRF token in your request for the server to authorize.
<form>
<input type="hidden" name="_csrf" value="${cookie['XSRF-TOKEN'].getValue()}" />
//Code goes here
</form>
In the event you're using a JS framework, you need to include the token by setting it in the request header.
Here is an example for a JQuery ajax call.
// Get the CSRF token from the cookie
const csrfCookie= document.cookie.replace(/(?:(?:^|.*;\s*)XSRF-TOKEN\s*\=\s*([^;]*).*$)|^.*$/, '$1');
// Add the CSRF token to each ajax request header
settings.beforeSend = function(xhr) {
xhr.setRequestHeader('X-XSRF-TOKEN', springCsrfCookie);
};
$.ajax(settings);
There are other implementations that will suit your needs documented in the following link by Spring | https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5/#servlet-csrf
We had pretty similar issue during our security tests where we suspected that we accidentally disable csfr in configure method of websecurityconfig class,by default it is enabled. by changing the congfigure method as shown below , we had spring automatically generate csfr tokens.
websecurityconfig class configure method==>
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/login","/loginError","/home","/interruption").permitAll()
.antMatchers("/admin").hasAuthority(Roles.ROLE_PREFIX.role()+Roles.HALLEYYNT01.role())
.antMatchers("/requests").hasAuthority(Roles.ROLE_PREFIX.role()+Roles.CCHALLEYLOGIN.role())
.antMatchers("/solrequests").hasAuthority(Roles.ROLE_PREFIX.role()+Roles.SOLHALLEYLOGIN.role())
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
//.failureUrl("/loginError")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/")
.and()
.logout().clearAuthentication(true).invalidateHttpSession(true).deleteCookies("JSESSIONID")
.logoutSuccessUrl("/login");
//.and()
//.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}

Disable BasicAuth on specific sub paths

I know there are some question on stackoverflow, but nothing helped...
http
.addFilterBefore(RestConfiguration.getCorsFilter(), ChannelProcessingFilter.class)
.authorizeRequests() //Authorize Request Configuration
.antMatchers("/api/**").hasRole("API")
.antMatchers("/api/confirm/**").permitAll()
.antMatchers("/api/version").permitAll()
.anyRequest().authenticated()
.and() //HTTP basic Authentication only for API
.antMatcher("/api/**").httpBasic()
.and() // angularjs requires csrf
.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.csrf().disable();
I've a api path with some sub paths. But I want to access two of them without basic auth (confirm/** and version).
How can I do that? I always get the login dialog.
You should use antMatchers("/api/**").hasRole("API") rule after /api/confirm/** and /api/version. Current ordering is not quite right:
.antMatchers("/api/confirm/**").permitAll()
.antMatchers("/api/version").permitAll()
.antMatchers("/api/**").hasRole("API")
To sum up, if you want to only secure /api/* and let public access on /api/confirm/**, /api/version and all other paths without api prefix, you should have a HttpSecurity config like this:
http
// Same as before
.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/confirm/**").permitAll()
.antMatchers("/api/version").permitAll()
.antMatchers("/api/**").hasRole("API")
.anyRequest().permitAll();
// Same as before

Categories

Resources