Spring boot and Spring Security, different logon forms to use different success handlers - spring

I have a spring boot/mvc site using spring security.
I have to use ways of logging in,
In the navbar present on each page
and the login page which you are redirected to when attempting to access a restricted resource.
For the navbar i'd like the user to stay on the page after successful login
For the login page i'd like the user to be redirected to the resource they were trying to originally access after login.
I can do each functionality individually
First use case is handled by:
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setUseReferer(true);
Second case is the default functionality.
But i've been unable to make them both work.
Does anyone have any insights on how to achieve this?

You can configure each login page with a different AuthenticationSuccessHandler like described here
https://www.baeldung.com/spring_redirect_after_login
Like:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/anonymous*").anonymous()
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/login")
.successHandler(myAuthenticationSuccessHandler())
.and()
.formLogin()
.loginPage("/login2.html")
.loginProcessingUrl("/login2")
.successHandler(mySecondAuthenticationSuccessHandler())
// ...
}

Related

How does one use different session creation policies for UI and REST endpoints with Spring Security?

I have an application that contains both a UI and some REST endpoints. The UI uses SAML login (the old Spring Security SAML extension) and the REST endpoints using a custom authentication. The REST endpoints are only called by external applications.
For the REST endpoints ("/api/**") I have stated a stateless session creation policy and for the rest of the endpoint no session creation policy at all (I also tried with ALWAYS as in the below example).
Prior to some Spring Boot version, not sure which, this worked. Currently I'm using Spring Boot v.2.6.1. The UI endpoint got the authentication object from the Http session.
But now it doesn't work. The security context object cannot be found in the Http session using the default HttpSessionSecurityContextRepository implementation. It is saved but it can't be restored.
So is it possible to use two session creation policy, one for the REST and the other for the UI part, or should this be handled in a different way?
Now it seems that the stateless session creation policy is also used by the UI, which is not intended.
I'm using two WebSecurityConfigurerAdapter classes; one for the API and the other for the UI.
After a successful SAML login the redirect URL now contains the ";jsessionid=6051854D94A0771BB9B99FE573AA4DFD" parameter. Probably because of the stateless policy...?
protected void configure(HttpSecurity http) throws Exception {
List<AbstractAuthenticationProcessingFilter> authFilters = new ArrayList<>();
authFilters.add(new OAuthMacAuthenticationProcessingFilter(authenticationManager(), this.properties));
ApiAuthenticationProcessingFilter apiAuthenticationProcessingFilter = new ApiAuthenticationProcessingFilter(authenticationManager(),authFilters);
http
.csrf()
.disable()
.antMatcher("/api/**")
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.addFilterBefore(apiAuthenticationProcessingFilter, BasicAuthenticationFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
and for the UI part
protected void configure(HttpSecurity http) throws Exception {
http.securityContext().securityContextRepository(customSessionSecurityContextRepository);
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class);
var auth = http
.authorizeRequests()
.antMatchers("/saml/**").permitAll()
.antMatchers("/loggedout/**").permitAll()
.antMatchers("/error").permitAll();
auth
.anyRequest()
.authenticated();
http.csrf().disable();
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
http.headers().frameOptions().sameOrigin();
http.exceptionHandling().accessDeniedHandler(this.accessDeniedHandler());
http
.logout()
.disable(); // The logout procedure is already handled by SAML filters.
}
I'll answer this myself. The above code does actually work. The problem was on the remote end, the IDP I was using had some problems that day that resulted in that it didn't work as expected. The day after, it worked.

adding httpSecurity.csrf().disable() to spring security configuration is allowing unauthenticated endpoint calls

I have the following security configuration for my springboot project
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/test_url_1");
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/");
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
now I have another endpoint /test_url_2. Calling which should be redirected to error page for an unauthorized user. If am removing csrf().disable() its working fine and the request is going to default /error page. but on adding csrf().disable() ,I am noticing a very unusual behaviour. once another endpoint ex. /test_url_3 is being called with an jwt header for authorization, next time onwards the endpoint(/test_url_2) is blocked even for unauthorized user, i.e. instead of going to the error page its calling the end point.
I am still new to spring security and spring, and not sure if I am writing some configuration wrong, please help me with the issue.
I didn't get exact meaning of the following sentences:
I am noticing a very unusual behaviour. once another endpoint ex. /test_url_3 is being called with an jwt header for authorization, next time onwards the endpoint(/test_url_2) is blocked even for unauthorized user, i.e. instead of going to the error page its calling the end point.
If you didn't get what exactly CSRF I recommend watching this video: https://youtu.be/uzZzlar-iQI from 1:08:00. If you want to authenticate url1 and don't want to authenticate url2 try the following code instead of using anyRequest().
httpSecurity.csrf().disable()
.authorizeRequests()
.antMatchers('/ur1')
.authenticated()
follow through this URL for more info on CSRF: https://www.baeldung.com/spring-security-csrf

Spring security user manual login session creation without password by admin

I am building a web application with spring, hibernated for backend and I am using html,css, javascript jquery forfrontend . I have created signup page, login page and home page. The flow is, User creates account and logins with username and password and if he is authenticated then he is redirected to home page. We do not store password in plaintext form for security reasons. Now I am the administrator and creator of the web application and sometimes a need arises for admin to change data for user or demonstrate what user can do in the interface. What I need to do is create a login session of the user and make changes in his account and/or demonstrate how user can do things on the website(by sharing screen). I want to create a user's session manually, as password is stored in plaintext form I can not login with username and password. Is there a way I can create browser login session without password. I am sharing screenshots of my web applications login page and home page. I am also sharing spring security configuration class. Is there a way I can just specify a username and spring can create a login session for me and I can access user's account just like a normal user session.
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// add a reference to our security data source
#Autowired
private DataSource myDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(myDataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/signup_page","/forgot_password","/signup","/reset_password").permitAll()
.antMatchers("/resources/**").permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login_page")
.loginProcessingUrl("/authenticateTheUser").permitAll()
.defaultSuccessUrl("/home_page")
.and()
.logout()
.logoutSuccessUrl("/login_page?logout")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.permitAll()
.and()
.sessionManagement()
.sessionFixation()
.migrateSession()
.invalidSessionUrl("/login_page")
.maximumSessions(3)
.expiredUrl("/login_page?logout");
}
}
below are the images of my web application.
Two concepts that you may want to look into are:
Pre-Authentication, normally for cases where you are behind a gateway that performs authentication prior to your application (see RequestHeaderAuthenticationFilter)
Switch User for cases where an ADMIN needs to impersonate a USER (see SwitchUserFilter)
Both of these require careful consideration and proper use so as not to accidentally open you up to bypassing authentication entirely. If you're just doing this in a development environment, enabling pre-authentication by adding a RequestHeaderAuthenticationFilter could work for you.

