【问题标题】:Best way to store permissions for the user account?存储用户帐户权限的最佳方式?
【发布时间】:2023-03-16 19:32:01
【问题描述】:

我有与我的应用程序中的每个帐户相关联的权限记录。每个账户可以根据账户类型拥有一个或多个权限记录。这是一个例子:

<cfquery name="qryUserPerm" datasource="#Application.dsn#">
    SELECT AccessType, AccessLevel, State, City, Building
    FROM Permissions
    WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>

上面的查询将为其中一个帐户生成如下数据:

RecID   AccountID   AccessType  AccessLevel State     City    Building
70      285A637D82B9    F            B        NY    New York    8010
71      285A637D82B9    F            B        NY    New York    5412
73      285A637D82B9    F            B        NY    New York    6103
74      285A637D82B9    F            B        NY    New York    3106

正如您在上面看到的,该帐户分配了 4 条记录。访问类型可以是完整的F 或仅查看V。访问级别可以是州“S”、城市“C”或建筑物“B”。用户当时只能分配一个访问级别,例如,用户无法分配城市和州级别。我的问题是从查询中组织特定访问级别的数据的最佳方法是什么?在这种情况下,我必须合并列表或数组中的 4 条记录。州级只能分配一个权限记录,城市和建筑物可以有多个记录。这是我所拥有的示例:

<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">

<cfloop query="qryUserPerm">
    <cfif qryUserPerm.AccessLevel EQ "S">
         <cfset local.permissionType = qryUserPerm.AccessType>
         <cfset local.permissionLevel = qryUserPerm.AccessLevel>
         <cfset local.permissionList = listAppend(permissionList, "", ",")>
    <cfelseif qryUserPerm.AccessLevel EQ "C">
         <cfset local.permissionType = qryUserPerm.AccessType>
         <cfset local.permissionLevel = qryUserPerm.AccessLevel>
         <cfset local.permissionList = listAppend(permissionList, qryUserPerm.City, ",")>
    <cfelseif qryUserPerm.AccessLevel EQ "B">
         <cfset local.permissionType = qryUserPerm.AccessType>
         <cfset local.permissionLevel = qryUserPerm.AccessLevel>
         <cfset local.permissionList = listAppend(permissionList, qryUserPerm.Building, ",")>
    <cfelse>
         <cfset local.permissionType = "">
         <cfset local.permissionLevel = "">
         <cfset local.permissionList = listAppend(permissionList, "", ",")>
    </cfif>
</cfloop>

将permissionType 和permissionLevel 保留在循环内似乎是多余的,但我目前不知道避免这种情况的更好方法。这也使得过程非常困难,以防我必须比较权限列表。如果当前登录的用户更改了他们的权限,我将不得不运行相同的过程并构建列表,以便将其与Session.premissionList 进行比较。有没有办法将这些记录与 SQL 合并?或者这种方法是更好的选择?

【问题讨论】:

  • 您如何使用应用程序中的值?回复:“如果当前登录的用户更改了他们的权限”为什么需要比较,而不是仅仅清除旧权限并存储新权限?
  • @Ageax 这也是一个选项。我在想在某些情况下,我必须在运行更新之前比较列表。
  • 比较可能在 SQL 中完成,但删除/全部替换更为典型。如果您只返回城市和建筑值,为什么不在 SQL 中这样做呢? (就此而言,您可以在 SQL 中构建列表,尽管 CF 也同样易于使用)。
  • @Ageax 有关如何在 SQL 中实现这一目标的任何示例?
  • @espresso_coffee - 我注意到你的另一个线程在 AccessLevel = "S" 时包含 "State",而这个线程没有。所以我在下面我之前的答案中添加了“状态”。

标签: sql session permissions coldfusion cfloop


【解决方案1】:

我很想删除循环。我认为这可能会使事情变得更简单。

<cfset local.permissionType = "">
<cfset local.permissionLevel = "">
<cfset local.permissionList = "">


<cfif qryUserPerm.AccessLevel EQ "S">
     <cfset local.permissionType = qryUserPerm.AccessType>
     <cfset local.permissionLevel = qryUserPerm.AccessLevel>
     <cfset local.permissionList = qryUserPerm.State>
<cfelseif qryUserPerm.AccessLevel EQ "C">
     <cfset local.permissionType = qryUserPerm.AccessType>
     <cfset local.permissionLevel = qryUserPerm.AccessLevel>
     <cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City))>
