6

我正在使用 GlassFish server 4.0,其中我为不同的用户分配了不同的权限/角色。

一个用户可能有多个权限/角色。例如,管理员用户可能与ROLE_ADMIN(以执行管理任务)和ROLE_USER(以作为注册用户执行任务)相关联。

在我的web.xml中,它的配置如下。

<security-constraint>
    <display-name>AdminConstraint</display-name>
    <web-resource-collection>
        <web-resource-name>ROLE_ADMIN</web-resource-name>
        <description/>
        <url-pattern>/admin_side/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <description/>
        <role-name>ROLE_ADMIN</role-name>
    </auth-constraint>
    <user-data-constraint>
        <description/>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<security-constraint>
    <display-name>UserConstraint</display-name>
    <web-resource-collection>
        <web-resource-name>ROLE_USER</web-resource-name>
        <description/>
        <url-pattern>/user_side/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <description/>
        <role-name>ROLE_USER</role-name>
    </auth-constraint>
    <user-data-constraint>
        <description/>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<login-config>
    <!--<auth-method>DIGEST</auth-method>-->
    <auth-method>FORM</auth-method>
    <realm-name>projectRealm</realm-name>
    <form-login-config>
        <form-login-page>/utility/Login.jsf</form-login-page>
        <form-error-page>/utility/ErrorPage.jsf</form-error-page>
    </form-login-config>
</login-config>

<security-role>
    <description/>
    <role-name>ROLE_ADMIN</role-name>
</security-role>

<security-role>
    <description/>
    <role-name>ROLE_USER</role-name>
</security-role>

这工作得很好。


