2010-07-26 3 views
14

Я использую Spring Security для защиты HTTP-запросов на веб-сайте. Основное использование - это защита страниц, при которых пользователь перенаправляется на страницу входа при попытке доступа к этим страницам.Spring Security Custom Filter (сменить пароль)

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

Использовал ли для этой цели Spring Security? Нужно ли создавать собственный фильтр?

Спасибо,

Эндрю

ответ

18

В Spring Security 3.0 вы можете реализовать пользовательские AuthenticationSuccessHandler.

В этом обработчике вы можете перенаправить пользователя с временным паролем на страницу смены пароля вместо первоначально запрошенной страницы. После изменения пароля вы можете перенаправить пользователя на первоначально запрошенную страницу, используя SavedRequestAwareAuthenticationSuccessHandler, который является реализацией обработчика по умолчанию.

public class MyHandler implements AuthenticationSuccessHandler { 
    private AuthenticationSuccessHandler target = new SavedRequestAwareAuthenticationSuccessHandler(); 

    public void onAuthenticationSuccess(HttpServletRequest request, 
     HttpServletResponse response, Authentication auth) { 
     if (hasTemporaryPassword(auth)) { 
      response.sendRedirect("/changePassword"); 
     } else { 
      target.onAuthenticationSuccess(request, response, auth); 
     } 
    } 

    public void proceed(HttpServletRequest request, 
     HttpServletResponse response, Authentication auth) { 
     target.onAuthenticationSuccess(request, response, auth); 
    } 
} 

@Controller("/changePassword") 
public class ChangePasswordController { 

    @Autowired 
    private MyHandler handler; 

    @RequestMapping(method = POST) 
    public void changePassword(HttpServletRequest request, 
     HttpServletResponse response, 
     @RequestParam(name = "newPassword") String newPassword) { 

     // handle password change 
     ... 

     // proceed to the secured page 
     handler.proceed(request, response, auth);   
    } 

    // form display method, etc 
    ... 
} 
+0

Спасибо за это. Я могу сделать первую часть довольно легко, но не уверен, что вы подразумеваете под «перенаправлением пользователя на ... с помощью SavedRequestAwareAuthenticationSuccessHandler». Как перенаправить на обработчик? – DrewEaster

+0

@dewzilla: Я добавил образец того, как он может выглядеть (с контроллером Spring MVC для смены пароля, а не с проверкой). – axtavt

+2

На самом деле это противоречит проверке истечения срока действия пароля в обработчике проверки подлинности, не так ли? Вы не сможете аутентифицироваться, если срок действия пароля истек. Проверьте ответ, предоставленный @jyore. – Octavian

6

Да, я сделал это с фильтром ForceChangePasswordFilter. Потому что, если пользователь вводит URL вручную, он может обходить форму пароля изменения. С фильтром запрос всегда перехватывается.

+0

Не могли бы вы поделиться своим кодом? – Jonathan

+1

Джонатан, это было много лет назад, у меня нет доступа к этому коду. – rodrigoap

+0

rodrigoap я понимаю. Я разделяю то же мнение, что если вы используете обработчик успеха, пользователь все равно сможет получить доступ к другим страницам, написав URL-адрес непосредственно в адресной строке. Но в принципе, вы могли бы поделиться хотя бы намерением внутри фильтра? Я попытался перенаправить внутри фильтра, и он вызывает бесконечный цикл переадресации, как то, что упоминается здесь: http://stackoverflow.com/questions/3954930/spring-security-3-0-how-do-i-specify-urls -в-который-, прикладывает-заказ фильтра. Просто опубликует отдельный вопрос для этого в SO. – Jonathan

13

Немного поздно на это, но, надеюсь, это может помочь другим найти эту ссылку. Если вы используете пользовательский UserDetailsService, вы можете, например, установить учетные данные объекта пользователяNonExpired на false, чтобы не разрешать доступ к любому защищенному контенту, пока это поле не будет установлено на true.

