I developed several Java applications with Spring Security using XML based configuration.
This time the application is Spring Boot 1.3.0 based and uses API based configuration instead of XML based configuration.
I could not find the API equivalent of create-session="never" (using 'never' or any other value) such as in:
<sec:http create-session="never" ...>
...
</sec:http>
Is there a way to set this, starting from the next class?
#Configuration
#EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
...
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
...
}
}
Thank you for your time.
See http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/config/annotation/web/builders/HttpSecurity.html#sessionManagement--
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
You can specify this by setting a property inside your application.properties.
You can find the spring boot security properties at the docs.
# ----------------------------------------
# SECURITY PROPERTIES
# ----------------------------------------
# SECURITY (SecurityProperties)
security.basic.authorize-mode=role # Security authorize mode to apply.
security.basic.enabled=true # Enable basic authentication.
security.basic.path=/** # Comma-separated list of paths to secure.
security.basic.realm=Spring # HTTP basic realm name.
security.enable-csrf=false # Enable Cross Site Request Forgery support.
security.filter-order=0 # Security filter chain order.
security.filter-dispatcher-types=ASYNC, FORWARD, INCLUDE, REQUEST # Security filter chain dispatcher types.
security.headers.cache=true # Enable cache control HTTP headers.
security.headers.content-type=true # Enable "X-Content-Type-Options" header.
security.headers.frame=true # Enable "X-Frame-Options" header.
security.headers.hsts= # HTTP Strict Transport Security (HSTS) mode (none, domain, all).
security.headers.xss=true # Enable cross site scripting (XSS) protection.
security.ignored= # Comma-separated list of paths to exclude from the default secured paths.
security.require-ssl=false # Enable secure channel for all requests.
security.sessions=stateless # Session creation policy (always, never, if_required, stateless).
security.user.name=user # Default user name.
security.user.password= # Password for the default user name. A random password is logged on startup by default.
security.user.role=USER # Granted roles for the default user name.
Just set security.sessions to the value which fits your needs
This is no longer valid. See MariuszS comment for details.
Related
I'm using Spring Security for Single Sign On implementation. I've added the Spring Security filter chain in my application with annotations, and not in the web.xml.
I want the Spring Security filter chain to only apply to the /saml URLs, and I don't want the /myapi requests to go through Spring Security. But my /myapi requests are still going through the Spring Security filter chain, and Spring's filters.
Here's my code for Spring Security filter registration:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.addFilterBefore(new SamlExtensionUrlForwardingFilter(), DisableEncodeUrlFilter.class)
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/saml/*").authenticated()
.antMatchers("/myapi/*").permitAll()
)
.saml2Login()
.successHandler(new MyAuthenticationSuccessHandler());
return http.build();
}
#Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/myapi/*");
}
With the webSecurityCustomizer(), I tried to exclude the /myapi/ endpoints from the Spring Security workflow, and bypass the Spring Security filter chain and other Spring Security filters for the endpoint.
Additionally, I tried to get the same results with the following as well, assuming that permitAll() would mean Spring Security authorization is not required for the /myapi requests:
.antMatchers("/myapi/*").permitAll()
Neither of the above two worked, and the /myapi requests still go through Spring'a filters like DisableEncodeUrlFilter, CsrfFilter etc. Ideally, I want only /saml/* requests to go through the Spring Security filter chain and Spring related filters.
I have configured my SecurityFilterChain thus:
#EnableWebSecurity
public class WebSecurityConfig {
....
#Bean
public SecurityFilterChain configure(final HttpSecurity http) throws Exception {
http
.csrf().disable()
.cors().disable()
.authorizeRequests()
.antMatchers(HttpMethod.DELETE, "/api/user/*").access("hasRole('ADMIN')")
.antMatchers(HttpMethod.POST, "/api/user").access("hasRole('ADMIN')")
.antMatchers("/auth/login").anonymous()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
Yet, the URL paths are open to any authenticated user irregardless of the assigned role.
I have debugged the request filter to confirm that the Principal has the right role yet a USER role can call the protected URLs successfully.
I am using Spring Boot 2.7.5.
If the path you're calling matches the authorization rule that you've declared as the last one (i.e. anyRequest().authenticated()), that implies that your test-requests don't match any of your rules that are meant to guard URLs that should be accessible only for Admins, namely:
.antMatchers(HttpMethod.DELETE, "/api/user/*").access("hasRole('ADMIN')")
.antMatchers(HttpMethod.POST, "/api/user").access("hasRole('ADMIN')")
Reminder: the matching rule declared first always weens
So, either HTTP-method or URL don't match (or both). For instance, if you're sending GET request, these restrictions would not be applied.
Regarding the URL, it should match exactly because you're using antMatchers(). I.e. path "/api/user" would not match other existing aliases of that path like "/api/user/" (more on that see here).
That's one of the reasons why in Spring Security 6.0 antMatchers() (as well as mvcMathcers() and regexMatchers()) have been removed from the API and replaced requestMatchers().
So make sure that HTTP-method is correct and path you're calling matchers exactly, and consider updating the Spring dependencies and switching to using new request-securing methods.
If you have no planes to update soon, then you can make use of the mvcMatchers(), which use Spring MVC matching rules (i.e. they take into consideration all the existing aliases of the given path), instead of antMatchers().
Here's an example of how your configuration might be implemented with Spring Security 6.0 and Lambda DSL (if you feel more comfortable with chaining configuration options using and() this flavor of DSL is still supported as well):
#Configuration
public class SecurityConfig {
#Bean
public SecurityFilterChain configure(final HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.DELETE, "/api/user/*").hasRole("ADMIN") // in Spring Security 6.0 method access() has been changed, and you don't need it anyway to verify the Role
.requestMatchers(HttpMethod.POST, "/api/user").hasRole("ADMIN")
.requestMatchers("/auth/login").anonymous()
.anyRequest().authenticated()
)
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
}
For a software in active development we are using Spring Boot (with Spring Security) and the Keycloak Adapter.
The goal is to:
require valid authentication for all endpoints except those annotated with #Public (see the code snippet) (this works)
the authentication must be via OAuth - the client gets a token directly from Keycloak and the Spring Security + Keycloak adapter make sure it is valid
optionally, Basic Auth is also supported (the Keycloak adapter can be configured to perform a login and make it appear like a regular token auth for the rest of the code) (this works as well)
Everything is working fine as it stands, but I have some problems understanding a few details:
The KeycloakWebSecurityConfigurerAdapter enables CSRF protection. I think this is only done so it can register its own Matcher to allow requests from Keycloak
It enables session management and requires some according beans
Even though requests are made with token authentication, a JSESSIONID cookie is returned
According to my understanding:
sessions should not be needed since stateless token authentication is used (so why is the KeycloakWebSecurityConfigurerAdapter enabling it). Is this only for the BASIC Auth part?
since sessions are enabled, CSRF protection is indeed needed - but I don't want the sessions in the first place and then the API would not need CSRF protection, right?
even if I set http.sessionManagement().disable() after the super.configure(http) call the JSESSIONID cookie is set (so where is this coming from?)
As stated in the code snippet, SessionAuthenticationStrategy is not set to the null once since we use the Authorization part of Keycloak and the application is a Service Account Manager (thus managing those resource records).
Would be great if someone can clear things up. Thanks in advance!
#KeycloakConfiguration
public class WebSecurityConfiguration extends KeycloakWebSecurityConfigurerAdapter {
#Inject private RequestMappingHandlerMapping requestMappingHandlerMapping;
#Override
protected void configure(final HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.requestMatchers(new PublicHandlerMethodMatcher(requestMappingHandlerMapping))
.permitAll()
.anyRequest()
.authenticated();
}
// ~~~~~~~~~~ Keycloak ~~~~~~~~~~
#Override
#ConditionalOnMissingBean(HttpSessionManager.class)
#Bean protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
/**
* {#link NullAuthenticatedSessionStrategy} is not used since we initiate logins
* from our application and this would not be possible with {#code bearer-only}
* clients (for which the null strategy is recommended).
*/
#Override
#Bean protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
/**
* HTTP session {#link ApplicationEvent} publisher needed for the
* {#link SessionRegistryImpl} of {#link #sessionAuthenticationStrategy()}
* to work properly.
*/
#Bean public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
#Override
#Bean public KeycloakAuthenticationProvider keycloakAuthenticationProvider() {
return super.keycloakAuthenticationProvider();
}
}
You may fall into excessive JWT token usage. Look at this article for example https://blog.logrocket.com/jwt-authentication-best-practices/. Especially look at the references at the end of the article about JWT as a session token.
For your web-application UI you are using sessions in most of the cases. It doesn't matter what type of token is used for authentication. Keycloak does everything correctly - it gives back httpOnly secure cookie for session management and tracks user status at backend. For better understanding of how it works you may look at the example code here: examples
For better separation of stateless backend (micro-)services and user UI session keycloak documentation suggest to use 2 different authentication stratagies: RegisterSessionAuthenticationStrategy for sessions and NullAuthenticatedSessionStrategy for bearer-only services
I have a very specific requirement in my project related to identity & authorization. I want to open 3 paths /public/, /protected/ & /private/ from my REST service module, which will behave as follows:
URLs starting with /public/ can be accessed without any authentication or authorization.
URLs starting with /private/ can be accessed only if the user is authenticated.
URLs starting with /protected/ can be accessed only if the user is authenticated as well as authorized.
To achieve this I have built a Configurator by extending "spring resource server configurator & overriding the configure method". But unfortunately it's not working. I have also tried to use "spring web service configurator & using the ignore ant url support " but the same is also not working. The configuration which is working only for /private/ & /protected/ URLs is as follows.
http.anonymous()
.disable()
.requestMatchers()
.antMatchers("/protected/**", "/private/**")
.and();
for (String protectedApiEp : configuredApis) {
http.authorizeRequests()
.antMatchers("/protected/" + protectedApiEp + "/**")
.hasAuthority(protectedApiEp);
}
http.authorizeRequests()
.antMatchers("/protected/**").denyAll()
.antMatchers("/private/**").permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
Can anyone guide me how I can enable /public/ URLs as open to all users, with the above configuration?
The following configuration should work:
#EnableWebSecurity
public class WebApplicationSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
// Allow Spring Security to authorize requests.
http
.authorizeRequests()
// Allow anyone to access URLs starting with /public/.
.antMatchers("/public/**").permitAll()
// Allow anyone with the protected role to access URLs starting with /protected/.
.antMatchers("/protected/**").hasAuthority("protected")
// Allow anyone who is authenticated successfully to access all other URLs.
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
Here is a sample application that shows this configuration in action. Start the application as mvn clean spring-boot:run and then navigate to http://localhost:8080 to access the application.
While testing Spring Boot (1.3.3) with a simple web app using spring-boot-starter-security:1.3.3:RELEASE I observed the following behaviour:
In order to override the default Spring web security configuration, I supplied a custom Java configuration class like so:
#Configuration
// #EnableWebSecurity apparently obsolete ?
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// http security checking left out for brevity ...
}
#Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
// user authentication left out for brevity ...
}
}
After startup, the application redirects to the login page and checks username/password correctly whether the #EnableWebSecurity annotation is provided or not (like in the example above). Is this annotation in this context therefore obsolete ? If so, why ?
The auto configuration of Spring Boot automatically enables web security and retrieves all beans of the type WebSecurityConfigurerAdapter to customize the configuration if certain conditions are met (spring-boot-starter-security on the classpath etc.). The auto configuration for web security is enabled in the class org.springframework.boot.autoconfigure.security.SpringBootWebSecurityConfiguration (Spring Boot 1.2.7, class name may have changed in newer versions).