Apache Shiro: как принудительно изменить пароль?

У меня запущено приложение на основе Java EE 7 (JSF, JPA) и CDI, использующее Shiro как для аутентификации, так и для авторизации.

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

План состоит в том, чтобы повторно отобразить страницу входа и представить другую форму (сменить пароль вместо входа в систему). Все идет нормально. Однако: как я могу принудительно изменить пароль и не позволить пользователю перейти на другую страницу?

Есть ли рекомендуемое (или даже встроенное) решение?

Моя идея состояла бы в том, чтобы реализовать фильтр, который проверяет объект входа в сеанс на предмет того, нужно ли сбрасывать PW или нет. Можно было бы надеяться, что это так же просто, как создать новый фильтр, внедрить туда логин и проверить состояние флага - и перенаправить пользователя на страницу входа, пока флаг истинен / он не обновляет свой pw.

(У нас уже есть пользовательский EnvironmentLoaderListener с поддержкой cdi для поддержки нашей области JPA.)

Новый фильтр будет идти за последней строкой?

[urls]
/javax.faces.resource/** = anon
/layout.xhtml = anon
/css/** = anon
/login.xhtml = user
/logout.xhtml = logout
/** = user

Итак, у нас есть:

/** = user,pwresetfilter

Предложения по деталям, а также по общему решению приветствуются.


person th_eg    schedule 16.06.2014    source источник


Ответы (2)


Вы можете использовать свое решение, но, вероятно, лучше иметь такое:

  1. Вы делаете свое собственное царство

    MyRealm extends AuthorizingRealm {
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        }
    }
    

    Здесь, в doGetAuthenticationInfo, вы проверите учетные данные и создадите исключение, если пароль необходимо изменить. Не стесняйтесь расширять область, используемую в настоящее время.

  2. В вашем EnvironmentLoaderListener вы регистрируете свою область

  3. Возможно, в конце концов вам понадобится фильтр для перенаправления на правильную страницу, или если вы используете REST (например, Джерси), у вас может быть сопоставитель исключений, который что-то ответит клиенту вашего браузера.

person win_wave    schedule 17.06.2014
comment
Таким образом, когда выдается это исключение, вы затем сохраняете текущий пароль и имя пользователя в bean-компоненте, запрашиваете смену пароля, а затем одновременно меняете и логин, и pw (сначала pw, затем логин)? у нас уже есть пользовательские области, так что это не будет проблемой. Но разве вы не хотели бы сначала войти в систему с его текущим паролем? В вашем сценарии вам нужно будет выполнить действие сброса пароля для пользователя, не вошедшего в систему, у которого вообще нет никаких разрешений. Я не считаю это очень безопасным. - person th_eg; 18.06.2014
comment
Вы перенаправляете пользователя на страницу, где он/она пишет свой текущий пароль и новый пароль. Конечно, эта страница будет вне безопасности, но это не повредит. Или, может быть, лучшее решение, вы не выдаете исключение, а говорите, что у этого пользователя есть только разрешение на просмотр страницы смены пароля. - person win_wave; 18.06.2014

У меня было аналогичное требование, которое было для OTP после аутентификации, и я использовал обычный фильтр, чтобы отфильтровать все запросы. СДЕЛАЙТЕ атрибут используемого bean-компонента, например lastPasswordChangedDate, или может быть isPasswordChangerequired, как вам нравится. и сравните его в фильтре.

Мой простой код otpFliter выглядит следующим образом, но вы можете сделать свой собственный в соответствии с потребностями, например jsf и т. д.:

/**
 * Servlet Filter implementation class OTPFilter
 */
@WebFilter(urlPatterns = {"/*"},initParams={@WebInitParam(name="enabled",value="0")})
public class OTPFilter implements Filter {

    /**
     * Default constructor. 
     */

    boolean enabled=true;


    public OTPFilter() {
        // TODO Auto-generated constructor stub
    }

    /**
     * @see Filter#destroy()
     */
    public void destroy() {
        // TODO Auto-generated method stub
    }

    /**
     * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // TODO Auto-generated method stub
        // place your code here

        // pass the request along the filter chain
        //System.out.println(enabled);
            if(enabled){
            if(SecurityUtils.getSubject().getPrincipal()!=null){
                if(request instanceof HttpServletRequest ){
                    HttpSession session = ((HttpServletRequest) request).getSession();
                    LoggedInUser user =  (LoggedInUser) session.getAttribute("userinfo");
                    String url = ((HttpServletRequest) request).getRequestURL().toString();
                    //System.out.println("url is "+ url);

                    if( !url.contains("public") && !user.isOTPverified()){



                        if(user.getOTP() == null)
                            {
                                user.setOTP(OTPUtils.generateOTP());
                            }
                        //user.setOTPverified(true);                        
                        ((HttpServletRequest) request).getRequestDispatcher("OTP.jsp").forward(request, response);

                        return;
                    }
                }
            }


            }

        chain.doFilter(request, response);
    }

    /**
     * @see Filter#init(FilterConfig)
     */
    public void init(FilterConfig fConfig) throws ServletException {
        // TODO Auto-generated method stub
        //System.out.println("fConfig.getInitParameter :" + fConfig.getInitParameter("enabled"));
         enabled = fConfig.getInitParameter("enabled").equals("1");
    }

}
person Dev    schedule 25.06.2014
comment
Вы правы, для этого нет необходимости использовать фильтр Широ. Я склоняюсь к использованию решения фильтра. - person th_eg; 02.07.2014