Ajax call after session expired not redirecting to login page - spring boot - ajax

I want to redirect to the login page if while doing an ajax call the session is expired. I'm following the instructions on this link to do that, but every time I do an ajax call with the session expired it returns the login page as part of the response instead of redirecting to the login page, this never fails and response always goes to success piece of the ajax call with readyState: 4 and status: 200 and this is giving me a jquery error because I'm expecting a list and it's returning the html code for the login page.
My code is as below:
WebSecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(URL_LOGIN, "/css/**", "/img/**").permitAll()
.antMatchers("/admin/**").hasAnyAuthority(authorizedRolesAdmin)
.antMatchers("/**").hasAnyAuthority(ArrayUtils.addAll(authorizedRolesUser, authorizedRolesAdmin))
.and()
.formLogin()
.loginPage(URL_LOGIN)
.defaultSuccessUrl("/", true)
.failureUrl(URL_LOGIN_FAILED)
.permitAll()
.and()
.logout()
.logoutSuccessUrl(URL_LOGOUT)
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.permitAll()
.and()
.exceptionHandling()
.accessDeniedPage(URL_LOGIN_UNAUTHORIZED)
.authenticationEntryPoint(new AjaxAwareAuthenticationEntryPoint(URL_LOGIN))
.and()
.sessionManagement()
.maximumSessions(1)
.expiredUrl(URL_LOGOUT)
.and()
.invalidSessionUrl(URL_LOGOUT);
}
AjaxAwareAuthenticationEntryPoint.java
public class AjaxAwareAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
String ajaxHeader = request.getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(ajaxHeader)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Ajax Request Denied (Session Expired)");
} else {
super.commence(request, response, authException);
}
}
}
While debugging after session expired, I noticed that it doesn't even enter to: AjaxAwareAuthenticationEntryPoint java class commence method.
What I'm missing here?

I know, that it is probably too late for the answer, but anyway the code you've attached helped me to resolve the absolutely same issue. To be honest, I don't see any issues in your code that makes it not to enter to the AjaxAwareAuthenticationEntryPoint, only may be you have some other configs behind the scene. But anyway, let my answer be an example for everyone, who struggles with the same issue, cause for me it works fine, and returns 403 if session is expired. I am using Spring Boot v2.3.5 and this is my code:
AjaxAwareAuthenticationEntryPoint.java
public class AjaxAwareAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginFormUrl) {
super(loginFormUrl);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
String ajaxHeader = ((HttpServletRequest) request).getHeader("X-Requested-With");
if ("XMLHttpRequest".equals(ajaxHeader)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Ajax Request Denied (Session Expired)");
} else {
super.commence(request, response, authException);
}
}
}
WebSecurityConfig.java
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private AccessDeniedHandler accessDeniedHandler;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/registration*").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.defaultSuccessUrl("/admin", true)
.failureHandler(authenticationFailureHandler)
.and()
.logout()
.invalidateHttpSession(true)
.clearAuthentication(true)
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login")
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and()
.exceptionHandling()
.authenticationEntryPoint(new AjaxAwareAuthenticationEntryPoint("/login"));
}
}
and inside my main.js where I have some jquery ajax calls I have this:
$(document).ajaxError(function myErrorHandler(event, xhr, ajaxOptions, thrownError) {
if (xhr.status == 403) {
window.location.href ="/login";
}
});

Related

How do I make logout possible using Google OAuth2?

I am trying to do log-out, and I am using Spring Boot 2.1.7.RELEASE, and Google OAuth2.
This is my class implementing WebSecurityConfigurerAdapter.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.headers().frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**").permitAll()
.antMatchers("/**").hasRole(Role.USER.name())
.anyRequest().authenticated()
.and()
.logout().logoutUrl("/logout").invalidateHttpSession(true)
.clearAuthentication(true)
.logoutSuccessUrl("/").deleteCookies("JSESSIONID").permitAll()
.and().csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(customOAuth2UserService);
}
And this is my #Controller code for Http-GET request, "/logout".
#GetMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(authentication != null) {
new SecurityContextLogoutHandler().logout(request, response, authentication);
}
SecurityContextHolder.getContext().setAuthentication(null);
return "index";
}
I tried almost everything I googled and saw on Stackoverflow, but I seem to fail to completely logout every time.

Spring security Basic Auth and Form login for the same API

I would like to access all my API's via two authentication mechanisms, Basic Auth & Form login. I know that there are existing questions, but, the answers did not work for me, and my use case is a little bit different.
My config:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {
#Configuration
#Order(1)
public static class SecurityConfigBasicAuth extends WebSecurityConfigurerAdapter {
final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
public SecurityConfigBasicAuth(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
#Qualifier("customUserDetailsService") UserDetailsService userDetailsService) {
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
// #Bean authenticationProvider()
// #Bean passwordEncoder()
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin().disable()
.logout().disable();
}
}
#Configuration
public static class SecurityConfigFormLogin extends WebSecurityConfigurerAdapter {
final private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
final private RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler;
final private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
public SecurityConfigFormLogin(RestAuthenticationEntryPoint restAuthenticationEntryPoint,
RestfulSavedRequestAwareAuthenticationSuccessHandler restfulSavedRequestAwareAuthenticationSuccessHandler,
CustomAuthenticationProvider hashAuthenticationProvider) {
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
this.restfulSavedRequestAwareAuthenticationSuccessHandler = restfulSavedRequestAwareAuthenticationSuccessHandler;
this.customAuthenticationProvider = customAuthenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
.and()
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.csrf().disable()
.httpBasic().disable()
.formLogin()
.usernameParameter("id1")
.passwordParameter("Id2")
.loginProcessingUrl("/test/login")
.successHandler(restfulSavedRequestAwareAuthenticationSuccessHandler)
.failureHandler(myFailureHandler())
.and()
.logout();
}
// #Bean myFailureHandler()
}
}
As you can see, I defined two 'WebSecurityConfigurerAdapters', one for Basic Auth, and one for Form login. The Form login is REST compatible (does not redirect, but gives HTTP responses).
The problem is as follows: The first 'WebSecurityConfigurerAdapter' that is loaded works and overrides the second. The above example, makes it possible to use basic auth, but I cannot login on POST '/test/login', I get a:
{
"timestamp": 1534164906450,
"status": 401,
"error": "Unauthorized",
"message": "Unauthorized",
"path": "/test/login"
}
Update fixed: the key was to use the 'requestMatchers()', see answer section for solution (as suggested by jzheaux)
Okay, this is how I fixed this:
I configured the Basic Auth configuration as:
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/api/**")
.and()
.cors()
.and()
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and();
}
If you do not want that the basic authentication returning new cookie with new JSESSIONID, add:
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.sessionFixation()
.migrateSession()
The Form login configuration as:
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers(HttpMethod.POST, "/test/login")
.and()
.cors()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(restAuthenticationEntryPoint)
.and()
.formLogin()
.usernameParameter("id1")
.passwordParameter("id2")
.loginProcessingUrl("/test/login")
.successHandler(authenticationSuccessHandler)
.failureHandler(myFailureHandler())
.and()
.logout();
}
Now, it is possible for me to authenticate via the Form login configuration, and use the cookie session id to call /api/** (configured in the Basic Auth configuration). I can also just use the Basic Auth authentication ofcourse.

Spring security only allowed oauth login

In my project, i just want to allow oauth login. my spring security config as follow:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/login.html", "/oauth/**", "/sign**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().disable()
.httpBasic().disable()[enter image description here][1]
.exceptionHandling().accessDeniedPage("/login.html")
.and()
.apply(new SpringSocialConfigurer());
}
The problem is the access denied page not working. I want to get login.html content as follow:
login.html
in fact i got 403 page as follow:
403
How to solve this problem?
I solve this problem, the code as follow:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login.html", "/oauth/**", "/sign**").permitAll()
.anyRequest()
.authenticated()
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(new HttpForbiddenEntryPoint(), AnyRequestMatcher.INSTANCE)
.and()
.logout()
.logoutUrl("/logout.html")
.clearAuthentication(true)
.logoutSuccessUrl("/login.html")
.and()
.apply(new SpringSocialConfigurer());
}
The entry point:
public class HttpForbiddenEntryPoint implements AuthenticationEntryPoint {
private String redirect_url = "/login.html";
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.sendRedirect(redirect_url);
}
public String getRedirect_url() {
return redirect_url;
}
public void setRedirect_url(String redirect_url) {
this.redirect_url = redirect_url;
}
}

Authentication failure redirect with request params not working

I am trying to configure my own success and authentication failure handlers. On authentication failure I want to redirect back to my login page with a request parameter, the presence of this parameter will output the error message on my login page. However although on error I am getting redirected back to my login page, the request parameter is always null.
Code below:
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login.html").permitAll()
.usernameParameter("username")
.passwordParameter("password")
.loginProcessingUrl("/login")
.successHandler(successHandler())
.failureHandler(handleAuthenticationFailure());
}
#Autowired
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//database checks
}
};
}
/**
* Authentication success handler defines action when successfully authenticated
* #return
*/
#Bean
public AuthenticationSuccessHandler successHandler(){
return new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest httpRequest, HttpServletResponse httpResponse, Authentication authentication)
throws IOException, ServletException {
// custom auth success here
httpResponse.setStatus(HttpServletResponse.SC_OK);
SavedRequest savedRequest = (SavedRequest) httpRequest.getSession().getAttribute("SPRING_SECURITY_SAVED_REQUEST");
httpResponse.sendRedirect(savedRequest.getRedirectUrl());
}
};
}
#Bean
public AuthenticationFailureHandler handleAuthenticationFailure() {
return new SimpleUrlAuthenticationFailureHandler() {
#Override
public void onAuthenticationFailure(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
AuthenticationException authenticationException) throws IOException, ServletException {
// custom failure code here
setDefaultFailureUrl("/login.html?error=fail");
super.onAuthenticationFailure(httpRequest, httpResponse, authenticationException);
}
};
}
Try with this:
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
// .......
response.sendRedirect("/login.html?error=fail");
}
Update:
It's really important that the "/login.html?error=fail" is added to an authorizeRequests() section otherwise the controller won't pick up the error parameter.
Replace .antMatchers("/login").permitAll() with .antMatchers("/login**").permitAll()
Also had problem with params (in my case when login was failed and some request params was added to url it redirected to login page without params).
This solved my problem
.antMatchers("/login**").permitAll()
I'm new in springBoot, if you are using spring boot 2.1.4.RELEASE, try this configuration:
http.csrf().disable()
.authorizeRequests()
// URLs matching for access rights
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
// form login
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.successHandler(sucessHandler)
.usernameParameter("email")
.passwordParameter("password")
.and()
// logout
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").and()
.exceptionHandling()
.accessDeniedPage("/access-denied");
To use the above-defined Spring Security configuration, we need to attach it to the web application. In this case, we don’t need any web.xml:
public class SpringApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecSecurityConfig.class};
}}
this means you create the following class which will be instanciated autoatically
SecSecurityConfig.class : is the class where you do all http.csrf().disable().authorizeRequests()... configurations
source : https://www.baeldung.com/spring-security-login
hope it helps :)

