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

Val*_*lva 47 jsf authorization login jsf-2 servlet-filters

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

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

Bal*_*usC 91

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

假设您有一个/login.xhtml将登录用户存储在JSF托管bean中的页面externalContext.getSessionMap().put("user", user),那么您可以通过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://stackoverflow.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.
}
Run Code Online (Sandbox Code Playgroud)

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

如果您碰巧使用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://stackoverflow.com/q/4194207/157882
                Servlets.setNoCacheHeaders(response);
            }

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

}
Run Code Online (Sandbox Code Playgroud)

也可以看看:

  • @Jake:如果你正在使用CDI的`@ Named`,那么你可以使用`@Inject`将它注入一个`@ManagedBean`(或者只是另一个`@ Named`)和一个`@WebFilter`. (5认同)
  • @Jake:错了.`@ManagedProperty`仅适用于`@ManagedBean`,而不适用于`@WebFilter`. (4认同)