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

52

我希望即使用户知道某些页面的URL,也能够阻止其访问某些页面。 比如说:/localhost:8080/user/home.xhtml(需要先登录),如果未登录,则重定向到/index.xhtml

在JSF中要怎么做呢?我在Google上看到需要一个过滤器,但我不知道该如何实现。

2个回答

102
你需要实现 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://dev59.com/FG855IYBdhLWcg3wsWr3
                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://dev59.com/FG855IYBdhLWcg3wsWr3
                Servlets.setNoCacheHeaders(response);
            }

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

}

另请参见:


5
@Jake:错误。@ManagedProperty 只能在 @ManagedBean 中使用,而不能在 @WebFilter 中使用。 - BalusC
5
如果你使用CDI的@Named,那么你可以使用@Inject来在一个@ManagedBean(或另一个@Named)和一个@WebFilter中注入它。 - BalusC
@BalusC 如果过滤器映射到“/user/*”,而登录页面位于“/login.xhtml”,我认为登录请求不会被捕获。登录页面应该位于“/user/login.xhtml”,但是我问自己:我们为什么需要保护登录页面? - maurizeio
1
@FarshiDNaqizadeh:在AuthorizationFilter代码片段中,它被定义为常量。如果您看不到它,请使用Ctrl + F查找。 - BalusC
1
@FarshiDNaqizadeh:在第一段中已经解释了:“JSF ajax请求需要一个特殊的XML响应,以便让JavaScript执行重定向。”。 - BalusC
显示剩余5条评论

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接