用于根据角色控制表单输入的属性文件

时间:2015-02-07 23:12:14

标签: jsf jsf-2 resourcebundle user-roles

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

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

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

user-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=""提供正确的值{1}}的{​​1}}属性。

这可能是一个解决方案,但应用程序的输入不仅仅是一个书名和类别,意味着我将添加许多必需和呈现的属性,这将使JSF页面看起来很丑,其中包含大量的变量托管bean。

我的问题是否有更好的方法/框架/解决方案?

1 个答案:

答案 0 :(得分:1)

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

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

管理多个属性文件:

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

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

UserMessagesResourceBundle我们将实现允许我们在属性文件之间切换的逻辑(假设yourpackage.user-inputs是user-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()));
    }
} 

在属性文件之间切换:

为了从属性文件切换到另一个属性文件,我们需要使用我们刚刚在上面的类中声明的方法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");

NB:您只能在请求范围的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的JSTL函数的更多信息:

  

返回特定子字符串后面的字符串的子集。

     

示例:

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

函数substringBefore

  

返回特定子字符串之前的字符串子集。

     

示例:

  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'], ':')}"

另见: