Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Security and Action Required after login

I'm trying to implement an action required screen after user is logged-in in Spring Security? I have a requirement where user has to perform to complete a form (change password, accept Terms Of Use, etc.), then once user completes that action he can use the rest of the app. I'm using Spring OAuth2 with the login screen that uses Spring Security flow.

So far I have tried to use http.formLogin().successHandler() that has custom implementation of SavedRequestAwareAuthenticationSuccessHandler, which detects if user has action required, then redirects user to the page when he can fill out the form, but the problem with that is that if user navigates away from that page, he will be logged in to the app and can use it without by skipping the form. But what I'm trying to do is to block user from establishing the session until after that Action Required form is complete. Once it is complete user should be automatically logged in (ex. if user was req. to only agree with Terms of Use, he should be logged in without entering a password second time)

Here is the code that I have so far the custom handler:

public class CustomLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    UserService userService;

    public final static String TARGET_URL_SESSION_ATTR_NAME = "target-url";

    public CustomLoginSuccessHandler(String defaultTargetUrl) {
        setDefaultTargetUrl(defaultTargetUrl);
    }

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
        HttpSession session = request.getSession();


        AuthorityUser authorityUser = (AuthorityUser)authentication.getPrincipal();

        String userId = authorityUser.getUserId();

        User u = userService.getById(userId);

        Boolean changeRequiredDob = u.getChangeRequiredDob();
        Boolean changeRequiredPwd = u.getChangeRequiredPwd();
        Boolean changeRequiredTou = u.getChangeRequiredTou();

        if(changeRequiredDob || changeRequiredPwd || changeRequiredTou){

            String targetUrl = determineTargetUrl(request, response);
            session.setAttribute(TARGET_URL_SESSION_ATTR_NAME, targetUrl);
            getRedirectStrategy().sendRedirect(request, response, "/action-required");
        } else {
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }
}

And then once it is successfully complete I'm redirecting user to TARGET_URL_SESSION_ATTR_NAME that was stored to the session.

It would be also helpful to know how to detect and redirect user to the action required screen during the established sessions (if user logged in and later while he is logged in admin sets action required flag on his account).

like image 389
Maksim Avatar asked Nov 11 '16 05:11

Maksim


People also ask

How do I bypass Spring Boot security?

First of all, according to Spring Boot dot, we have to add @EnableWebSecurity annotation. Second of all, we have to override configure method WITH @Override annotation AND super. configure(http) at the end of the method.

How do I set up Spring Security authentication?

The Spring Security Configuration Here we're using the httpBasic() element to define Basic Authentication inside the SecurityFilterChain bean. What's relevant here is the <http-basic> element inside the main <http> element of the configuration. This is enough to enable Basic Authentication for the entire application.

Which starter is required to use Spring Security in Spring Boot application?

Spring Boot provides a spring-boot-starter-security starter which aggregates Spring Security related dependencies together. The simplest and preferred method to leverage the starter is to use Spring Initializr using an IDE integration (Eclipse, IntelliJ, NetBeans) or through https://start.spring.io.

How do I redirect after login in Spring Boot?

By default, Spring Security will redirect after login to the secured ressource you tried to access. If you wish to always redirect to a specific URL, you can force that through the HttpSecurity configuration object. Assuming you are using a recent version of Spring Boot, you should be able to use JavaConfig.


2 Answers

https://github.com/king-julien/spring-oauth2-customfilter Here is a working sample with Authorization and Resource Server. This Resource Server (vanilla) is a basic stateless application which will not proceed any further until you accept Terms of Service (to accept TOS, Just a do a POST on /tos end point) after authentication.

Create a filter

@Component
public class TosFilter extends OncePerRequestFilter{

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        System.out.println(request.getRequestURI());

        // In realworld scenario HelloWorldController.acceptedTOS is a persisted value rather than a static variable
        if(!HelloWorldController.acceptedTOS){
            //response.sendRedirect("/no-tos");
            request.getRequestDispatcher("error-no-tos").forward(request, response);
        }
        filterChain.doFilter(request,response);
    }
}

Register that filter

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    TosFilter rolesFilter;

    @Override
    public void configure(HttpSecurity httpSecurity) throws Exception{

        httpSecurity
                .addFilterAfter(rolesFilter, AbstractPreAuthenticatedProcessingFilter.class)
                .csrf().disable()
                .authorizeRequests().anyRequest().permitAll();
    }
}

Annotate your main with @EnableResourceServer.

@SpringBootApplication
@EnableResourceServer
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}
like image 65
so-random-dude Avatar answered Oct 21 '22 23:10

so-random-dude


The way we solve that is to have the OAuth2 approval page be a single page application. By default the approval page controller is WhitelabelApprovalEndpoint. We override this by defining out own OauthApproval Controller which overrides "/oauth/confirm_access", so we can add extra stuff to the model. When the approval (jsp) page is loaded, we convert some of the model properties into javascript variables (var token = '${_csrf.token}';), and start an AngularJS application. The approval page can then do whatever it wants (before showing the actual approval form), we just need to build REST endpoints for the different functionalities.

Remember to add @SessionAttributes("authorizationRequest") to the Controller

like image 38
Klaus Groenbaek Avatar answered Oct 22 '22 00:10

Klaus Groenbaek