В принципе, если у вас есть срок действия пароля, вы установите поле в своей модели пользователя (возможно, passwordExpired), и когда пользователь UserDetailsService вытащит пользователя, ваш UserDetailsService будет использовать это значение для установки учетных данныхNonExpired.

Затем все, что вам нужно сделать, это добавить конфигурацию вашего приложенияContext-security.xml для настройки сопоставлений исключений аутентификации. Это позволит вам поймать исключение, занесенное с использованием устаревших учетных данных, и заставить пользователя перейти на страницу сброса пароля. Вы можете дополнительно заблокировать заблокированные и отключенные учетные записи, используя аналогичный метод. Пример конфигурации приведен ниже:

ApplicationContext-security.xml

<beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler"> 
    <beans:property name="exceptionMappings"> 
     <beans:props>   
      <beans:prop key="org.springframework.security.authentication.BadCredentialsException">/login_error</beans:prop> 
      <beans:prop key="org.springframework.security.authentication.CredentialsExpiredException">/password_expired</beans:prop> 
      <beans:prop key="org.springframework.security.authentication.LockedException">/locked</beans:prop> 
      <beans:prop key="org.springframework.secuirty.authentication.DisabledException">/disabled</beans:prop> 
     </beans:props> 
     </beans:property> 
</beans:bean> 

<http use-expressions="true"> 
    <!-- ADD BLACKLIST/WHITELIST URL MAPPING --> 
    <form-login login-page="/login" default-target-url="/" authentication-failure-handler-ref="exceptionTranslationFilter" /> 
</http> 

Тогда просто убедитесь, что у вас есть настройки контроллеров служить эти ссылки с соответствующим содержанием.

+2

Это решение имеет недостаток, который проверяет учетные данные пользователяNonExpired перед аутентификацией пользователя, таким образом вы обнаруживаете, что учетные данные пользователя являются истек до не аутентифицированного пользователя (например, злоумышленника). Я предполагаю, что такой фрагмент информации не является критическим, но я все еще нахожу решение для rodrigoap с фильтром лучше. –

3

Очень полезная форма ответа jyore, это было именно то, что я искал. Если вы используете пользовательский класс, реализующий UserDetailsService, вы можете сделать это как следующее вместе с указанным выше определением компонента в вашем applicationContext.xml. Одна вещь, которая основана на вашем заголовке ХМЛ может понадобиться использовать <bean .... или <prop ... вместо <beans:bean ... или <beans:prop ...

import ...... 

@Service("userService") 
public class UserDetailsServiceImpl implements UserDetailsService { 

private static Logger logger = LoggerFactory 
     .getLogger(UserDetailsServiceImpl.class); 

@Autowired 
private UserDao userDao; 

@Override 
public UserDetails loadUserByUsername(String username) 
    throws UsernameNotFoundException, DataAccessException , CredentialsExpiredException ,BadCredentialsException , 
    LockedException , DisabledException , UsernameNotFoundException 
{ 
    User user = userDao.getUserByUsername(username); 
    System.out.println("User Found"); 

    if(user == null){ 
     // System.out.println("User Not Found"); 
     logger.error("User Not Found"); 
     throw new UsernameNotFoundException(username + " is not found."); 
    } 

    if(user.isEnabled() == false){ 
    // System.out.println("User not enabled"); 
    logger.error("User not enabled"); 
     throw new DisabledException("User not enabled"); 
    } 

    if(user.isLocked() == true){ 
     //System.out.println("User is Locked"); 
     logger.error("User is Locked"); 
      throw new LockedException("User is Locked"); 
     } 
    if(user.isPasswordExpired() == true){ 
     // System.out.println("Password Expired"); 
     logger.error("Password Expired"); 
     throw new CredentialsExpiredException("Password Expired"); 
    } 

    return user; 
    } 
} 
+0

проблема заключается в том, что UserDetailsService вызывается до проверки пароля. –

Смежные вопросы