我的任务是更新一个旧应用程序并遇到了麻烦。该应用程序在 Railo(ColdFusion) 上运行,带有 MySQL 5 数据库。
该应用程序使用基于角色的身份验证来管理用户。之前的开发者似乎没有修改/添加/删除用户的代码,只是直接修改了数据库列。我已经处理了添加/删除用户部分,没问题。修改部分是我绊倒的地方。
下面是角色和成员的基本表格布局。
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 选择标记和几个 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,"")#' 并在我的选择标签中获取所有角色,添加一个逗号,并将它们吐到成员中。角色栏。
我什至尝试一步步创建一个数组:
<cfset roleArray = ArrayNew(1)>
<cfloop query = "GetUser">
<cfset temp = ArrayAppend(roleArray, "#role#")>
</cfloop>
<cfset roleList = ArrayToList(roleArray, ",")>
我也尝试执行 SQL FIND_IN_SET()。
无论我尝试什么,我都会得到可怕的 bool 错误数组。
建议?我应该尝试一种不同于 HTML 选择标签的方法吗?有什么我忽略的吗?任何帮助将不胜感激。
希望我把这个问题发好,这是我第二次在这里发问题。 :D
--------------------更新--------------------
我想补充一点,该应用程序在整个站点中使用 IsUserInRole() 来检查用户是否可以访问某些区域。例如:
<cfif IsUserInRole("Administrator")>
Do stuff here as an administrator.
</cfif>
它也与多个组一起使用,ColdFusion 会自动查找以逗号分隔的每个组:
<cfif IsUserInRole("Administrator,Group1,Group2")>
ColdFusion 是否有更好的方式来管理角色?
--------------------更新#2--------------------
我发现最初的开发者使用 CFLOGIN 来为用户设置角色。我听说过关于 CFLOGIN 的恐怖故事,那么它是否应该被禁止以支持更好的东西?也许改为在 session 变量中设置用户角色?
<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>
注意到 cfquery 参数了吗? :D 我很快应用了从你们那里学到的东西! :D
最佳答案
适当角色系统的示例结构
我只是将这样的系统放入 Leigh 描述的应用程序中。不幸的是,虽然它是为 Railo 构建的,但它是基于 SQL Server 构建的,因此您可能需要修改 sql。
Users
UserID | Username ....
Roles
RoleID | RoleTitle ...
Permissions
UserID | RoleID
我的编辑用户查询如下所示
你会注意到很长的
<cfqueryparam>
标签。对于 Railo 和 ACF,它们都是抵御 sql 注入(inject)的关键防线。编辑:使用 Leigh 编写的更清晰的查询进行了更新。它比用于相同目的的我的版本更干净。
我确实添加了第 6 行 (
and r.roleID in (select distinct pm.roleID from permissions pm where userID = <cfqueryparam cfsqltype="int" value="#session.userID#">)
)。这确保了审核用户可以影响的唯一角色是他们自己所在的角色。
-
/* pull user details and role in one query */
SELECT ur.*, CASE WHEN p.UserID IS NOT NULL THEN 1 ELSE 0 END AS HasRole
FROM (SELECT u.*, r.RoleID, r.RoleTitle
FROM Users u CROSS JOIN Roles r
WHERE u.UserID = <cfqueryparam cfsqltype="int" value="#url.userID#">
and r.roleID in (select distinct pm.roleID from permissions pm where userID = <cfqueryparam cfsqltype="int" value="#session.userID#">)) ur
LEFT JOIN Permissions p
ON p.RoleID = ur.RoleID
AND p.UserID = ur.UserID;
/* get permissions separately */
SELECT r.RoleID, r.RoleTitle, CASE WHEN p.RoleID IS NOT NULL THEN 1 ELSE 0 END AS HasRole
FROM Roles r LEFT JOIN Permissions p
ON p.RoleID = r.RoleID
AND p.UserID = <cfqueryparam cfsqltype="int" value="#url.userID#">
在表单本身中,我有这段代码。
<cfoutput query="GetUser" group="username">
...other form elements...
<input type="checkbox" name="roleschanged" value="1" onchange="if ($(this).prop('checked')) { $('.rolesCheck').removeAttr('disabled'); } else { $('.rolesCheck').attr('disabled','false'); }"> Editing Roles?<br><br>
<cfoutput group="roleID">
<input type="checkbox" name="roleID" value="#roleID#" class="rolesCheck" disabled #(hasrole eq 1 ? "checked" : "")#> #RoleTitle#<br>
</cfoutput>
..
</cfoutput>
复选框加载已禁用,但如果用户是该角色的一部分,则会选中它们,如果不是,则取消选中。 roleschanged 复选框可以启用它们,因此可以更改角色,并且还在处理页面上起到关键作用,表示用户角色已被编辑。
在表单处理代码中,我有。
<cfif isDefined("form.rolesChanged") and form.rolesChanged eq 1><cfquery datasource="#request.dsn#">
delete from Permissions
where userID = <cfqueryparam cfsqltype="int" value="#val(url.userID)#">
and roleID in (select distinct pm.roleID from permissions pm where userID = <cfqueryparam cfsqltype="int" value="#session.userID#">)
</cfquery>
<cfloop list="#form.roleID#" index="i">
<cfquery datasource="#request.dsn#">
insert into Permissions(userID,roleID)
values(<cfqueryparam cfsqltype="int" value="#val(url.userID)#">,<cfqueryparam cfsqltype="int" value="#val(i)#">)
</cfquery>
</cfloop></cfif>
Form.roleschanged 的最终目的是让这些查询不会在每次运行页面时运行,而不管角色是否采取行动。
虽然这是最终目的,但它确实允许审核用户跳过更改数据而无需重置表单的其余部分。
为了直观地表示该功能,这就是在页面加载时禁用复选框的原因。
我的角色表还有一个 Area 字段,它是他们可以编辑的区域的绝对路径,比如......
RoleID, RoleTitle, RoleArea
1 Pages /wwwroot/cpanel/cms/
2 News /wwwroot/cpanel/news/
每当用户尝试访问 cpanel 中的任何文件夹时,我都会进行检查,如果他们没有与他们尝试访问的文件夹相匹配的角色,他们就会被踢出注销。
我也可以(但不能)使用此表来构建管理链接,以便管理员只能看到指向他们可以使用的区域的链接。 (我从我的 cms 中提取链接,但我的 cms 条目也有指向角色表的链接。唯一的区别是更基本的路由将链接到 index.cfms 列出用户可以在每个文件夹中做什么,而我实际上使用的方法使用允许我提供直接链接..并不是一个显着的收获)。
关于html - SQL 查询内联表,打印到 HTML <select> 标签,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26227103/