2013-11-02 49 views
6

我正在使用GlassFish服務器4.0,我已將不同的權限/角色分配給不同的用戶。如何讓每個用戶根據其在JAAS中的權限/角色在特定位置訪問資源?

用戶可能擁有多個權限/角色。例如,管理員用戶可能與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_ADMINROLE_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. 
} 

如果你需要看其他的東西在我的應用程序的話,請讓我知道。

+0

你到底是如何以註冊用戶身份登錄管理員的?如果一個特定的用戶有多個角色,那麼實際上沒有辦法只使用其中的一個登錄。所以整個問題很混亂。 – BalusC

+0

這是一個驗證用戶身份的過濾器。過濾器映射到URL模式 - '/ jass/*'(錯誤地鍵入,應該是'/ jaas/*')。它指示一個目錄,其中只有一個頁面 - 調用JSF頁面上的登錄按鈕時(通過其相應的JSF託管bean)調度請求的'temp.jsp'。有沒有辦法一次作爲管理員登錄和一次使用相同的ID /密碼的用戶登錄?我也覺得如此,但認爲容器可能支持一些機制來做到這一點。我看了一些教程,但一無所知。 – Tiny

回答

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)

    然後在主tamplate:

    <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這樣的大多數安全框架都有builtin APIs

+0

我目前堅持「*每用戶基礎*」方法,而不是不必要地進入複雜性。主要是,我想知道它是否受到容器的支持,開箱即用,我完全厭倦了來回翻看Oracle文檔。:)現在我去掉了我很大的疑問。謝謝。 – Tiny

1

我覺得得到你要的是非常小心這種行爲,可能添加其他管理員帳戶,而不是並從該帳戶的ROLE_USER的方式。

+0

在問這個問題之前,我也認爲這只是這個問題的答案:)。這樣做,但是我們不能說一個用戶可以擁有多個權限/角色,因爲用戶需要擁有多個對應於多個權限/角色的帳戶。雖然單個用戶可能擁有多個具有不同權限/角色的帳戶,但是**系統都將被系統視爲不同的用戶** :)。 – Tiny

相關問題