AuthenticationSuccessHandler in Spring MVC Java based configuration

I have three roles, and i want to redirect user to different pages after login according to their roles. I know this can be done by AuthenticationSuccessHandler, but I am having trouble in declaring it in Java based configuration.
So far I have done this.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/resources/**", "/login").permitAll()
.antMatchers("/admin/**").hasRole("USER")
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/")
.successHandler(successHandler) //----- to handle user role
.failureUrl("/loginfailed")
.permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.deleteCookies("JSESSIONID")
.invalidateHttpSession( true )
.and();
}
My question is where to declare successHandler and how to autowire it in this class, or how to declare successHandler method in this class and use it.
Try this: Moving Spring Security To Java Config, where does authentication-success-handler-ref go?
Code from the post above:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("")
.defaultSuccessUrl("/")
.failureUrl("")
.successHandler(//declare your bean here)
.and()
.logout()
.permitAll()
.and()
}
Then in the authentication handler you can apply the required logic
public class MYSuccessHandler implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication) throws IOException {
handle(request, response, authentication);
}
protected void handle(HttpServletRequest request,
// logic
redirectStrategy.sendRedirect(request, response, targetUrl);
}
/** Builds the target URL according to the logic defined in the main class Javadoc. */
protected String determineTargetUrl(Authentication authentication) {
}
}
Tutorial listed here http://www.baeldung.com/spring_redirect_after_login

Resources