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(), чтобы проверить свои правила.