Spring Безопасность и действия, необходимые после входа в систему

Я пытаюсь реализовать экран, требующий действия после входа пользователя в систему Spring Безопасность? У меня есть требование, когда пользователь должен выполнить для заполнения формы (сменить пароль, принять Условия использования и т.д.), А затем, как только пользователь завершит это действие, он сможет использовать остальную часть приложения. Я использую Spring OAuth2 с экраном входа, который использует поток Spring безопасности.

До сих пор я пытался использовать http.formLogin().successHandler(), который имеет пользовательскую реализацию SavedRequestAwareAuthenticationSuccessHandler, которая определяет, требуется ли пользователю действие, а затем перенаправляет пользователя на страницу, когда он может заполнить форму, но проблема с этим что если пользователь перейдет от этой страницы, он войдет в приложение и сможет использовать его, не пропуская форму. Но я пытаюсь запретить пользователю устанавливать сеанс до тех пор, пока эта форма действия не будет завершена. После того, как он будет заполнен, пользователь должен автоматически войти в систему (например, если пользователь согласился с Условиями использования, он должен войти в систему без повторного ввода пароля)

Вот код, который у меня до сих пор является пользовательским обработчиком:

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

И затем, как только он будет успешно завершен, я перенаправляю пользователя на TARGET_URL_SESSION_ATTR_NAME, который был сохранен в сеансе.

Также было бы полезно узнать, как обнаружить и перенаправить пользователя на экран требуемого действия в течение установленных сеансов (если пользователь вошел в систему, а позже, когда он выполнил вход в систему, устанавливает флажок требуемого действия в своей учетной записи).

Ответ 1

https://github.com/king-julien/spring-oauth2-customfilter Вот рабочий пример с Authorization and Resource Server. Этот ресурс-сервер (vanilla) - это базовое приложение без гражданства, которое не будет продолжаться до тех пор, пока вы не примете Условия обслуживания (принять TOS, просто сделать POST в конечной точке) после аутентификации.

Создать фильтр

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

Зарегистрируйте этот фильтр

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

Аннотировать свой основной файл с помощью @EnableResourceServer.

@SpringBootApplication
@EnableResourceServer
public class Application {

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

}

Ответ 2

Мы решаем, что страница утверждения OAuth2 является одностраничным. По умолчанию контроллер страницы утверждения является WhitelabelApprovalEndpoint. Мы переопределяем это, определяя собственный контроллер OauthApproval, который переопределяет "/oauth/confirm_access", поэтому мы можем добавить дополнительные элементы к модели. Когда загружается страница утверждения (jsp), мы конвертируем некоторые из свойств модели в javascript-переменные (var token = '$ {_ csrf.token}';) и запускаем приложение AngularJS. Затем страница утверждения может делать все, что захочет (перед отображением фактической формы утверждения), нам просто нужно построить конечные точки REST для разных функций.

Не забудьте добавить @SessionAttributes ( "authorizationRequest" ) в контроллер

Ответ 3

Вместо AuthenticationSuccessHandler вы должны использовать фильтр:

public class ActionRequirementCheckingFilter extends OncePerRequestFilter {

    /* This matcher should not match static resources (js,css etc),
     * url`s needed to handle the action and possibly something else,
     * depending on your application */
    private RequestMatcher matcher;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        HttpSession session = request.getSession();
        Boolean actionRequired = false;

        /* calculate actual value for actionRequired */

        if(matcher.matches(request) && actionRequired){

            /* save current request info into session for later use if needed */

            response.sendRedirect("/action-required");
        } else {
            filterChain.doFilter(request, response);
        }
    }

}

Этот подход соответствует всем вашим требованиям:

  • Пользователь не сможет перейти от него.
  • Пользователь будет автоматически зарегистрирован после завершения действия.
  • Он будет работать даже для существующих сеансов.

Единственным недостатком является то, что сеанс будет фактически создан до того, как действие будет завершено, но если у вас нет реальной причины не делать этого (что я не могу даже делать с изображением), это незначительно.

Ответ 4

Другой способ проверки прав доступа пользователя во время успешного входа в систему - через фильтр api

https://www.mkyong.com/spring-mvc/how-to-register-a-servlet-filter-in-spring-mvc/

Затем вы можете реализовать функциональность, необходимую в doFilter(), чтобы проверить свои правила.