Spring Security: how to recognize, in a public page, that a user is authenticated

I have a simple Spring Boot web application consisting of 2 pages:
a Home Page (freely accessible) at the url https://example.com/
a secured page (requires login for being accessed) at the url https://example.com/secure/page.html
In the Home Page I'm printing the First Name of the visiting user (if he/she is already authenticated) or a sentence saying that the page is visited by an anonymous user.
I'm using Keycloak as far as authentication is concerned.
Here the Spring Security configuration:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/secure/**")
.authenticated()
.and()
.csrf().requireCsrfProtectionMatcher(keycloakCsrfRequestMatcher())
.and()
.sessionManagement()
.sessionAuthenticationStrategy(sessionAuthenticationStrategy())
.and()
.addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(keycloakAuthenticatedActionsFilter(), BasicAuthenticationFilter.class)
.addFilterAfter(keycloakSecurityContextRequestFilter(), SecurityContextHolderAwareRequestFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint())
.and()
.logout()
.addLogoutHandler(keycloakLogoutHandler())
.logoutUrl("/sso/logout").permitAll()
.logoutSuccessUrl("/");
}
If the user is already authenticated, the problem is that the Home Page says he is anonymous because the Principal is always null.
But if the user enters the secured page (and Keycloak lets him in because he's already authenticated) when he comes back to the Home, the page contains - correctly - his First Name.
Where is my configuration wrong?
It seems that Spring Security doesn't check the authentication on non secured pages. Is there a way to tell Spring Security to check every page (both secured and non-secured)?
Thanks in advance for your support.
The solution to this problem is to add /** to security context/handling (with permitAll()).
The art is to do it correctly:
Multiple antMatchers in Spring security (First ant matcher wins!!, https://www.google.com/search?q=spring+security+permitall+not+working)
http://blog.florian-hopf.de/2017/08/spring-security.html
So in this case:
http
.authorizeRequests()
.antMatchers("/secure/**").authenticated()
.antMatchers("/**").pernmitAll()
.and()...
...should fill the (anonymous) Principal also in "permitted area" (i.e. /**(!) ...and leave secure/** restricted!;).
To the core/title question (once Principal is filled), i think the answer is already given:
here (verbally): https://stackoverflow.com/a/26117007/592355
and here(with code): https://stackoverflow.com/a/57054816/592355
..if you use Spring Security (JSP) Taglibs isAnonymous() is handy, and otherwise (in default) you just need to check for hasRole('ROLE_ANONYMOUS') (Ref)

Custom .formLogin() Spring Security

I am trying to use my page created to authenticate, it is the login.jsf. I use in .loginPage / login.jsf with 2 inputs and 1 button to authenticate. However, by filling in the logins and password fields, and clicking the button, nothing happens. Unlike Spring Security own .formLogin() itself, which already does all the processing by clicking the button.
My method
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login.jsf").anonymous()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.jsf")
.defaultSuccessUrl("/homepage.jsf")
.failureUrl("/login.jsf?error=true")
.and()
.logout().logoutSuccessUrl("/login.jsf");
}
Anyone have any suggestions?
Print screen default .formLogin()
Take a look at the following article from spring security documentation.
https://docs.spring.io/spring-security/site/docs/current/guides/html5/form-javaconfig.html#creating-a-login-view
Quoting the article,
The URL we submit our username and password to is the same URL as our
login form (i.e. /login), but a POST instead of a GET.
So I believe you have to make a POST call to the /login endpoint with your authentication details for it to work.
Don't use file extensions when specifying pages in the configuration. The ViewResolver will take care of it. Simply specify
.loginPage("/login").defaultSuccessUrl("/homepage")
.failureUrl("/login?error=true")
.and()
.logout().logoutSuccessUrl("/login");`
And make sure a ViewResolver is configured to append .jsf to the url. The DispatcherServlet will take care of locating the appropriate page.
Also, not to mention, you should have controllers with the appropriate #RequestMappings defined to handle your requests.

Resources