如何在 JSF 中实现登录过滤器?

Posted

技术标签:

【中文标题】如何在 JSF 中实现登录过滤器?【英文标题】:How implement a login filter in JSF? 【发布时间】:2012-01-18 18:55:18 【问题描述】:

即使用户知道某些页面的url,我也想阻止某些页面的访问。 例如/localhost:8080/user/home.xhtml(需要先登录)如果没有登录则重定向到/index.xhtml

如何在 JSF 中做到这一点?我在 Google 中看到需要过滤器,但我不知道该怎么做。

【问题讨论】:

【参考方案1】:

您需要实现javax.servlet.Filter 类,在doFilter() 方法中完成所需的工作,并将其映射到覆盖受限页面的URL 模式上,/user/* 可能吗?在doFilter() 中,您应该以某种方式检查会话中登录用户的存在。此外,您还需要考虑 JSF ajax 和资源请求。 JSF ajax 请求需要一个特殊的 XML 响应来让 javascript 执行重定向。需要跳过 JSF 资源请求,否则您的登录页面将不再有任何 CSS/JS/图像。

假设您有一个/login.xhtml 页面,该页面通过externalContext.getSessionMap().put("user", user) 将登录用户存储在JSF 托管bean 中,那么您可以通过session.getAttribute("user") 获取它,如下所示:

@WebFilter("/user/*")
public class AuthorizationFilter implements Filter 

    private static final String AJAX_REDIRECT_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        + "<partial-response><redirect url=\"%s\"></redirect></partial-response>";

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException     
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        HttpSession session = request.getSession(false);
        String loginURL = request.getContextPath() + "/login.xhtml";

        boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
        boolean loginRequest = request.getRequestURI().equals(loginURL);
        boolean resourceRequest = request.getRequestURI().startsWith(request.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER + "/");
        boolean ajaxRequest = "partial/ajax".equals(request.getHeader("Faces-Request"));

        if (loggedIn || loginRequest || resourceRequest) 
            if (!resourceRequest)  // Prevent browser from caching restricted resources. See also https://***.com/q/4194207/157882
                response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
                response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
                response.setDateHeader("Expires", 0); // Proxies.
            

            chain.doFilter(request, response); // So, just continue request.
        
        else if (ajaxRequest) 
            response.setContentType("text/xml");
            response.setCharacterEncoding("UTF-8");
            response.getWriter().printf(AJAX_REDIRECT_XML, loginURL); // So, return special XML response instructing JSF ajax to send a redirect.
        
        else 
            response.sendRedirect(loginURL); // So, just perform standard synchronous redirect.
        
    


    // You need to override init() and destroy() as well, but they can be kept empty.

此外,过滤器还禁用了安全页面上的浏览器缓存,因此浏览器后退按钮将不再显示。

如果你碰巧使用了 JSF 实用程序库OmniFaces,上面的代码可以简化如下:

@WebFilter("/user/*")
public class AuthorizationFilter extends HttpFilter 

    @Override
    public void doFilter(HttpServletRequest request, HttpServletResponse response, HttpSession session, FilterChain chain) throws ServletException, IOException 
        String loginURL = request.getContextPath() + "/login.xhtml";

        boolean loggedIn = (session != null) && (session.getAttribute("user") != null);
        boolean loginRequest = request.getRequestURI().equals(loginURL);
        boolean resourceRequest = Servlets.isFacesResourceRequest(request);

        if (loggedIn || loginRequest || resourceRequest) 
            if (!resourceRequest)  // Prevent browser from caching restricted resources. See also https://***.com/q/4194207/157882
                Servlets.setNoCacheHeaders(response);
            

            chain.doFilter(request, response); // So, just continue request.
        
        else 
            Servlets.facesRedirect(request, response, loginURL);
        
    


另见:

Our Servlet Filters wiki page How to handle authentication/authorization with users in a database? Using JSF 2.0 / Facelets, is there a way to attach a global listener to all AJAX calls? Avoid back button on JSF web application JSF: How control access and rights in JSF?

【讨论】:

而不是使用getAttribute("auth"),你能用@ManagedProperty(value="#auth") private Auth auth; @Jake:错了。 @ManagedProperty 仅适用于 @ManagedBean,不适用于 @WebFilter @Jake:如果您使用 CDI 的 @Named 代替,那么您可以使用 @Inject 将其注入 @ManagedBean(或只是另一个 @Named)和 @987654344 @. @BalusC:我们是否需要担心这种技术中的线程安全? @BalusC 如果过滤器映射到/user/* 并且登录页面位于/login.xhtml 我想登录请求不会被捕获。登录页面应该位于/user/login.xhtml,但我问自己:为什么我们需要保护登录页面?【参考方案2】:

虽然使用简单的 Servlet 过滤器当然是合法的,但还有其他替代方法,例如

Spring Security Java EE Security Apache Shiro

【讨论】:

以上是关于如何在 JSF 中实现登录过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java Web 应用程序中实现复杂的页面流

JSF 登录过滤器,会话为空

如何使用 PrimeFaces 在 JSF 上重定向?

如何在 play 2.2.0 Java 中实现 CORS 过滤器?

如何在 RealmRecyclerViewAdapter 中实现可过滤

如何在 android 中实现 Drag Distortion Image 过滤器?