星期六, 九月 27, 2008

beanshell Bsh.Console如何在jdk 1.6中使用

直接使用String.format(format,param)会出现错误。
必须使用如下语法,
String.format("%3.2f",new Object[]{(float)6/11});

就可以使用了

星期五, 九月 19, 2008

dhtmlXGrid 覆盖函数。

这是覆盖每个cell 动态的tooltip的提示的方法。

dhtmlXGridCellObject.prototype.getTitle=function()
{
ccolName=this.grid.getColumnId(this.cell._cellIndex);
rowID=this.cell.parentNode.idd;
result=this.cell.innerHTML;
if(ccolName=='RULE_NAME')
result=this.grid.cells(rowID,4).getValue();
return result;
}

修改每个行的颜色
mygrid.setRowColor(rowid,"yellow");
或者更加底层的,可以修改更多属性CSS

mygrid.rowsAr[idrow].style.backgroundColor="yellow"

星期四, 九月 18, 2008

dhtmlXGrid 1.3版本如何修改每个cell的帮助提示呢?Tooltip

经过dhtmlXGrid论坛发现了一个渐变的方法。

dhtmlXGridCellObject.prototype.getTitle=function()
{
return this.cell.innerHTML+"<h1>F1 HELP</H1><BR/><P>..."+this.cell._cellIndex+"|||"+this.grid.getColumnId(this.cell._cellIndex);
}

不过这个innerHTML只承认纯文本,所以HTML语法无法使用了,\nJavascript符号还是认识的。


星期六, 九月 13, 2008

JBOSS JAAS如何编写自己的身份(principal)?

详细的步骤在:http://wiki.jboss.org/wiki/UsingCustomPrincpalsWith
不过经过事件发现只需要重载两个方法就可以了。如果直接从org.jboss.security.auth.spi.DatabaseServerLoginModule扩展,这样可以利用JBOSS已经有的密码加密,编码等其他一些特性,不需要再重写。

类似如下:
public class StandardLoginModule extends DatabaseServerLoginModule
编写自己的定制身份类Principal,可以加入除了名字之外的其他属性.

public class userPropertiesPrincipal extends SimplePrincipal
{
//User's property
private String email;
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email=email;
}
//gender
private String gender;
public String getGender()
{
return gender;
}
public void setGender(String gender)
{
this.gender=gender;
}
public userPropertiesPrincipal(String name)
{
super(name);
}

}

必须重载的三个方法(红色)
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options)
   public boolean login() throws LoginException
{
}
   protected Principal getIdentity()
{
 可能需要重载的类.
protected Group[] getRoleSets() throws LoginException
{}

具体设置步骤:在login-config.xml里加入,通过
principalClass来制定自己定制的身份类.也注意加入了
userPropertiesQuery,这个需要在initialize方法中从Map options中取出来,如下:
super.initialize(subject, callbackHandler, sharedState, options);

userPropertiesQuery=(String ) options.get("userPropertiesQuery");
if(userPropertiesQuery==null)
userPropertiesQuery="select * from sysUser where userid=?";


<application-policy name = "seamAC">
<authentication>

<login-module code = "org.security.login.jass.StandardLoginModule"
flag = "required">

<module-option name = "dsJndiName">java:/SecMasterDS</module-option>
<module-option name = "principalsQuery">select password from sysUser where userid=? </module-option>
<module-option name = "userPropertiesQuery">select * from sysUser where userid=? </module-option>
<module-option name = "rolesQuery">SELECT A.roleID as Role,'Roles' FROM sysRoles A,UserRoles B WHERE A.roleID=B.roleID AND B.userId=?</module-option>
<module-option name = "debug">true</module-option>
<module-option name="principalClass">org.security.login.jass.userPropertiesPrincipal</module-option>
<!--Password is md5 encrypt-->
<module-option name="hashAlgorithm">MD5</module-option>
<module-option name ="hashEncoding">HEX</module-option>
</login-module>
</authentication>
</application-policy>

Web.xml中必须指明使用使用JAAS Security Manage RealM

<Realm className="org.jboss.web.tomcat.security.JBossSecurityMgrRealm"
certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping"
allRolesMode="authOnly"
debug="99"
/>

星期四, 九月 11, 2008

Jboss如何获取用户验证后的角色?

通过JAAS验证的用户,要想得到验证的用户的所有的角色,JBOSS目前推荐的是如下代码段
主要是基于新的JACC规范:
Java Authorization Contract for Containers (Java ACC) specification (JSR-115)

import javax.security.auth.Subject ;
import javax.security.jacc.PolicyContext;

private String findRole() throws Exception
{
String result="";

// Get the Authenticated Subject
Subject subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");

// Now look for a Group called Roles
Set principals = subject.getPrincipals(Principal.class);
Iterator iter = principals.iterator();
while(iter.hasNext())
{
Principal p = (Principal)iter.next();
if(p instanceof SimpleGroup)
{
SimpleGroup sg = (SimpleGroup)p;
if("Roles".equals(sg.getName()))
{
Enumeration en = sg.members();
while(en.hasMoreElements())
{
String role = en.nextElement().toString();
result=result+","+role;
System.out.println("Role:"+role);
}
}
}
}
return result;
}

JAASRealm和Jboss(CMA) Container Manager Authentication

这是为了结合JAAS和CMA两者的有点提出的接口。
在http://www.jboss.org/file-access/default/members/jbossweb/freezone/docs/2.1.0/realm-howto.html#JAASRealm,描述了相关配置
web.xml

<Realm className="org.jboss.web.tomcat.security.JBossSecurityMgrRealm"
certificatePrincipal="org.jboss.security.auth.certs.SubjectDNMapping"
allRolesMode="authOnly"
debug="99"
/>


其他配置同JAAS标准配置一样,需要在web.xml,login-config.xml和LoginModule中调用同一个appName

但是注意,在这个LoginModule你必须自己保存用户的验证信息。
It is the responsibility of your login module to create and save User and Role objects representing Principals for the user

下列代码就是Jboss实现的:org.jboss.web.tomcat.security.login.WebAuthentication


/*
* JBoss, Home of Professional Open Source
* Copyright 2007, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.web.tomcat.security.login;

import java.security.Principal;
import java.security.cert.X509Certificate;

import javax.naming.NamingException;

import org.apache.catalina.Session;
import org.apache.catalina.authenticator.Constants;
import org.apache.catalina.connector.Request;
import org.jboss.web.tomcat.security.SecurityAssociationValve;

//$Id$

/**
* JBAS-4077: Programmatic Web Login
* @author Anil.Saldhana@redhat.com
* @since Mar 12, 2007
* @version $Revision$
*/
public class WebAuthentication
{
private static final String AUTH_TYPE = "PROGRAMMATIC_WEB_LOGIN";
public WebAuthentication()
{
}

/**
* Login an user via the CLIENT-CERT method
* @param certs X509 certificates
* @return Authenticated User Principal
*/
public boolean login(X509Certificate[] certs)
{
//Get the active request
Request request = (Request) SecurityAssociationValve.activeRequest.get();
if(request == null)
throw new IllegalStateException("request is null");
Principal p = request.getContext().getRealm().authenticate(certs);
if(p != null)
{
register(request,p, null, null);
}
return p!= null;
}

/**
* Login an user via the BASIC, FORM, DIGEST methods
* @param username
* @param credential
* @return
* @throws NamingException
*/
public boolean login(String username, Object credential)
{
//Get the active request
Request request = (Request) SecurityAssociationValve.activeRequest.get();
if(request == null)
throw new IllegalStateException("request is null");

Principal p = null;
if(credential instanceof String)
{
p = request.getContext().getRealm().authenticate(username, (String)credential);
}
else if (credential instanceof byte[])
{
p = request.getContext().getRealm().authenticate(username, (byte[])credential);
}
if(p != null)
{
register(request,p, username, credential);
}
return p != null;
}

/**
* Log the user out
*
*/
public void logout()
{
//Get the active request
Request request = (Request) SecurityAssociationValve.activeRequest.get();
if(request == null)
throw new IllegalStateException("request is null");
unregister(request);
}

/**
* Register the principal with the request, session etc just the way AuthenticatorBase does
* @param request Catalina Request
* @param principal User Principal generated via authentication
* @param username username passed by the user (null for client-cert)
* @param credential Password (null for client-cert and digest)
*/
protected void register(Request request, Principal principal, String username, Object password)
{
request.setAuthType(AUTH_TYPE);
request.setUserPrincipal(principal);

//Cache the authentication principal in the session
Session session = request.getSessionInternal(false);
if(session != null)
{
session.setAuthType(AUTH_TYPE);
session.setPrincipal(principal);
if (username != null)
session.setNote(Constants.SESS_USERNAME_NOTE, username);
else
session.removeNote(Constants.SESS_USERNAME_NOTE);
if (password != null)
session.setNote(Constants.SESS_PASSWORD_NOTE, getPasswordAsString(password));
else
session.removeNote(Constants.SESS_PASSWORD_NOTE);
}
}

/**
* Log the user out
* @param request
*/
protected void unregister(Request request)
{
request.setAuthType(null);
request.setUserPrincipal(null);

//Cache the authentication principal in the session
Session session = request.getSessionInternal(false);
if(session != null)
{
session.setAuthType(null);
session.setPrincipal(null);
session.removeNote(Constants.SESS_USERNAME_NOTE);
session.removeNote(Constants.SESS_PASSWORD_NOTE);
}
}

private String getPasswordAsString(Object cred)
{
String p = null;

if(cred instanceof String)
{
p = (String)cred;
}
else if(cred instanceof byte[])
{
p = new String((byte[])cred);
}
return p;
}
}

星期三, 九月 10, 2008

JBOSS JAAS和tomcat j_security_check CMS关系(之二)

Jboss 的 WebAuthentication的login方法到底做了什么工作?将JAAS和tomcat container链接了起来?
这个要查看代码:

http://www.javakey.net/source/jboss/4.x/org/jboss/web/tomcat/security/login/WebAuthentication.java.html

从这里可以看到,如何从tomcat中获取当前激活的
 org.apache.catalina.connector.Request(http://www.docjar.com/html/api/org/apache/catalina/connector/Request.java.html)
的后门方法

Request request = (Request) SecurityAssociationValve.activeRequest.get();

这个方式是非常cool的
.这里实际调用了Request上下文的RealM的
authenticate多元方法.
这个方法是在tomcat web.xml里通过来定义的。
比如:jboss 4.2.3GA. server\default\deploy\jboss-web.deployer\server.xml
里定义:
<realm classname="org.jboss.web.tomcat.security.JBossSecurityMgrRealm" certificateprincipal="org.jboss.security.auth.certs.SubjectDNMapping" allrolesmode="authOnly">

Jboss相关类源代码:http://www.javakey.net/source/jboss/4.x/allClasses.html
整个工作流程:
JAASRealm authenticates the user and creates a GenericPrincipal with
userPrincipal set to some principal returned by LoginModule.

Later RealmBase.hasResourcePermission() calls request.getUserPrincipal()
to recover authenticated user principal

Request.getUserPrincipal() checks if the principal is instanceof
GenericPrincipal, and if it is, it returns its userPrincipal.

RealmBase.hasRole() checks if the principal is instanceof GenericPrincipal
and if not it fails immediately.

Note: previous versions of JAASRealm had their own hasRole() implementation.

Note: request.isUserInRole() is not getting userPrincipal from
GenericPrincipal when calling realm.hasRole() and this one seems to

work.


星期六, 九月 06, 2008

JBOSS JAAS和tomcat j_security_check CMS关系

JAAS是JAVA强大的安全框架模型,JBOSS 对其的支持也是完全和强劲的。

但JAAS总体来说是基于组件的AAA体系(Authentication(验证) Authorization(授权) Accounting(审计)).

Web应用中,往往是基于角色的安全控制(RBAC Role-Based Access Control)比较多,往往授权关系是树形的和矩阵性的复杂关系。

tomcat实现了一个容器类的范围内的验证系统,也就是所谓的CMS(Container Managed Security),这种方式的好处是比较符合要么全有,

要么全无的安全认证,粒度不如JAAS控制的细致。web.xml通过

<security-constraint>
  <web-resource-collection>

<auth-constraint>

三个标志来控制不同角色访问的内容范围.

JBOSS中支持这两种验证方式,但是就出现了一个比较严重的问题。

两种方式是独立的,有各自的LoginContext上下文,必须通过程序的方式在JAAS模块中对Container来进行验证。JBOSS 4.2.3通过class:

org.jboss.web.tomcat.security.login.WebAuthentication;

来实现了这两者对接。综合了两种方式的有点。

另外一种常见的技术是用login proxy的方式来手工调用jsp让内置的j_security_check起作用来综合JAAS和CMS的有点,这里也加入简单说明

在servlet或JSF的backing bean的action对应的方法中,加入如下语句:
String url="/login.jsp"

        RequestDispatcher  dispatcher = request.getRequestDispatcher(url);
        dispatcher.forward(request, response);
login.jsp包含如下内容:

<html>
    <head>
      <meta
http-equiv="Content-Type"
content="text/html; charset=windows-1252"/>
<title>Logging in</title>
    </head>
      <body
onload="document.forms[0].submit()">
      <form
method="post" action="j_security_check">
<input type="hidden" name="j_username"
value="${param.j_username}"/>
<input type="hidden" name="j_password"
value="${param.j_password}"/>
      </form>
    </body>
  </html>

上面的j_username和j_password都是从上页用户输入后传递过来的

上述的方式比较简陋,所以下面就是程序完全控制的方式。

以一个JSF的完全应用为例子

首先编写:login.jsp

<?xml version="1.0" encoding="UTF-8"?>
<jsp:root version="2.0"
xmlns:jsp="http://java.sun.com/JSP/Page"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
>
<jsp:directive.page  contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" />
<html>
    <head>
    <meta http-equiv="Content-Type"
            content="text/html; charset=UTF-8"></meta>
        <title>Login Wizard</title>
    </head>
    <body>

<f:view >
        <h:form >
            <h:outputLabel  value="#{SysRes.username}"/><h:inputText value="#{loginManager.userID}" id="j_username" /><br/>
            <h:outputLabel value="#{SysRes.password}"/><h:inputSecret value="#{loginManager.password}" id="j_password" /><br/>
            <h:commandButton id="checkout" value="${SysRes.OK}" action="#{loginManager.loginAction}" />   
        </h:form>
</f:view>
    </body>   
</html> 
</jsp:root>

 

其次配置faces-config.xml这个是默认的配置文件,可以通过在web.xml中定义改变之,后面举例

<managed-bean>
<managed-bean-name>loginManager</managed-bean-name>
<managed-bean-class>org.security.login.LoginManager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>

 

三:再看managed-bean,代码如下

package org.security.login;

import java.security.Principal;
import java.util.Set;

import javax.faces.application.Application;
import javax.faces.application.NavigationHandler;
import javax.faces.context.FacesContext;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import javax.security.auth.login.LoginContext;
import javax.security.auth.callback.CallbackHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import javax.faces.event.ActionEvent;

import javax.security.auth.Subject ;
import javax.servlet.RequestDispatcher;

import org.jboss.security.SimplePrincipal;
import org.jboss.web.tomcat.security.login.WebAuthentication;
import org.security.login.jass.*;

public class LoginManager
{
       private String _uid = "";
        private String _pwd = "";
         private String requestURI="";
        public String getUserID() { return _uid; }
        public void setUserID(String uid) { _uid = uid; }
        public String getPassword() { return _pwd; }
        public void setPassword(String pwd) { _pwd = pwd; }
        public String loginAction() throws Exception
        {
            FacesContext context = FacesContext.getCurrentInstance();
            HttpServletResponse response =
            (HttpServletResponse) context.getExternalContext().getResponse();
            HttpServletRequest request =
                (HttpServletRequest) context.getExternalContext().getRequest();           
        String securityDomain = "seamAC";//安全区域,在logic-config.xml中定义
        CallbackHandler callbackHandler = new StandardCallbackHandler(getUserID(),getPassword());
        try
        {
            LoginContext lc =
            new LoginContext( securityDomain,
                      callbackHandler );
            lc.login();
            WebAuthentication pwl = new WebAuthentication();
            boolean flag=pwl.login(getUserID(),getPassword());
            System.out.println("WebAuthentication is?"+flag);
            System.out.println("User Principal="+request.getUserPrincipal());
        }
        catch ( Exception e )
        {
            e.printStackTrace();
//            FacesContext context = FacesContext.getCurrentInstance();
//            HttpServletResponse response =
//            (HttpServletResponse) context.getExternalContext().getResponse();
//            response.sendError(100);
//            context.responseComplete();
           // FacesContext.getCurrentInstance().addMessage("checkout",new FacesMessage(e.getMessage()));
//            throw e;
            return "loginFail";
        }
        String url=request.getHeader("referer");
        String cUrl=request.getRequestURI();
        //再次获取
        request =(HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();       
        System.out.println("referer:"+url+" curl:"+cUrl);
        System.out.println("用户角色sysAdmin:"+request.isUserInRole("sysAdmin"));
        System.out.println("用户角色guest:"+request.isUserInRole("guest"));

//        Principal principal = new SimplePrincipal(getUserID());
//        ObjectName jaasMgr = new ObjectName("jboss.security:service=JaasSecurityManager");
//        Object[] params = { securityDomain, principal };
//        String[] signature = { "java.lang.String", Principal.class.getName() };
//        MBeanServer server = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
//        server.invoke(jaasMgr, "flushAuthenticationCache", params, signature);
        if(!(url==null ||url.trim().equals("")))
        {   
            String host=request.getHeader("host");
            String cpath=request.getContextPath();
            url=url.replace("http://"+host+cpath,"");
            System.out.println("url now:"+url+" cpath:"+cpath);           
                response.sendRedirect(cpath+"/"+url);   
        //use j_security_check proxy tech,j_username,j_password
//        RequestDispatcher  dispatcher = request.getRequestDispatcher(url);
//        dispatcher.forward(request, response);
        }       
        return "loginPass";   
        }
        public void logout(){
            FacesContext fc = javax.faces.context.FacesContext.getCurrentInstance();
            ((HttpSession)fc.getExternalContext().getSession(false)).invalidate();
            Application ap = fc.getApplication();
            NavigationHandler nh = ap.getNavigationHandler();
            nh.handleNavigation(fc,null,"logout");
        }

}

 

四:配置web.xml 加入受保护的资源

<!-- 安全部分的配置 -->
<security-constraint>
  <web-resource-collection>
   <web-resource-name>Secure Pages</web-resource-name>
   <url-pattern>/Genesis/*</url-pattern>
   <url-pattern>/Guest/*</url-pattern>  
   <http-method>GET</http-method>
   <http-method>POST</http-method>
   <!-- http-method>HEAD</http-method>
   <http-method>PUT</http-method>
   <http-method>OPTIONS</http-method>
   <http-method>TRACE</http-method>
   <http-method>DELETE</http-method -->
  </web-resource-collection>
<!-- only Admin role can access -->
  <auth-constraint>
<role-name>sysAdmin</role-name>
  </auth-constraint>

</security-constraint>

<!-- 登录验证方式 -->
<login-config>
  <auth-method>FORM</auth-method>
  <realm-name>seamAC</realm-name>
  <form-login-config>
   <form-login-page>/Login.jsp</form-login-page>
   <form-error-page>/Login.jsp</form-error-page>
  </form-login-config>
</login-config>
<!-- Declare all Role List -->
<security-role>
  <role-name>sysAdmin</role-name>
</security-role>
  <security-role>
  <role-name>sysGuest</role-name> 
</security-role>

五:配置jboss相关部分.使用Jboss 4.2.3 GA JDK1.6 update 10
   JAAS部分:conf/login-config.xml,注意seamAC就是LoginManager class中的login方法中引用的,Realm名字.

<!--JAAS  -->
    <application-policy name = "seamAC">
       <authentication>
          <login-module code = "org.jboss.security.auth.spi.DatabaseServerLoginModule"
             flag = "required">
             <module-option name = "unauthenticatedIdentity">guest</module-option>
             <module-option name = "dsJndiName">java:/SecMasterDS</module-option>
             <module-option name = "principalsQuery">select password from sysUser where userid=? </module-option>
             <module-option name = "rolesQuery">SELECT A.roleID as Role,'Roles' FROM sysRoles A,UserRoles B WHERE A.roleID=B.roleID AND B.userId=?</module-option>
             <module-option name = "debug">true</module-option>
<!--Password is md5 encrypt-->
                        <module-option name="hashAlgorithm">MD5</module-option>
            <module-option name ="hashEncoding">HEX</module-option>
          </login-module>
       </authentication>
    </application-policy>

注意:rolesQuery部分,根据Jboss的class org/jboss/security/auth/spi/DatabaseServerLoginModule.java中有关角色部分的说明,必须有一个roleGroup的字段值为Roles,否则出现用户没有角色的错误,小心注意,这是很愚蠢的假定,往往配置了很久,不能工作,就是这些小地方。

  141   /** Overriden by subclasses to return the Groups that correspond to the
142   to the role sets assigned to the user. Subclasses should create at
143   least a Group named "Roles" that contains the roles assigned to the user.
144   A second common group is "CallerPrincipal" that provides the application
145   identity of the user rather than the security domain identity.
146   @return Group[] containing the sets of roles
147   */

数据库部分:deploy/mysql-ds.xml(使用MYSQL 5.1),注意SecMasterDS



<local-tx-datasource>
   <jndi-name>SecMasterDS</jndi-name>
   <connection-url>jdbc:mysql://localhost:3306/gate?useUnicode=TRUE</connection-url>
   <driver-class>com.mysql.jdbc.Driver</driver-class>
   <user-name>root</user-name>
   <password></password>
   <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
   <metadata>
      <type-mapping>mySQL</type-mapping>
   </metadata>
</local-tx-datasource>

 


支持,繁琐的配置终于可以工作了,访问一个目录,然后经过验证,显示出来内容,实在是非常惬意的事情。更加人性化的界面,比如不具有权限的人向访问一个特定角色的资源,可以在web.xml中截获403错误来做到:



< error-page>
  <error-code>403</error-code>
  <location>/roleerror.jsp</location>
</error-page>

JBOSS JAAS 同JSF 相关问题

如果出现验证后出现,isUserInRole返回false的情况
首先打开
conf/jboss-log4j.xml 的debug.


http://wiki.jboss.org/wiki/SecurityFAQ

<!--Debug log login sql -->
<category name="org.jboss.security">
<priority value="TRACE" class="org.jboss.logging.XLevel"/>
</category>
<category name="org.jboss.web.tomcat.security">
<priority value="TRACE" class="org.jboss.logging.XLevel"/>
</category>
<category name="org.apache.catalina">
<priority value="DEBUG"/>
</category>

星期五, 九月 05, 2008

JSF 或JSP EL(Express Language)中的变量

在JSF和JSP中,EL引用一个变量,语法如下#{class.fieldName}或${class.fieldName}
这里fieldName必须是首字母小写的,但对应的class的方法名为getFieldName()
这种奇怪的语法是来自类 java.beans.PropertyDescriptor 的构造函数规定的
PropertyDescriptor

public PropertyDescriptor(String propertyName,
Class beanClass) throws IntrospectionException

为某特性构造 PropertyDescriptor ,它通过拥有 getFoo 和 setFoo 访问者来遵循标准 Java 约定。因此若参数名是“fred”,则假定读程序方法是“getFred”并且写程序方法是“setFred”。注意特性名应以小写字符开始, 它将在方法名中被大写化。

Note that the property name should start with a lower case character, which will be capitalized in the method names.