有两种 URL 模式/admin_side/*/user_side/*. 管理员有两个ROLE_ADMIN角色ROLE_USER

当管理员使用权限登录时,应该访问位于ROLE_USER唯一资源。应该禁止访问/user_side/*位于其中的资源,因为管理员是以注册用户而不是管理员身份登录的。/admin_side/*

到目前为止,在我的情况下发生的情况是,当管理员使用任何权限登录时,可以访问这两个位置的资源,这是完全非法的。这是因为系统能够找到该特定用户的两个权限。

如何让每个用户根据他们的权限/角色访问特定位置的资源?


身份验证过滤器:

@WebFilter(filterName = "SecurityCheck", urlPatterns = {"/jass/*"})
public final class SecurityCheck implements Filter
{
    private FilterConfig filterConfig = null;

    @Resource(mappedName="jms/destinationFactory")
    private ConnectionFactory connectionFactory;
    @Resource(mappedName="jms/destination")
    private Queue queue;
    @EJB
    private final UserBeanLocal userService=null;

    public SecurityCheck() {}

    private void sendJMSMessageToDestination(String message) throws JMSException
    {
        Connection connection = null;
        Session session = null;

        try
        {
            connection = connectionFactory.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            MessageProducer messageProducer = session.createProducer(queue);
            TextMessage textMessage = session.createTextMessage();
            textMessage.setText(message);
            messageProducer.send(textMessage);
        }
        finally
        {
            if(session!=null){session.close();}
            if(connection!=null){connection.close();}
        }
    }

    private void doBeforeProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException
    {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        httpServletRequest.login(httpServletRequest.getParameter("userName"), httpServletRequest.getParameter("password"));
    }

    private void doAfterProcessing(ServletRequest request, ServletResponse response) throws IOException, ServletException, JMSException
    {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        HttpServletResponse httpServletResponse=(HttpServletResponse)response;
        ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
        Map<String, Object> sessionMap = externalContext.getSessionMap();

        if(httpServletRequest.isUserInRole("ROLE_USER"))
        {
            sendJMSMessageToDestination(httpServletRequest.getLocalName());
            UserTable userTable = userService.setLastLogin(httpServletRequest.getParameter("userName"));
            userTable.setPassword(null);
            sessionMap.put("userName", userTable!=null?userTable.getFirstName():"Unknown");
            sessionMap.put("user", userTable);

            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.sendRedirect("../user_side/Home.jsf");
        }
        else if(httpServletRequest.isUserInRole("ROLE_ADMIN"))
        {
            sendJMSMessageToDestination(httpServletRequest.getLocalName());
            UserTable userTable = userService.setLastLogin(httpServletRequest.getParameter("userName"));
            userTable.setPassword(null);
            sessionMap.put("adminName", userTable!=null?userTable.getFirstName():"Unknown");
            sessionMap.put("user", userTable);

            httpServletResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            httpServletResponse.setHeader("Pragma", "no-cache");
            httpServletResponse.setDateHeader("Expires", 0);
            httpServletResponse.sendRedirect("../admin_side/Home.jsf");
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        try
        {
            doBeforeProcessing(request, response);
        }
        catch (Exception e)
        {
            HttpServletResponse httpServletResponse=(HttpServletResponse)response;
            //FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "Error", "Incorrect user name and/or password. Access denied."));
            httpServletResponse.sendRedirect("../utility/Login.jsf");
            return;
        }

        chain.doFilter(request, response);

        try
        {
            doAfterProcessing(request, response);
        }
        catch (JMSException ex)
        {
            Logger.getLogger(SecurityCheck.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    //The rest of the filter.
}

如果您需要在我的应用程序中查看其他内容,请告诉我。

4

2 回答 2

1

我认为获得您要求的行为的方法非常小心,也许可以添加另一个管理员帐户并从该帐户中删除 ROLE_USER。

于 2013-11-11T02:49:58.400 回答
1

您似乎认为当一个用户具有多个角色时,该用户只能同时使用其中一个角色登录。这不是真的。用户不是按角色登录的。用户基于每个用户登录。如果一个用户有多个角色,那么它们将在整个登录会话中使用和应用。

实际上,不可能让用户在整个会话期间只选择和使用其中一个分配的角色。到目前为止,这听起来太像您的管理员不应该ROLE_USER首先拥有的。但这在现实世界中毫无意义。角色不应该“扩展”现有角色。即ROLE_ADMIN不应该复制相同的限制ROLE_USER然后在此之上添加更多内容。不,它应该仅仅代表“更多”。然后只为管理员用户分配这两个角色(您正确地完成了该部分)。否则,您最终会在用户和管理员都可以访问/使用的地方对整个代码进行重复检查。然后我不是在谈论第三个角色,它可能需要在代码中进行三次检查。您需要在所有地方编辑现有代码。

如果您想在运行时以编程方式切换角色,可能是因为您希望能够以普通用户的身份“预览”站点(例如,在隐藏仅管理员部分/按钮时检查站点的外观),然后基本上有两种选择:

  1. 将一些标志设置为会话属性或可能作为请求参数,并对其进行代码检查。例如

    <h:form>
        <h:selectBooleanCheckbox value="#{sessionScope.preview}">
            <f:ajax render="@all" />
        </h:selectBooleanCheckbox>
    </h:form>
    

    (注意:代码原样,#{sessionScope}是一个隐式 EL 变量引用ExternalContext#getSessionMap();不需要额外的支持 bean)

    然后在主模板中:

    <c:set var="userIsAdmin" value="#{request.isUserInRole('ROLE_ADMIN') and not preview}" scope="request" />
    

    在包含一些管理员特定内容的目标视图中:

    <h:commandButton value="Some awesome admin button" rendered="#{userIsAdmin}" />
    

  2. 以普通用户身份执行编程登录。您可以使用HttpServletRequest#login()以编程方式触发容器管理的身份验证。这样,管理员可以“模拟”不同的用户并浏览网站,就好像他以特定用户的身份登录一样。例如在会话范围的 bean 中:

    public void runAs(User user) {
        // ...
        try {
            request.login(user.getUsername(), user.getPassword());
            originalUser = currentUser;
            currentUser = user;
            // ...
        } catch (ServletException e) {
            // ...
        }
    }
    
    public void releaseRunAs() {
        // ...
        try {
            request.login(originalUser.getUsername(), originalUser.getPassword());
            currentUser = originalUser;
            // ...
        } catch (ServletException e) {
            // ...
        }
    }
    

    您甚至可以通过将所有以前的用户保存在会话范围内的 LILO(后进后出)队列中来扩展它。大多数安全框架(如 Apache Shiro)都为此提供了内置 API

于 2013-11-14T09:49:32.463 回答