带有CDI的JSF 2.3中的会话固定和会话作用域Bean

问题描述 投票:3回答:2

登录用户时,更新HTTP会话是一种常见的最佳做法。这将强制使用新的会话ID,从而避免了会话固定漏洞。

当涉及@SessionScoped bean时,是否存在使用CDI实现此目标的首选模式?困难在于,通过使当前HTTP会话无效,您将在下一个请求(而不是下一个请求)之前获得一个不同的会话范围的Bean。

例如,假设有一个用于存储用户登录信息的会话Bean:

@Named("sessionbean")
@SessionScoped
public class SessionBean implements Serializable {
    private int userId;
    private String username;
    private List<String> privileges;

    // Accessors omitted 
}

和另一个用于管理登录的bean:

@Named("loginbean")
@ViewScoped
public class LoginBean implements Serializable {

    private String username;
    private String password;
    @Inject private SessionBean session;
    @Inject private SessionManager sessionManager;
    @Inject private PrivilegeManager privilegeManager;      

    public String doLogin() {
        String destinationUrl;

        if (validate(username, password)) {
            FacesContext context = FacesContext.getCurrentInstance();

            // force renewal of HTTP session
            context.getExternalContext().invalidateSession();

            // retrieve new session bean  ** No longer works with CDI **
            Application app = context.getApplication();
            session = app.evaluateExpressionGet(context, "#{sessionbean}", SessionBean.class);

            session.setUsername(username);
            session.setSessionId(sessionManager.createNewSession(username));
            session.setPrivileges(privilegeManager.getPrivileges(username));

            destinationUrl = createLandingPageUrl();

        } else {
            destinationUrl = createFailureUrl("Unknown user or password");
        }

        return destinationUrl;
    }
}

对于托管Bean,这将检索一个新的SessionBean,但是对于CDI,以上代码将仅返回相同的SessionBean。有什么建议或聪明的主意吗?

session jsf cdi jsf-2.3
2个回答
0
投票

由于我不是安全专家,因此我将答案仅限于CDI。我也不知道所要求的一般想法是否是一个好主意。无论如何,这就是我认为您会按照自己的意愿去做的事情。

用纯CDI术语表示,问题可以改写为:

我有一个对象,该对象来自特定的Context。我知道此Context产生的对象的生命周期。如何正确地告诉Context使其正在管理的当前对象无效,并加载或创建一个新对象?

一般方法将是:

  • [@Inject一个Provider<SessionBean>而不是直接一个SessionBean(这将使您可以向CDI正确地请求“新”对象)]
  • [@Inject a BeanManager(因此您可以获得管理Context对象的正确SessionScoped)]
  • BeanManager给您与AlterableContext注释对应的SessionScoped
  • 告诉AlterableContext销毁当前bean的上下文实例
  • 调用Provider.get()来创建一个新的]

因此doLogin方法的相关部分可能看起来像这样(未经测试):

final AlterableContext context = (AlterableContext) this.beanManager.getContext(SessionScoped.class);
assert context != null;

final Bean<?> bean = beanManager.resolve(beanManager.getBeans(SessionBean.class));
assert bean != null;

context.destroy(bean);

final SessionBean newSessionBean = this.sessionBeanProvider.get();
assert newSessionBean != null;

我认为应该起作用。


0
投票

困难在于,通过使当前HTTP会话无效,您将在下一个请求中获得一个不同的会话范围的Bean,但要等到下一个请求时才可以。

然后不要使会话无效,而是要更改会话ID。换句话说,不要使用HttpSession#invalidate(),而要使用HttpSession#invalidate()(自Servlet 3.1以来的新增功能,鉴于您正在使用JSF 2.3,因此无疑应该已经在使用它。)>

换句话说,替换

HttpServletRequest#changeSessionId()

作者

HttpServletRequest#changeSessionId()

这基本上更改了// force renewal of HTTP session object context.getExternalContext().invalidateSession(); cookie,而没有更改// force renewal of HTTP session ID ((HttpServletRequest) context.getExternalContext().getRequest()).changeSessionId(); 。非常适合防止会话固定。

使会话无效仅在注销期间有用。

© www.soinside.com 2019 - 2024. All rights reserved.