我的任务是更新旧的应用程序,然后遇到了麻烦。该应用程序在带有MySQL 5数据库的Railo(ColdFusion)上运行。
该应用程序正在使用基于角色的身份验证来管理用户。以前的开发人员似乎没有修改/添加/删除用户的代码,而只是直接修改了数据库列。我已经处理了添加/删除用户部分,没问题。修改部分是我偶然发现的地方。
下面是角色和成员的基本表布局。
MEMBERS TABLE
id | username | role
1 | user1 | Administrator,User,Group1,Group2
2 | user2 | User,Developer
3 | user3 | SuperUser,Group1,Group2
4 | user4 | SuperUser,Developer
5 | user5 | Guest
ROLES TABLE
role_id | role_name
1 | Administrator
2 | Developer
3 | SuperUser
4 | Guest
5 | User
6 | Group1
7 | Group2
要修改用户的角色,我的第一个想法是使用一个简单的HTML select标签和几个CFQUERY。类似于下面的代码。
<select name="role" multiple="multiple" size="7">
<cfoutput>
<option value="#GetRole.role_name#" <cfif GetRole.role_name = GetUser.role>checked</cfif>>#GetRole.role_name#</option>
</cfoutput>
</select>
<CFQUERY NAME="GetRole" DATASOURCE="#request.dataSource#">
SELECT role_name
FROM roles
</CFQUERY>
<CFQUERY NAME="GetUser" DATASOURCE="#request.dataSource#">
SELECT a.id, a.username, a.role, b.role_name
FROM members a
LEFT JOIN roles b ON b.role_name = a.role
WHERE a.id = #FORM.id#
</CFQUERY>
select标签将从GetRole中提取并填充所有可用角色。GetUser查询将从成员表中的列表中查找用户是其当前成员的角色。然后,我只需在select标签中使用“ checked”标志。那是我碰壁的地方。不管我走什么路,我都会不断进入数组以列出问题。
当我添加一个新用户时,我只使用“ role ='#ListQualify(FORM.role,”“)#',它将所有角色添加到我的select标记中,添加一个逗号,然后将它们吐入members.role列。
我什至试图一步一步地创建一个数组:
<cfset roleArray = ArrayNew(1)>
<cfloop query = "GetUser">
<cfset temp = ArrayAppend(roleArray, "#role#")>
</cfloop>
<cfset roleList = ArrayToList(roleArray, ",")>
我也尝试做一个SQL FIND_IN_SET()。
无论我尝试什么,我都会将可怕的数组转换为布尔错误。
有什么建议吗?我应该尝试使用不同于HTML select标签的方法吗?有什么我忽略的东西吗?非常感激任何的帮助。
希望我很好地发布了这个问题,只有第二次在这里发布了问题。:D
- - - - - - - - - - 更新 - - - - - - - - - -
我想补充一点,该应用程序在整个站点上都使用IsUserInRole()来检查用户是否可以访问某些区域。例如:
<cfif IsUserInRole("Administrator")>
Do stuff here as an administrator.
</cfif>
它还与多个组一起使用,ColdFusion会自动查找每个用逗号分隔的组:
<cfif IsUserInRole("Administrator,Group1,Group2")>
ColdFusion是否有更好的方法来管理角色?
--------------------更新#2 --------------------
我发现原始开发人员使用CFLOGIN来为用户设置角色。我听说过有关CFLOGIN的恐怖故事,是否应该改而改用更好的东西?而是在会话变量中设置用户角色?
<cfquery name="qryGetUserDetails" datasource="#request.datasource#">
SELECT *
FROM members
WHERE username = <cfqueryparam cfsqltype="cf_sql_varchar" value='#Trim(FORM.username)#'>
</cfquery>
<cflogin>
<cfloginuser name="#FORM.username#" password="#FORM.password#" roles="#qryGetUserDetails.role#">
</cflogin>
注意到cfqueryparam吗?:DI迅速运用了我从你们那里学到的知识!:D
(评论太久了)
我什至试图一步一步地创建一个数组:
暂时忽略db结构,创建数组是浪费时间:)如果将角色存储为列表,那么您要做的就是获取列表,创建单个元素数组并将其再次转换回相同的列表。因此摆脱数组。这没有任何目的。
只需循环getRole
查询并使用列表功能即可。Dan很好地说明了复选框,但我将使用您的原始代码更好地说明:
<select name="role" multiple="multiple" size="7">
<cfoutput query="getRole">
<option value="#getRole.role_name#"
<!--- if the current role is found in the list of user roles --->
<cfif listFindNoCase(getUser.ListOfRoles, getRole.role_name)>
selected
</cfif>>
#GetRole.role_name#
</option>
</cfoutput>
</select>
话虽如此,真正的问题是您的数据库结构。存储列表是其中的一件事情,它看起来会让生活变得更轻松,但是几乎总是会产生比它解决的问题更多的问题。例如,使用当前结构-您将如何识别具有“管理员”和“来宾”角色而不是“超级用户”角色的所有用户?
尽管有一些技巧可以解决存储列表的一些固有限制,但您应该尽可能地更改表结构。列表更容易出现数据完整性问题。另外,由于依赖于字符串函数,因此经常需要使用SQL索引的卷积SQL查询,因此无法很好地扩展。
正如我在评论中提到的,更好的结构是创建第三个表:MemberRole。将每个memberID + roleID组合存储为单独的行。该结构将提供更大的灵活性和可靠性。有关示例,请参见bricktsage的答案。虽然可以稍微简化“编辑用户”查询的联接。为了清楚起见,我删除了一些逻辑。但是,正如原始帖子中提到的那样,出于安全原因,您可能需要添加其他过滤器来限制当前用户可以分配的角色。否则,任何用户都可以分配任何权限。
注意:我添加了一个布尔标志,希望在我的应用程序中使用。使用OUTER JOIN和CASE语句,您可以创建一个名为的布尔列IsAssigned
,该列指示是否将每个角色都分配给了所选用户。该标志可用于在编辑屏幕上预选列表项(或复选框)。
SELECT ur.roleID
, ur.roleTitle
, ur.UserID
, ur.UserName
, CASE WHEN p.UserID IS NOT NULL THEN 1 ELSE 0 END AS IsAssigned
FROM (
SELECT u.UserID
, u.UserName
, r.RoleID
, r.RoleTitle
FROM Users u CROSS JOIN Roles r
WHERE u.UserID = <cfqueryparam value="#FORM.id#" cfsqltype="cf_sql_integer">
)
ur LEFT JOIN Permissions p
ON p.RoleID = ur.RoleID
AND p.UserID = ur.UserID
注意:请务必阅读CROSS JOIN
就是说,出于可读性考虑,我经常只运行两个查询:一个查询获取用户信息,另一个查询分配的角色。这是一个额外的db调用,但是回撤的数据略少,因此额外的查询并不是什么大问题。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句