XRX/用户管理
您想管理网站用户的组,验证他们在网站上的角色,并跟踪依赖于用户的相关信息。此应用程序将管理用户列表,并将用户的登录尝试次数,会话超时间隔以及分配给他们的角色相关联。
我们的应用程序将使用基于角色的访问控制 (RBAC)。我们将使用几个函数来执行用户管理。
xmldb:login($collection, $user, $pass) will log the user into the system and verify they have write access to a collection.
注意,要注销用户,我们将登录更改为用户“guest”。
xmldb:get-current-user() will return the user-id of the current user.
我们还需要添加一个新函数来检查给定用户是否具有某个角色。
auth:has-role(xmldb:get-current-user(), $role) as xs:boolean check to see if a user has a given role.
如果当前用户具有给定角色,这将返回true()。
以下是一个用户信息的示例。请注意,我们不会将密码存储在此文件中。eXist 系统用于存储用户的密码。
<user>
<id>47</id>
<user-id>jdoe</user-id>
<person-given-name>John</person-given-name>
<person-family-name>Doe</person-family-name>
<account-active-indicator>true</account-active-indicator>
<max-login-retrys>5</max-login-retrys>
<session-timeout-minutes>60</session-timeout-minutes>
<roles>
<role>ba</role>
<role>ba-admin</role>
</roles>
</user>
在上面的示例中,用户jdoe的角色是ba和ba-admin,这意味着该用户被归类为业务分析师,并且还具有管理系统中业务分析师工具的能力。
因此,以下函数
auth:has-role('jdoe', 'ba')
将返回true()
以下是一个简化的登录表单,允许用户输入登录名和密码并执行 HTTP POST 操作。请注意,这与 eXist 标准登录不同,因为它不会使用 HTTP get 将密码放在 URL 上。此信息放置在日志文件中可能会损害系统的安全性。
xquery version "1.0";
import module namespace style ='http://code.google.com/p/xrx/style' at '/db/xrx/modules/style.xqm';
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";
let $user := xmldb:get-current-user()
(: this gets the entire input URL :)
let $get-query-string := request:get-query-string()
(: were are only interested in the portion after the return= :)
let $return-uri := substring-after($get-query-string, 'return=')
return
<html>
<head>
<title>Login</title>
{style:import-css()}
</head>
<body>
{style:header()}
<h1>Login</h1>
<p>Current User = {$user}</p>
<form action="login.xq" method="post">
<table class="login" cellpadding="5">
<tr>
<th colspan="2" align="left">Please Login</th>
</tr>
<tr>
<td align="left">Username:</td>
<td><input name="user" type="text" size="20"/></td>
</tr>
<tr>
<td align="left">Password:</td>
<td><input name="pass" type="password" size="20"/></td>
</tr>
</table>
<input name="return" type="hidden" value="{$return-uri}" size="20"/>
<input type="submit" value="Login"/>
</form>
<p>We will return to the following URI on success: {$return-uri}</p>
{style:footer()}
</body>
</html>
以下脚本从 HTTP POST 操作获取传入的登录数据并执行登录。HTTP POST 数据从以下键值对格式的request:get-data()格式到达
user=jdoe&pass=mypass&return=/exist/rest/db/xrx/apps/test/view/view-item.xq&id=47
请注意,我们假设user-id在第一个键值对中,pass在第二个键值对中。如果您想要更通用的接口,则可以在所有传入的表单键值对中扫描正确的键。
xquery version "1.0";
import module namespace style ='http://code.google.com/p/xrx/style' at '/db/xrx/modules/style.xqm';
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";
let $data := request:get-data()
let $tokens := tokenize($data, '&')
let $user := substring-after($tokens[1], 'user=')
let $pass := substring-after($tokens[2], 'pass=')
let $return := substring-after($tokens[3], 'return=')
(: this is required to fix the incomming URI encodings :)
let $unescape-uri := request:unescape-uri($return, 'UTF-8')
(: this is required to put the first parameter deliminator ? back in place of the ampersand :)
let $fix-first-param := replace($unescape-uri, 'xq&', 'xq?')
let $login-success-indicator := xmldb:login("/db", $user, $pass)
return
<html>
<head>
<title>Login Result</title>
{style:import-css()}
</head>
<body>
{style:header()}
{style:breadcrumb()}
<h1>Login Status</h1>
{if ($login-success-indicator)
then <p>Your Login Was Successful <a href="{$fix-first-param}">Return</a></p>
else <p>Login Failed <a href="login-form.xq">Try Again</a></p>
}
You are logged in to the root database collection as {xmldb:get-current-user()}
<br/>
<a href="logout.xq">Logout</a>
{style:footer()}
</body>
</html>
以下脚本将您注销系统。从技术上讲,它只是将您登录为具有只读访问权限的用户。
xquery version "1.0";
import module namespace style='http://mdr.crossflow.com/style' at '/db/crossflo/modules/style.xq';
declare option exist:serialize "method=xhtml media-type=text/html indent=yes";
let $null := xmldb:login("/db", "guest", "guest")
let $user := xmldb:get-current-user()
return
<html>
<head>
<title>Logout</title>
{style:import-css()}
{style:breadcrumb()}
</head>
<body>
{style:header()}
<h1>You have been logged out.</h1>
<p>User = {$user}</p>
<a href="login-form.xq">Login</a>
{style:footer()}
</body>
</html
现在我们将创建一个执行给定用户的身份验证检查的 XQuery 模块。
许多屏幕都有指向编辑表单的链接,允许用户更改数据。您可以通过将以下代码添加到通常具有编辑链接的每个页面来有条件地显示这些编辑链接
{if (auth:has-role(xmldb:get-current-user(), 'ba'))
then
<div>
<a href="../edit/edit.xq?id={$id}">Edit Item</a>
<a href="../edit/delete-confirm.xq?id={$id}">Delete Item</a>
</div>
else
<span>User {xmldb:get-current-user()} has no roles with edit privileges
<a href="{auth:login-uri()}">Login</a>
</span>
}
您还可以在具有敏感信息或功能的任何页面中添加一个函数,方法是检查用户是否已登录。这可以通过一个名为 auth:validate-user() 的函数完成。
request:get-session-attribute("user") - this function will return the user name associated with the login session
response:redirect-to($uri as xs:anyURI) - this function will redirect the user to another URI such as a login panel.
请注意,通常您希望让用户能够轻松地返回到他们所在的屏幕。为此,您通常希望在 login.xq XQuery 中添加一个参数,该参数将用户重定向回他们来自的地方。
response:redirect-to('/exist/rest/db/xrx/apps/user-manager/login.xq?return=/exist/rest/db/myapp/edit/edit.xq?id=47')
以下是一个示例函数,可以放置在所有需要用户身份验证的页面顶部
declare function auth:validate-user() as xs:string {
let $url:= request:get-url()
let $param-names := request:parameter-names()
let $param-string :=for $count in 1 to count($param-names)
let $param := $param-names[$count]
let $value := request:get-parameter($param, '')
let $return := if ($count=1) then (concat('?',$param,'=',$value)) else (concat('&',$param,'=',$value))
return
$return
let $return-param :=fn:string-join($param-string,'')
let $get-session-name :=request:get-session-attribute("user")
let $login-uri := '/exist/rest/db/xrx/apps/user-manager/login.xq?url='
return
if ($get-session-name) then
($get-session-name)
else(
response:redirect-to(xs:anyURI(concat($string,$url,fn:string-join($param-string,'')))))
};