<cfelseif qryUserPerm.AccessLevel EQ "B">
     <cfset local.permissionType = qryUserPerm.AccessType>
     <cfset local.permissionLevel = qryUserPerm.AccessLevel>
     <cfset local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building))>
</cfif>

而且,如果您想在将来比较列表是否相等,您可能需要使用:

<cfset local.permissionList = ListSort(local.permissionList,"textnocase","asc")>

更新:

<cfscript>

qryUserPerm = queryExecute("
  SELECT AccessType, AccessLevel, State, City, Building 
  FROM Permissions
  WHERE AccountID = :AccountID 
",
{
  AccountID = {value = Trim(session.AccountID), cfsqltype = "cf_sql_integer"}
},
{
  datasource = Application.dsn 
});

local.permissionType = "";
local.permissionLevel = "";
local.permissionList = "";

if(qryUserPerm.AccessLevel EQ "S"){
   local.permissionType = qryUserPerm.AccessType;
   local.permissionLevel = qryUserPerm.AccessLevel;
   local.permissionList = qryUserPerm.State;
}
else if(qryUserPerm.AccessLevel EQ "C"){
   local.permissionType = qryUserPerm.AccessType;
   local.permissionLevel = qryUserPerm.AccessLevel;
   local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.City));
}
else if(qryUserPerm.AccessLevel EQ "B"){
   local.permissionType = qryUserPerm.AccessType;
   local.permissionLevel = qryUserPerm.AccessLevel;
   local.permissionList = ListRemoveDuplicates(ValueList(permissionList,qryUserPerm.Building));
}

</cfscript>

【讨论】:

  • 所以你说循环不是必需的,这可以通过valueList() 来实现?
  • @expresso_coffee 好吧,在这种情况下,看起来唯一变化的列值是 RecID、City 和 Building,因此您可以在 ValueList() 中收集这些不同的值
  • 你能提供cfscript的例子吗?我正在尝试让它与 cfscript 一起使用。
  • @espresso_coffee 我也在“cfscript”中添加了查询,因为我怀疑这部分可能对您不起作用?
  • 我现在已经把所有东西都排好了。非常感谢。
【解决方案2】:

这可以在 SQL 本身中完成,这可能比在代码中操作数据的性能更高。

数据的一个问题是State、City 和Building 列需要取消透视才能转换为逗号分隔的列表。

由于您使用的是 SQL 2008,因此您可以访问所需的功能。

查询是:http://sqlfiddle.com/#!18/0f4f7/1

; WITH cte AS (
  SELECT
      AccountID, AccessType, AccessLevel
      , CASE AccessLevel
          WHEN 'S' THEN State
          WHEN 'C' THEN City
          WHEN 'B' THEN Building
        END AS Permissions
  FROM Permissions
  WHERE AccountID = 
    <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#"> 
    /* Dynamic variable here */
)
SELECT DISTINCT AccountID, AccessType, AccessLevel
  , CASE 
      WHEN AccessLevel = 'S' THEN Permissions 
      ELSE LEFT(ca.pl, COALESCE(LEN(ca.pl),0)-1)
    END AS PermissionList
FROM cte
CROSS APPLY (
  SELECT p.Permissions + ', '
  FROM cte p
  WHERE p.AccountID = cte.AccountID
    AND p.AccessType = cte.AccessType
    AND p.AccessLevel = cte.AccessLevel
  FOR XML PATH('')
) ca (pl)     ;

我从 CTE 开始,以基于 AccessLevel 构建 Permissions 的“未透视”列表。如果可以将其放入 SQL 视图中,则可以在此处省略 WHERE 语句,并在调用视图时调用它。如果可以将 View 放入数据库,我会更喜欢 View。

获得 CTE 后,我只需选择基本列(AccountID、AccessType 和 AccessLevel,然后我 CROSS APPLYPermissions 的逗号分隔列表。我使用 FOR XML PATH('')构建以逗号分隔的列表。

如果这能够转换为视图,那将是一个简单的

<cfquery name="qryUserPerm" datasource="#Application.dsn#">
    SELECT AccessType, AccessLevel, PermissionList
    FROM myPermissionsView
    WHERE AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#trim(session.AccountID)#">
</cfquery>

如果没有,您必须尝试在 cfquery 标记内运行上述完整查询。

这应该会给你一个像这样的数据集:

| AccessType | AccessLevel |         PermissionList |
|------------|-------------|------------------------|
|          F |           B | 8010, 5412, 6103, 3106 |

您只需处理一个结果,无需循环。

================================================ ========================

如果您想采用代码内路线,我仍然建议您尝试使用cfscript 来构建结构。但是,如果您可以拥有多个 AccessLevel,那么您的结果可能不是您认为的那样。您必须仔细检查您的数据。

  local.permissionType = q2.AccessType ;
  local.permissionLevel = q2.AccessLevel ;

  switch( q2.AccessLevel ) {
    case "S" :  local.permissionList = q2.State ;
      break ;
    case "C" :  local.permissionList = ListRemoveDuplicates(ValueList(q2.City)) ;
      break ;
    case "B" :  local.permissionList = ListRemoveDuplicates(ValueList(q2.Building)) ;
      break ;
  }

https://trycf.com/gist/e811ec86f0d5a52fd9ce703f897cb5aa/acf2016?theme=monokai

【讨论】:

  • 我的建议是视图。但我更喜欢尽可能靠近源头进行数据操作;这通常意味着 SQL。我使用 SQL 的次数越多,我就越意识到当我在代码中做事时,我在我的数据库和我的网络上都被打败了。当然,就像所有其他 IT 事物一样,这取决于。如果您每分钟与数千名用户一起工作,那么将数据操作负载分散到应用服务器而不是数据库可能会更好。你必须加载测试。
  • 再说一次,我不喜欢在 CF 服务器上增加可以由数据库更有效地处理的负载。我花了一段时间才意识到数据库不仅仅是一个保存数据的地方。
  • @espresso_coffee - 听起来查询只返回 5-10 行,所以它可能是 6 行,半打其他。但是,我同意肖恩关于在数据库方面投入更多工作的观点。通常,您将负载放在最适合它的任何工具上。对于数据操作,那就是数据库。它更适合处理大型数据集和复杂的过滤。此外,它仅通过下拉您需要的内容来节省带宽。另一方面,如果您需要精细的字符串操作,CF 更适合。像许多事情一样 - 这一切都取决于任务。
  • Shawn - 伟大的头脑 ;-) 当我之前更新我的时,没有看到你已经发布了一个示例。
  • 就数据而言,代码的工作方式不如数据库在列中允许的内容重要。如果需要,设置复合键以确保它们不能输入错误数据,否则会浪费大量时间来尝试追踪某人的页面突然崩溃的原因。尤其是当他们发誓时,他们没有做出任何改变。 :-)
【解决方案3】:

您可以通过使用CASE 来缩减代码,以根据访问级别将所有内容合并到一个列中。

SELECT AccessType
        , AccessLevel
        , CASE AccessLevel
            WHEN 'C' THEN City
            WHEN 'B' THEN Building
            WHEN 'S' THEN State
          END AS AccessValue
 FROM   Permissions
 WHERE  AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">

然后从该列构建您的列表。不需要 cfif。

 <cfset local.permissionType = qryUserPerm.AccessType>
 <cfset local.permissionLevel = qryUserPerm.AccessLevel>
 <cfset local.permissionList = valueList(qryUserPerm.AccessValue)>

您也可以构建CSV list in SQL only,但不确定在这种情况下是否值得,因为在 CF 中构建同样容易。

SELECT TOP 1 AccessType
        , AccessLevel
        , STUFF(( SELECT ','+ l.AccessValue
                  FROM ( SELECT CASE AccessLevel 
                                  WHEN 'C' THEN City 
                                  WHEN 'B' THEN Building 
                                  WHEN 'S' THEN State 
                                END AS AccessValue
                         FROM   Permissions l
                         WHERE  l.AccountID = p.AccountID 
                     ) l
                  GROUP BY l.AccessValue
                  FOR XML PATH('')
                ),1,1,'') AS PermissionsList
 FROM   Permissions p            
 WHERE  AccountID = <cfqueryparam cfsqltype="cf_sql_integer" value="#session.AccountID#">       

无论如何,使用上述查询将在一行中返回您需要的所有内容:AccessType、AccessLevel 和 csv 权限列表。

 <cfset local.permissionType = qryUserPerm.AccessType>
 <cfset local.permissionLevel = qryUserPerm.AccessLevel>
 <cfset local.permissionList = qryUserPerm.PermissionsList>

【讨论】:

    猜你喜欢
    • 2011-04-10
    • 2021-10-29
    • 1970-01-01
    • 2013-09-10
    • 2016-12-23
    • 1970-01-01
    • 2020-12-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多