Custom AuthenticationProvider ignore sessionManagement - spring-security

I wrote custom AuthenticationProvider and it work. I follow this tutorial.
I'm also use HttpSecurity configuration like this.
http
....
....
.sessionManagement()
.sessionFixation().migrateSession()
.maximumSessions(1)
.expiredUrl("/expired")
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry())
;
But user still can login twise and more.
As a comparison, inMemoryAuthentication and jdbcAuthentication working. Only AuthenticationProvider is not.

If you are using a custom Authentication, UserDetails, or principal for the Authentication ensure to implement hashCode and equals methods.

Related

How to configure Spring Security URL authorisation?

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();
}
}

Spring security with vaadin combined with thymeleaf

The spring security annotations are ignored on the Vaadin views; I have a #DenyAll at the class level but the view is rendered anyway.
The project combines thymeleaf and vaadin within spring boot. The first for a fast rendering of a large HTML with command and events over a websocket, the latter for ease of developing the administrative screens. Vaadin is setup under "/vdn/", spring MVC with thymeleaf under "/".
Spring security works correctly in thymeleaf; login, logout, and the sec:authorize correctly hides or shows parts of the generated HTML. But the security tag on the Vaadin view is ignored.
#Route("/")
#StyleSheet("context://../vaadin.css")
#DenyAll
public class MainView extends AppLayout {
Based on the documentation (https://vaadin.com/docs/latest/security/enabling-security) if no annotation is present the view should not be shown at all, it however is. So somehow Vaadin is not picking up the Spring security. Any suggestions what is missing?
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Autowired
private DataSource dataSource;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().passwordEncoder(new BCryptPasswordEncoder()) //
.dataSource(dataSource) //
.usersByUsernameQuery("select username, password, enabled from person where username=?") //
.authoritiesByUsernameQuery("select username, role from person where username=?") //
;
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests() //
.anyRequest().authenticated() //
.and() //
.formLogin() //
.and() //
.csrf().disable() // needed for vaadin https://tutorialmeta.com/question/server-connection-lost-after-successful-login-with-spring-security
.logout()
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
;
return http.build();
}
}
As mentioned in the comments as well, in order for the Vaadin View-Based security to work, it should be enabled first by extending your security configuration class from VaadinWebSecurity (for V23.2+) or VaadinWebSecurityConfigurerAdapter (older versions). You can refer to the documentation here: https://vaadin.com/docs/latest/security/enabling-security/#security-configuration-class
When extending from either of the above classes, if you are overriding the void configure(HttpSecurity http) throws Exception method (which is needed almost always), do not forget to call super.configure(http); in the correct order mentioned that mentioned in the documentation. This is important as the viewAccessChecker bean is enabled through this call, and this is what you need to have the View-Based security work.
Note: You probably have seen the chain of calls on http.authorizeRequests() (which is of type ExpressionInterceptUrlRegistry) in many tutorials and documentations. Once the .anyRequest().PermitAll() or some similar method is called on it, it does not accept any more configuration of those pattern matching configurations, so it is important to configure your custom pattern matching configs before the call to super.configure(http); (as shown in the mentioned documentation).
Finally, setting the login form which is done in the documentation via calling setLoginView(http, LoginView.class); is an important step, as not only does it introduce your custom login view to the viewAccessChecker bean, but, it also enables Spring Security's form-based login feature which is needed for the view-based security mechanism to work properly.
If you have all the steps mentioned above in order, then those access annotations such as #DenyAll or #RolesAllowed are taken into account and you can expect them to work as documented. If you still have problems enabling it, please provide a Minimal, Reproducible Example that isolates your problem so that the community can help more effectively.

What is httpBasic method in spring security?

I override configure(HttpSecurity http) method in SampleSecurityConfig Class like this
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/delete/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().and().httpBasic();
}
If i don't use httpBasic method, it seems no problem occurred.
what does httpBasic method exactly do?
Calling this method on HttpSecurity will enable Http Basic Authentication for your application with some "reasonable" defaults.
It will return a HttpBasicConfigurer for further customization.
You can test this by curl and passing a header like Authorization: Basic bzFbdGfmZrptWY30YQ== but base64 encoding a valid username/password combination.
Documentation for httpBasic
What are we saying by calling httpBasic() ?
When httpBasic() is called, we are telling Spring to authenticate the request using the values passed by the Authorization request header. If the request is not authenticated you will get a returned status of 401 and a error message of Unauthorized
What is actually happening when httpBasic() is called ?
By calling httpBasic(), an instance of the BasicAuthenticationFilter is added to the filter chain. The BasicAuthenticationFilter will then proceed with trying to authenticate the request in the typical Spring Security fashion. If authentication is successful, the resulting Authentication object will be placed into the SecurityContextHolder, which can then be used for future authentication purposes.

spring boot basic http authentication with multiple roles throws 403 forbidden error

I am trying to configure spring boot-Embedded Tomcat basic HTTP authentication with multiple roles, with most of the url's similar but few of them specific to each role. Here for first role the basic HTTP authentication pops up and working fine. With below code,
#Configuration
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class TestSecurityAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().antMatchers(null, getAppAdminRolePaths()).authenticated()
.anyRequest().hasAnyRole("APPADMIN")
.and()
.httpBasic();
http.csrf().disable()
.authorizeRequests().antMatchers(null, getAppUserRolePaths()).authenticated()
.anyRequest().hasAnyRole("APPUSER")
.and()
.httpBasic();
http.authorizeRequests().antMatchers(null, new String[]{"/app/appOwnerView.html"}).authenticated()
.anyRequest().hasAnyRole("APPOWNER")
.and()
.httpBasic();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("appadminname").password("appadminpwd").roles("APPADMIN").and()
.withUser("appusername").password("appuserpwd").roles("APPUSER").and()
.withUser("appownername").password("appoownerpwd").roles("APPOWNER");
}
private static String[] getAppAdminRolePaths(){
return new String[]{"/appweb/*",
"/app/checkService.html",
"/app/index.html",
"/app/testData.html",
"/app/adminView.html",
"/app/demo.html"};
}
private static String[] getAppUserRolePaths(){
return new String[]{"/appweb/*",
"/app/checkService.html",
"/app/index.html",
"/app/testData.html",
"/app/userView.html",
"/app/demo.html"};
}
}
For HTTP username/password popup in browser with url http://localhost:8080/app/index.html say with appadminname/appadminpwd it works fine. But for same url if I enter appusername/appuserpwd it throws HTTP 403 Forbidden access error. Here why is the second role APPUSER configured is throwing this error is I am not sure. Please let know if some way to get this resolved.
Thanks
I appreciate this question is a little old now, but this may still be useful to someone.
Firstly, I'm not sure why your calls to antMatchers() supply null as the first argument; antMatchers() expects a list of strings defining the URLs to be covered by this rule, so I'm not sure what null is expected to match in this case.
Secondly, anyRequest() means that this rule will be applied to any request made to the application regardless of the URL used, and Spring will apply security rules in the order that they are defined. You would typically define URLs and their associated roles first, and then default to a rule for any other request that must be authenticated (but does not necessarily need any specific roles) with something like anyRequest().authenticated()
Your first rule says that any request made to the application must be made by users with the role APPADMIN, which denies you access when you try to log in as appusername, so the second rule to allow APPUSERs is not even processed.
Thirdly, you are making multiple calls to http.authorizeRequests() when you should probably actually be chaining them together, for example:
http.csrf().disable().authorizeRequests()
.antMatchers( getAppAdminRolePaths() ).hasRole("APPADMIN")
.antMatchers( getAppUserRolePaths() ).hasRole("APPUSER")
.anyRequest().authenticated();
Lastly, when you have just a single role to check against, you can use hasRole() instead of hasAnyRole().
You also don't need to supply authenticated() and hasRole() in the same rule because hasRole() implies that the user is already authenticated.
You can find more explanations and examples in the Spring documentation: http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#authorize-requests

What happens with AuthenticationException?

I have a spring-boot application with spring-security enabled. I have a custom AbstractPreAuthenticatedProcessingFilter that checks request header for some authentication token set by external system. In case token is absent or invalid corresponding AuthenticationManager throws AuthenticationException. Looks pretty simple.
My problem is that I'd like to be able to handle this AuthenticationException by replying with a 403 (Forbidden) status. How can I do it? I tried adding the following to WebSecurityConfigurerAdapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint());
...
but it doesn't work. The issue is that ExceptionTranslationFilter that is supposed (if I understand it correctly) to handle these things is the last filter in the default security filter chain and exceptions thrown just never get to it. I'm totally stuck. How is this supposed to work? Please help!
A long time ago you question was done, but I was stucked in the same question without solution, so I decided to explain how to resolve this.
You are allowed to edit the FilterChain, you may put one ExceptionTranslationFilter before your PreAuthenticationFilter as ...
http
.csrf().disable()
.addFilter(myPreAuthenticationFilter)
.addFilterBefore(new ExceptionTranslationFilter(
new Http403ForbiddenEntryPoint()),
myPreAuthenticationFilter.getClass()
)
.authorizeRequests()
.antMatchers(authSecuredUrls).authenticated()
.anyRequest().permitAll()
.and()
.httpBasic()
.authenticationEntryPoint(new Http403ForbiddenEntryPoint())
;
Your AbstractPreAuthenticatedProcessingFilter getPreAuthenticatedCredentials or getPreAuthenticatedPrincipal methods may throw AuthenticationException if any credential is not provided as ...
if ( !StringUtils.hasText(request.getParameter("MY_TOKEN")) ) {
throw new BadCredentialsException("No pre-authenticated principal found in request");
}
Or your AuthenticationManager authenticate method can throw AuthenticationException too.

Resources