I wrote one program using spring security framework and used JDBC approach using DataSource. Following is piece of code.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/users/**").hasRole("USER")
.antMatchers(HttpMethod.POST, "/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PUT, "/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.PATCH, "/users/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/users/**").hasRole("ADMIN")
.and().httpBasic();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
}
This program is using default table what spring framework expect.
Now my question, because here I am using httpBasic authentication approach when I will come GET /users url, does spring framework hit the table on every request and valid user with credential or after first authentication it cache and validate against that cache.
Could someone help me to understand it.
Thanks in advance
If you use stateless HTTP Basic, the database will by default be "hit" on every request. If you find that a problem you can set a cache like this:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource).userCache(userCache);
}
See UserCache javadoc for details about which cache implementations you can use.
Because you're using httpBasic(), then the query would be performed each time - this is the nature of stateless authentication, which is what folks using httpBasic() are typically driving at.
You could cache the JDBC query results with an L2 cache or similar.
Or, if you are okay with some authentication state, then you could
add session management, as another answer indicates. Doing this means that the first request would include credentials in the Authorization header and its response would include a session cookie. Subsequent requests would send that session cookie back, instead of the Authorization header, until the session expires.
use a token (like an OAuth token). You can instead present your credentials to an Authorization Server. The Authorization Server will exchange this for a token that you can then supply to the Authorization header (Authorization: Bearer) instead of the user's credentials (Authorization: Basic).
This behaviour is configured by sessionManagement().sessionCreationPolicy() when configuring HttpSecurity.
Unless you set it to sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) ,its default value is IF_REQUIRED which will cache the authentication result (i.e. SecurityContext) in the HttpSession.Subsequent requests from the same session will simply get the authentication result from the session rather than hitting the database to validate the credential .
Related
i am trying to implement a simple Spring security project but I am facing an issue where the behavior of http.authorizeRequests().anyRequest().authenticated(); is not understandable. what i expect from this method is to prevent all incoming requests until the user is authenticated but in my case all the requests are go through and no interception happened. the normal behavior of preventing request to go through took a place when I un-comment the lines which include hasAnyAuthority.
Below is My security configs
#Override
protected void configure(HttpSecurity http) throws Exception {
CustomAuthFilter customAuthFilter = new CustomAuthFilter(authenticationManagerBean());
//override behavior of url for our api
customAuthFilter.setFilterProcessesUrl("/api/login");
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/api/login/**").permitAll();
http.authorizeRequests().antMatchers("/register/**").permitAll();
/////http.authorizeRequests().antMatchers(GET,"/api/users/").hasAnyAuthority("ROLE_USER");
//////http.authorizeRequests().antMatchers(POST,"/api/user/save/**").hasAnyAuthority("ROLE_ADMIN");
http.authorizeRequests().anyRequest().authenticated();
http.addFilter(customAuthFilter);
http.addFilterBefore(new CustomAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
i have resolved the issue , it was just a miss-understanding of how authenticated method works.
so first Spring security checks if the user authenticated and and then checks if this endpoint need any type of authorization. if authenticated and not authorization exist the user would be redirected to the endpoint. it make sense for me now.
Thank you.
I have override the configure method of WebSecurityConfigurerAdapter class as:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin();
}
I have APIs as /admin, /admin/user, /admin/user/test. When i login as admin i can access all the three URLs. I just wanted to know the difference between '/admin/**' and '/admin',
In case of /api/**, hasRole(...) will be authorized to all the requests that starts with the pattern /api.
And in case of /api, hasRole(...) will be authorized to only one request i.e. /api
In the above question only the '/admin' request is authorized to 'ADMIN' role. We can also access the other URLs because other URLs just need to be authenticated ignoring the role. We can also access the '/admin/user' or '/admin/user/test' while logging with user. If we have used antPattern as '/admin/**', then we won't be able to access those APIs through the session of user.
I am new to Spring Security and i was about to post the question but after spending some time, i came to know a little about it, so i also included my understanding for suggestions.
In this application I dont want any spring authentication because Authentication is being done by a SSO service, the SSO sends me the the userId and role
Now, I want to use the role being sent for authorisation. The code to set the role is in the implementation of the UserDetailsService
The problem I have is that the UserDetailsService only gets called if I add formLogin to the security config. And of course I dont want formLogin at all as I dont want authentication. So how can I do authorisation without calling formLogin?
#Override
protected void configure(HttpSecurity http) throws Exception{
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceEncoding(true);
http
.addFilterBefore(encodingFilter,CsrfFilter.class)
.addFilterAfter(new MdcFilter(), SwitchUserFilter.class)
.addFilterAfter(new ParameterSanitizerFilter(), MdcFilter.class)
.authorizeRequests()
.antMatchers("/**").hasAnyRole(StringConstants.USER_ROLE, StringConstants.MANAGER_ROLE);
//.formLogin();
}
**** EDIT
Thanks for the help, pczeus' link seems to be the same issue. However still struggling!
I set up a controller on RequestMapping path "/" but it doesnt get fired (the purpose of the controller is to get the role from session and set it in a new UserAuthenticationInfoService - as per pczeus link)
so I need the controller to execute before this line:
.antMatchers("/**").hasAnyRole(StringConstants.USER_ROLE, StringConstants.MANAGER_ROLE);
how can I do it?
Is it necessary to protect JAX-RS requests against CSRF?
By definition REST is stateless and therefore exists no session id (session cookie), because there is no session at all (see also https://stackoverflow.com/a/15746639/5277820).
My Spring Security Java Configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
#Configuration
#Order(1)
public static class JaxRsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.antMatcher("/services/**")
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/services/**").permitAll()
.anyRequest().hasAuthority("ROLE_user")
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
}
}
But I found for example following blog: Stateless Spring Security Part 1: Stateless CSRF protection. Unfortunately the blog does not explain, why one needs CSRF protection.
Is there any other CSRF attack without session cookie?
CSRF attacks don't need a session to exist. A CSRF attack consists in doing something on a user's behalf by tricking him/her into clicking a link or submitting a form that goes to an application where the user is logged in.
Whether basic authentication or a session cookie is used to identify the user is irrelevant.
Note that using a cookie doesn't mean that the app is not stateless. A cookie, just like basic authentication, simply consists in sending an additional header with each HTTP request.
Access tokens are sometimes stored in a (secured http-only at best) cookie, so that clients don't have to bother adding it in each request manually: cookies are automatically attached to the requests by the browsers. This is a reason why CSRF protection needs to be implemented.
The article you linked proposes to have the clients generate and send the same unique secret value in both a Cookie and a custom HTTP header, which is quite smart:
Considering a website is only allowed to read/write a Cookie for its
own domain, only the real site can send the same value in both
headers.
That is, if you receive an email with a fake image targeting http://yourserver.com/admin/deleteAll for example (and the server handles it through GET...), the unique secret won't be set in the request header (an old one could still be present in a cookie): the server must reject the request.
Can I chain multiple instances of AuthenticationEntryPoint in Spring Security 3.2.4?
I attempting to create the following scenario:
A certain URL is secured with Spring Security
The AuthenticationEntryPoint used is LoginUrlAuthenticationEntryPoint
An admin interface can spawn services under this URL
The admin can choose to secure these services with CLIENT-CERT
When a user attempts to access the secure URL:
If the path has been secured with CLIENT-CERT then authentication fails unless they have provided a valid certificate the corresponds to a user in the UserService. Standard Spring Security x509 authentication.
Once the user has been authentication as per the first point, or if the URL is not secured with CLIENT-CERT, they are directed to a FORM based authentication page.
Once they successfully authenticate with a username and password, they are directed to a landing page.
I am running on Tomcat 7.0.54 with clientAuth="want". This works perfectly in a "simple" Spring Security set up - i.e. with one WebSecurityConfigurerAdapter set to x509() and another set to formLogin() as per this example
So, I want a process flow something like the following:
I have had some success with dynamically changing the used authentication method by using a DelegatingAuthenticationEntryPoint but:
When using an AntPathRequestMatcher to map, say, /form/** to a LoginUrlAuthenticationEntryPoint the authentication servlet (/j_spring_security_check) gives a HTTP404 error.
When using an AntPathRequestMatcher to map, say, /cert/** to a Http403ForbiddenEntryPoint the user's details are not extracted from the presented client certificate so this gives a HTTP403 error.
I also cannot see how to force a user to authenticate twice.
I am using the java-config and not XML.
My code:
I have a DelegatingAuthenticationEntryPoint:
#Bean
public AuthenticationEntryPoint delegatingEntryPoint() {
final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> map = Maps.newLinkedHashMap();
map.put(new AntPathRequestMatcher("/basic/**"), new BasicAuthenticationEntryPoint());
map.put(new AntPathRequestMatcher("/cert/**"), new Http403ForbiddenEntryPoint());
final DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(map);
entryPoint.setDefaultEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
return entryPoint;
}
And my configure
#Override
protected void configure(final HttpSecurity http) throws Exception {
defaultConfig(http)
.headers()
.contentTypeOptions()
.xssProtection()
.cacheControl()
.httpStrictTransportSecurity()
.addHeaderWriter(new XFrameOptionsHeaderWriter(SAMEORIGIN))
.and()
.authorizeRequests()
.accessDecisionManager(decisionManager())
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(delegatingEntryPoint())
.and()
.sessionManagement()
.maximumSessions(1)
.sessionRegistry(sessionRegistry())
.maxSessionsPreventsLogin(true);
}
Where decisionManager() returns a UnanimousBased instance. sessionRegistry() returns a SessionRegistryImpl instance. Both methods are #Bean.
I add a custom UserDetailsService using:
#Autowired
public void configureAuthManager(
final AuthenticationManagerBuilder authBuilder,
final InMemoryUserDetailsService authService) throws Exception {
authBuilder.userDetailsService(authService);
}
And I have a custom FilterInvocationSecurityMetadataSource mapped using a BeanPostProcessor as in this example.
Chaining multiple entry points won't really work.
Your best option here might be to just customize the form-login process to check for the certificate if it's needed (before authenticating the user). That would probably simplify the configuration overall. It would really just be the same as a normal form-login setup.
The work done by the X509 filter is quite minimal. So for example, you could override the attemptAuthentication method, call super.attemptAuthentication() and then check that the certificate information matches the returned user authentication information.