基于角色控制表单输入的属性文件

Properties files to control form inputs based on roles

我正在构建的应用程序要求用户角色是动态的,它们将存储在数据库中,并且它们还将映射到应用程序的功能(表单),也存储在数据库。

限制角色访问特定页面并不困难,但要求还规定表单输入必须根据角色自定义,这意味着输入可以是强制性的或不可见的,也可以是可见的或不可见的,阅读-仅或不基于角色。

我控制这些限制的方法是基于为每个角色创建一个 属性 文件,该文件将存储应用程序中所有表单的所有输入,作为键,以及一个长字符串作为值我们定义输入的状态,如下所示:

用户-inputs.properties

# form.input=mandatory:visibility

searchBooks.bookName=true:true
searchBooks.bookCategory=false:true
searchBooks.authorName=false:false

admin-inputs.properties

searchBooks.bookName=true:true
searchBooks.bookCategory=false:true
searchBooks.authorName=false:true

然后执行一些神奇的 Java 代码,每当访问表单时,从特定用户角色的文件中读取其输入属性,并解析这些值,以便我可以为 rendered=""required="" 属性 <h:inputText/>.

这可能是一个解决方案,但应用程序的输入不仅仅是书名和类别,这意味着我将放置大量必需的和呈现的属性,这将使 JSF 页面看起来很难看,因为有大量变量在托管 bean 中。

我的问题有更好的approach/framework/solution吗?

我认为你的方法是正确的,我将继续使用你的方法,包括创建多个 属性 文件,每个用户一个,除了我们不会使用任何 "大量的变量 在托管 bean 中.

因此,第一步包括使用单个资源包前缀(<resource-bundle> 中的 <var></var>)管理多个资源属性,在第二步中我们将了解如何在这些文件之间切换,在最后一步中,我们将使用 JSTL 从 属性 文件中读取。

管理多个 属性 个文件:

我们首先在 faces-config 文件中定义我们的 ResourceBundle:

<application>
      <resource-bundle>
         <base-name>UserMessages</base-name>
         <var>msgs</var>
      </resource-bundle>
</application>

UserMessages 是一个 ResourceBundle,我们将在其中实现允许我们在 属性 文件之间切换的逻辑(假设 yourpackage.user-inputs 是完全限定的您的用户名-inputs.properties):

import java.util.Enumeration;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.context.FacesContext;

public class UserMessages extends ResourceBundle {

    public UserMessages() {
        // we are loading user-inputs.properties as the default properties file
        setParent(getBundle("yourpackage.user-inputs", FacesContext.getCurrentInstance()
                .getViewRoot().getLocale()));
    }

    @Override
    protected Object handleGetObject(String key) {
        // we could just return parent.getObject(key) but we want to respect JSF recommandations
        try {
            return parent.getObject(key);
        } catch (MissingResourceException e) {
            return "???" + key + "???";
        }
    }

    @Override
    public Enumeration<String> getKeys() {

        return parent.getKeys();
    }

    // this is the method that will allow us to switch between our .properties
    public void setResourceBundle(String basename) {
        setParent(getBundle(basename, FacesContext.getCurrentInstance()
                .getViewRoot().getLocale()));
    }
} 

正在 属性 个文件之间切换:

为了从一个 属性 文件切换到另一个文件,我们需要使用我们刚刚在上面的 class 中声明的方法 setResourceBundle(String basename),所以在托管 bean 中您正在声明您的业务逻辑以及您打算根据用户角色切换文件的位置,您需要注入捆绑包,例如:

//don't forget adding getters and setters or you end with NullPointerException
@ManagedProperty("#{msgs}")
private UserMessages userMesssages;

然后,要切换到另一个文件(admin-inputs.properties),就这样使用:

//yourpackage.admin-inputs is the fully qualified name
userMesssages.setResourceBundle("yourpackage.admin-inputs");

注意: 您只能在请求范围的 bean 中以这种方式(如上)注入捆绑包,要在更广泛的范围内使用它,请参阅:Read i18n variables from properties file in a Bean

现在,我们可以轻松地从 user-inputs 切换到 admin-inputs,最后一步是最简单的。

正在解析 属性 文件:

坏消息是,当使用这种方法时,您需要将 rendered=""required="" 属性添加到您愿意管理的每个输入(但不要忘记好的是您不需要在托管 bean 中管理变量 ;))。

首先,您需要在 xhtml 文件的顶部添加 JSTL 命名空间声明:

xmlns:fn="http://java.sun.com/jsp/jstl/functions"

您可以在 javadocs 中找到有关函数 substringAfter:

的更多信息

Returns a subset of a string following a specific substring.

Example:

  P.O. Box: ${fn:substringAfter(zip, "-")}

函数substringBefore:

Returns a subset of a string before a specific substring.

Example:

  Zip (without P.O. Box): ${fn:substringBefore(zip, "-")}

其次,作为 String 的第一部分代表 required 属性:

//Returns the substring of msgs['searchBooks.authorName'] before the first occurrence of the separator ':'    
required="${fn:substringBefore(msgs['searchBooks.authorName'], ':')}"

和第二部分:

//Returns the substring of msgs['searchBooks.authorName'] after the first occurrence of the separator ':'.
rendered="${fn:substringAfter(msgs['searchBooks.authorName'], ':')}"

另请参阅: