Java 如何处理/限制用户对 servlet 和 jsp 的访问?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18241008/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 23:40:05  来源:igfitidea点击:

How can I handle/restrict user-access to servlets & jsp's?

javajspjakarta-eeservletsuser-management

提问by MrTrustworthy

I'm currently writing a little dynamic web-application in Java. The application is supposed to be an event-platform where you can create a user-account, log in, and then you can see all open events (in a later iteration, users can create/participate in those events).

我目前正在用 Java 编写一个小的动态 Web 应用程序。该应用程序应该是一个事件平台,您可以在其中创建用户帐户、登录,然后您可以看到所有打开的事件(在以后的迭代中,用户可以创建/参与这些事件)。

Right now, the structure of the web-app could be (simplified) described like this:

现在,网络应用程序的结构可以(简化)如下描述:

Register-Servlet -> Register.jsp
        |
        V
Login-Servlet -> Login.jsp
        |
        V
Main-page-Servlet -> Main.jsp

So right now, a user could go to Login.jsp, his login-information would be sent to the Login-Servlet, which would validate it and then send it to the Main-Page-Servlet. The Main-Page-Servlet then (after validating login again) gets all current events from a database, attaches it to the request, and forwards it to the Main.jsp, which displays it for the user to see.

所以现在,用户可以转到 Login.jsp,他的登录信息将被发送到 Login-Servlet,它会验证它,然后将它发送到 Main-Page-Servlet。Main-Page-Servlet 然后(在再次验证登录后)从数据库中获取所有当前事件,将其附加到请求中,并将其转发到 Main.jsp,后者将其显示给用户查看。

Now, if a user wants to access the Main.jsp directly (without coming from the Main-Page-Servlet), it obviously can not display the available events. The workaround I'm using currently is doing a null-check to see if the events are there, and if not, redirect to the Main-Page-Servlet.

现在,如果用户想直接访问 Main.jsp(不来自 Main-Page-Servlet),显然无法显示可用事件。我目前使用的解决方法是进行空检查以查看事件是否存在,如果不存在,则重定向到 Main-Page-Servlet。

It bothers me to solve my problem like that, as I don't think that's the best practice and I think it will just create a lot of other problems the bigger my application gets.

像这样解决我的问题让我很困扰,因为我认为这不是最佳实践,而且我认为我的应用程序越大,它只会产生很多其他问题。

My first thought about this was, that it might be useful if I could simply "hide" all .jsp's from the user, so the user would be landing on servlets only and could not access the .jsp's in a different way.

我对此的第一个想法是,如果我可以简单地向用户“隐藏”所有 .jsp 可能会有用,这样用户将仅登陆 servlet 而无法以不同的方式访问 .jsp。

Is there a way to do that? Or, if not, what would be the best practice solution if I would be writing a professional enterprise-level application?

有没有办法做到这一点?或者,如果不是,如果我要编写专业的企业级应用程序,最佳实践解决方案是什么?

采纳答案by Luiggi Mendoza

This can be handled in a Filterand there are great explanation and example in StackOverflow Servlet-Filter wiki.

这可以在 a 中处理,Filter并且StackOverflow Servlet-Filter wiki 中有很好的解释和示例。

Adapting the code there for your problem (note the addition and usage of the needsAuthenticationmethod):

针对您的问题调整代码(注意该needsAuthentication方法的添加和使用):

@WebFilter("/*")
public class LoginFilter implements Filter {
    @Override
    public void init(FilterConfig config)
        throws ServletException {
        // If you have any <init-param> in web.xml, then you could get them
        // here by config.getInitParameter("name") and assign it as field.
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        HttpSession session = request.getSession(false);

        String requestPath = httpServletRequest.getRequestURI();

        if (needsAuthentication(requestPath) ||
            session == null ||
            session.getAttribute("user") == null) { // change "user" for the session attribute you have defined

            response.sendRedirect(request.getContextPath() + "/login"); // No logged-in user found, so redirect to login page.
        } else {
            chain.doFilter(req, res); // Logged-in user found, so just continue request.
        }
    }

    @Override
    public void destroy() {
        // If you have assigned any expensive resources as field of
        // this Filter class, then you could clean/close them here.
    }

    //basic validation of pages that do not require authentication
    private boolean needsAuthentication(String url) {
        String[] validNonAuthenticationUrls =
            { "Login.jsp", "Register.jsp" };
        for(String validUrl : validNonAuthenticationUrls) {
            if (url.endsWith(validUrl)) {
                return false;
            }
        }
        return true;
    }
}

I would recommend to move all the pages that require authentication inside a folder like appand then change the web filter to

我建议将所有需要身份验证的页面移动到一个文件夹中app,然后将网络过滤器更改为

@WebFilter("/app/*")

In this way, you can removethe needsAuthenticationmethod from the filter.

通过这种方式,您可以从过滤器中删除needsAuthentication方法。

回答by JavaEE guy

There're several ways to do it such as servlet filter as above. I saw in some projects they use a simpler mechanism to do it by creating a common action (servlet). So instead of extends HttpServlet, all servlet will be extended the common action. And you can implement a lot of common stuffs such as authentication, validations, permissions...

有几种方法可以做到这一点,例如上面的 servlet 过滤器。我在一些项目中看到他们使用一种更简单的机制通过创建一个通用操作 (servlet) 来实现。所以不是扩展 HttpServlet,而是所有的 servlet 都会扩展通用动作。你可以实现很多常见的东西,比如身份验证、验证、权限......

Here's common action example:

这是常见的操作示例:

public class CommonServlet extends HttpServlet {
................
................
protected boolean validate(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    response.setContentType("text/html; charset=UTF-8");
    request.setCharacterEncoding("UTF-8");

    String email = (String) request.getSession().getAttribute("email");
    Object salaryGroup = request.getSession().getAttribute("SALARY_GROUP");

    if (email == null || email.equals("")) {
        request.setAttribute("err", "You have not logged in");
        request.getRequestDispatcher("/login.jsp").forward(request, response);
        return false;
    }

................
................
}

public void setRoleAndValidate(HttpServletRequest request, HttpServletResponse response, String role)
        throws ServletException, IOException {
    if (!validate(request, response)) {
        return;
    }

    setRoleCode(role);
}
................
................

}

}

Your action servlet will be as below:

您的操作 servlet 将如下所示:

@WebServlet("/employeeManager")
public class EmployeeManager extends CommonServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
                 ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        setRoleAndValidate(request, response, Permission.EMPLOYEE_LIST.toString());

        String action = request.getParameter("action");
        .....

Here's the simple implementation

这是简单的实现