Part 4 Sinking the
business requirements
Sinking 业务需求
Seam goes well beyond being simply a web application framework. It gives
you support to cover all the business requirements, which you’ll learn to appreciate
in this part of the book.
SEAM所做的不只是简化WEB应用框架。它的支持可满足所有的业务需要,在这个部分你就可以了解到。
Chapter 11 shows how quickly you can weave security into a Seam application.
A single method on a POJO gives you both authentication and role-based
authorization. You use annotations and the EL to define restrictions. Going
deeper, you’ll get a crash course in the Drools rule engine and use it to create
fine-grained, contextual restrictions. Finally, you’ll learn to keep out pesky spammers
and bots using CAPTCHA, a nearly zero-effort integration.
第11章讲述如何快速地向SEAM应用中织入安全性。一个POJO上的单独的方法可让你认证或基于角色的认证。你使用annotations和EL来定义restrictions。再进一步,你会在Drools rule引擎得到一个crash球场,使用它建立一个细粒度的上下文相关的restrictions.最后,你将学到如何使用CAPTCHA(一个轻松的集成)避开麻烦的spammers及bots。
Though critical, security can often be a dry topic. But everyone loves Ajax!
Chapter 12 highlights the two flavors of Ajax in a Seam application. First you’ll
study Ajax-enabled UI components, which honor the JSF life cycle and let you
avoid the JavaScript and CSS nightmares that typically come with adopting Ajax.
If you prefer the low-level control, the JavaScript Remoting library lets you interact
directly with server-side components from JavaScript, stepping outside of the
JSF life cycle. The latter approach opens the door to alternative front ends such
as GWT.
虽然饱受批评,安全经常是个枯燥的话题。但所有人都爱Ajax!第12章强调了SEAM应用中Ajax的两个偏爱。第一,你将学习引用了Ajax的UI部件,其遵循JSF生命周期,而让你避开JavaScript and CSS带来的Ajax恶梦。如果你更想低水平的控制,JavaScript Remoting让你直接与服务器端部件交互,超出了JSF生命周期。后面的方法为前端替代品如GWT打开了大门。
If this book is a full-course meal, then chapter 13 is definitely dessert. This is
the chapter where you’ll learn to make your application pop. It begins by showing
how to accept file uploads using a JSF input binding. You’ll then discover the
versatility of Facelets templates to create and serve PDF documents, compose
and send email messages with attachments, and produce RSS. Finally, you’ll
learn to use themes and i18n to customize the application.
如果本书是套餐,那么第13章就是甜点。本章能让你的应用流行开来。它以使用JSF录入绑定来接受文件上传开始。随后你会发现各式的Facelets模板来建立和处理PDF文档,编辑并发送带有附件的email信息,产生RSS。最后,你会学到使用主题和i18n来自定义应用。
That’s where the book ends and the online chapters pick up. Chapter 14 eases you
into Seam’s business process integration and shows that a business process is simply a
multiuser conversation, controlled using the same declarative approach as a singleuser
conversation. Chapter 15 reveals how Seam taps into the Spring container. The
Spring integration is vital because it allows Spring to leverage Seam’s proper management
of the persistence context.
在这个地方,本书结束,在线章节开始。第14章让你轻松进入SEAM业务过程集成并显示业务过程只是简单的多用户会话,控制使用相同的声明式方法为单个会话。第15章揭示SEAM如何加入到Spring容器。Spring集成很重要,因为它允许Spring使用SEAM恰当地管理存储上下文。
The underlying theme of this final part is that Seam’s programming model
remains consistent, regardless of which integration you use, making the technologies
accessible.
这最后部分的底层主题是SEAM的程序模型的一致性,不管你用了哪个集成,要保证技术的的可用。
11、Securing Seam applications
This chapter covers
■ Developing an authentication routine
■ Enforcing role-based authorization
■ Writing permission rules with Drools
■ Adding a CAPTCHA challenge to a form
SEAM应用的安全性
本章包括:
开发一个认证路线
使用基于角色的认证
用Drools写许可规则
向表单增加CAPTCHA挑战
[@more@]
11、Securing Seam applications
This chapter covers
■ Developing an authentication routine
■ Enforcing role-based authorization
■ Writing permission rules with Drools
■ Adding a CAPTCHA challenge to a form
SEAM应用的安全性
本章包括:
开发一个认证路线
使用基于角色的认证
用Drools写许可规则
向表单增加CAPTCHA挑战
While winding down after a round of golf, I came across a magazine ad for Microsoft
Visual Studio 2005 that serves as an example of how not to treat security. The ad
shows side-by-side shots of a software development scene in which two developers
are discussing a web application, one before the product is introduced and one
after. The developer paraphernalia and the to-do list on the whiteboard reflect the
state of the project, with the before scene being far more cluttered and laden with
stress. But the contrast reveals a critical oversight in the after scene. An outstanding
item on the to-do list reads “TEST CODE FOR SECURITY!!” The items crossed off are
personalization features, consistency review of UI, accessibility, and breadcrumbs.
At least the application will look pretty while it’s being hacked.
在一个高尔夫回合的尾声,我看见一个Microsoft Visual Studio 2005杂志广告,示范如何可以不用关心安全。旁边是两个开发人员在讨论WEB应用。
Just because this chapter is in the final part of this book doesn’t mean you should
wait until the last minute to implement or test for security. A common misconception
is that security can be tacked onto the application when it’s ready to be sent off to QA,
or worse, production, as if it’s just polish. A complex application can’t be made secure
after the fact. Security must be present from the beginning and weaved into every
layer of the application, from the view down to the database. That’s why security is
such an integral part of Seam.
不要因为这一章在本书的末尾就认为你可以直到最后一分钟再实现和测试安全。一个常见的误解是安全可以在产品要发布时再加进去。安全必须在一开始时就考虑,并织入应用的每一层,从view到数据库。这就是为什么安全是SEAM的一个集成部分。
Seam helps you secure your application without spending a lot of time on the
details. In this chapter, you’ll learn how to implement an authentication routine and
how to protect areas of the application from unauthorized access across all layers. The
foundation of Seam’s security model is a role-based system that ensures a quick start.
Obviously, some applications require a more granular approach. To meet these needs,
Seam builds on this foundation by leveraging the Drools rule engine to support contextual,
rule-based permissions. The possibilities opened up by a rules system are limitless.
Seam 2.1 introduces an identity and permissions management module to make
administering security even easier. The best part of Seam’s security model is that you
don’t have to stomach a single line of XML, a breath of fresh air for those who have
used other Java security frameworks.
SEAM帮助你解决安全问题而不用你在细节上花太多时间。在这一章,你会学到如何实现一个认证路线,怎么保护应用的区域以防止各层的非认证的访问。SEAM的安全模型的基础是基于角色的系统。明显地,一些应用需要更细粒度的方法。为了满足这一需要,SEAM在此基础上使用Drools rule规则引擎来支持上下文的,基于角色的许可。规则系统带来的可能性是无限的。Seam 2.1引入了一个许可管理模块来安全的管理更轻松。SEAM的安全模块的最好的地方是不用加一行XML,这对那些已使用了其它JAVA安全框架的人来说是一丝新鲜空气。
Seeing is believing, so I want to start by showing you the quickest way to secure your
application after first defining some basic security terminology and how a user’s identity
is represented. As the chapter progresses, the security becomes more sophisticated.
眼见为实,在首先定义一些基本安全术语及用户的一致必怎样被体现后,让我们看看一种最快的方式来保卫你的应用。
11.1 Authentication jump-start
Why do you think security often gets the back seat in the design and development process?
My reasoning is that security frameworks, such as the Java Authentication and
Authorization Service (JAAS) and Spring Security (formally Acegi), are just too hard
to implement. The first is too cryptic and, let’s face it, primitive, and the second buries
you deep in XML Hell.
开始了解认证
为什么你经常认为安全应处于次要位置?我的理由是安全框架,如Java Authentication and Authorization Service (JAAS)及Spring Security (formally Acegi)太难实现。第一个太神秘,说实在的是太原始;第二个由将你埋入XML深渊。
The goal of a security layer is to prevent hackers, nonprivileged users, and rogue
client endpoints from accessing sensitive areas of the applicaion, and not scare off
developers from implementing it. Given its importance, security should be easy to
configure and use, and it should be integrated into the core of the application framework
rather than split off as an extension. Both are true in Seam. What’s more important
is that this ease of use is accomplished without compromising the ability of
Seam’s security model to scale in accordance with security requirements. I start by presenting
authentication, the foundation of security, and show you how to tie it into a
Seam application in three simple steps.
安全层的目标是防止黑客,未授权用户及坏客户去访问应用的敏感数据区域,又不会困难得吓跑开发者。不单重要,还要容易配置和使用,并且要集成到应用框架的核心而不是分离成扩展的结构。这两个对于SEAM来说都是真的。更重要的是这种使用并不需要组织SEAM安全模型以适应安全需求。我以认证开始,安全的基础,展示如果用三步绑定到一个SEAM应用。
11.1.1 Giving the user an identity
Authentication is just a fancy way of saying “login.” The login routine, which you are
about to implement, prefaces nearly every action we perform in today’s online world.
But it’s not just about making you ransack the piles of papers on your desk to find the
scribbled characters that get you past the login challenge. Authentication is about
giving an anonymous user a face, as depicted in figure 11.1.
给用户一个标识
认证只是“登录”的一个漂亮说法。你即将实现的登录路线,几乎是当今在线世界的每个action都不可少的开端。认证只是给匿名用户一张脸,如图11.1:
SHOW ME YOUR FACE
The transformation in figure 11.1 symbolizes the user establishing an identity.
During authentication, Seam assembles an instance of Subject from the JAAS API and associates it with the user’s session,
allowing the server to recognize the user until the session expires or the user explicitly logs out.
让我看一下你的脸
图中的变形表示建立了用户的标识。在认证过程中,SEAM从JAAS API组装了Subject实例,并将其关联到用户session,让服务器认识用户直到session过期或用户明确地登出。
The Subject instance is a digital representation of the user. It consists of a collection
of principals, filling in the user’s facial features. A principal implements the
Principal interface from the JAAS API. There can be an unbounded set of principals,
but Seam limits it to just two. The first principal holds the username of the authenticated
user and is called the user principal. The second principal, known as the roles
group, implements the Group interface and holds a collection of roles. Each role is a
Principal as well, though distinct from the user principal and roles group and appropriately
referred to as a role principal.
Subject实例是用户的数字表示。它组成了规则的集合,填入用户的外表特性。一个规则实现了JAAS API的Principal接口。可以有无数类型的规则集合,但SEAM将其限制为两个。第一个规则保持授权用户的用户名,被叫做用户规则。第二个规则,叫做角色组,实现组接口,保存角色的集合。每个角色也是一个Principal,即然从用户规则中区分出,角色组被称作角色特性。
But the details about the structure of the Subject don’t really matter because Seam
provides a simple abstraction layer that you use to establish a user’s principals during
authentication and to access those principals when performing authorization checks.
但Subject结构的细节是无关紧要的,因为SEAM提供了一个简单的抽象层,在认证时你可以用来建立用户的规则,当做认证检查时,可访问这些规则。
JAAS A LA CARTE
Seam uses JAAS, but only select parts of it. Don’t panic when you see that four-letter
word because, by and large, Seam’s JAAS integration is completely transparent to you.
Behind the scenes, Seam relies on JAAS to handle the authentication handoff, which
delegates to one of your components to give the approval, and also uses the identity
portion of the API as mentioned above. The only time you even come in contact with
JAAS is to assert a user’s role using Servlet security (e.g., isUserInRole()). Seam
ignores the permission and policy piece of JAAS, instead offering its own multifaceted
authorization strategy. We cover authorization after we finish with authentication.
Let’s take it one A at a time.
JAAS A LA CARTE
SEAM使用JAAS,但只使用其中一部分。在看到四个字符时,不要怕,因为SEAM的JAAS集成对你是完全透明的。在后台,SEAM依靠JAAS来处理认证,这表示你的部件之一会投用,或也会用到上面提到的API的标识部分。你唯一你会接触JAAS的时候是声称用户的角色是使用Servlet security(如isUserInRole())。SEAM忽略许可 和JAAS的策略部分,而是提供了自己的授权策略。我们会在认证后讲解。让我们一次看一个。
LOCATING THE USER ACCOUNTS
To implement authentication, you need to decide where the user accounts for your
application are stored. Seam leaves this task entirely up to you. If you do want Seam to
help out, you have the option in Seam 2.1 of letting the identity manager consult your
database or LDAP to find an account, though it requires that you follow a standard, yet
flexible, model.
定位用户的账户
要实现认证,你需要知道应用的用户帐户保存在哪里。SEAM完全让你决定这些。如果你真的要SEAM来帮你,Seam 2.1中你就有了选择让标识管理器咨询你的数据库,或LDAP,以找到账户,需要你遵循标准,但依然有弹性的模型。
In Open 18, the accounts are mapped to a table in the database through the Member
entity and retrieved using the EntityManager. The Member entity, introduced in
chapter 4, has fields to store the member’s username and hashed password, but
doesn’t have a field to hold the member’s roles. Though not mandatory, you may want
to assign roles to the user during authentication, which are pulled from the database.
First, you need a Role entity:
在Open 18中, 账户通过Member实体,用EntityManager映射到数据库的一个表。在第4章引用的Member实体,有字段可以保存成员的用户名和密码,不是没有字段可以保存成员的角色。虽然不是强制的,你应在认证时指定一个角色到用户,其被从数据库中取出。首先,你需要一个Role实体:
Next, you add a Role collection to the Member entity, related through a join table:
然后,你增加一个Role集合到Member实体,通过一个join表关联:
private Set roles = new HashSet();
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "MEMBER_ROLE",
joinColumns = @JoinColumn(name = "member_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
public Set getRoles() { return this.roles; }
public void setRoles(Set roles) { this.roles = roles; }
While roles are optional for authentication, they are essential when it comes to implementing
authorization. In a sense, authentication is the binary part of security: The
user is either authenticated or the user is not authenticated. Only after establishing
the user’s identity can we begin talking about authorization. Let’s find out, in three
steps, how a 0 becomes a 1.
当角色是认证的可选,对于实现认证,他们很重要。可以这样认为:认证是安全的分界线,要么是认证了的,要么是没认证的。只在了解了认证后,我们才能谈授权。通过三步,0变成了1。
11.1.2 Implementing authentication in three steps
The three steps for setting up authentication in Seam are as follow:
1 Switch on authentication by configuring an authentication method.
2 Verify the user’s credentials in the authentication method.
3 Create a JSF login form.
三步实现授权
SEAM中三步设置认证如下:
通过配置认证方法来切换认证
在认证方法中校验用户的评证
建立JSF登录表单
When these steps are complete, your application will support form-based authentication.
Later on, you’ll learn that in Seam 2.1, you can let Seam’s identity manager handle
the second step for you. If you would rather not bother with the login form, you
can plug in Seam’s support for HTTP authentication, in which case the credentials are
negotiated by the browser. Let’s put these alternatives aside for now and continue with
the steps laid out here.
这些步骤完成后,你的应用即支持基于表单认证。后面,你将学到在Seam 2.1中,你可以让SEAM身份管理器为你处理第二步。如果你不想使用登录表单,可以插入SEAM对HTTP认证的支持,在这种情况下,身分由浏览器处理。让我们先将这些选择放一放,继续看看那些步骤:
STEP 0: ZERO PREREQUISITES
The first step isn’t a step at all but simply a fact. You don’t need any extra libraries to
implement authentication and role-based security in Seam. Only when you branch
out to Seam’s rule-based security, covered in section 11.4, are additional dependencies
needed.
步0:无前题
第一步不是步,但是一个简单的事实。在SEAM中,你无须任何额外的库来实现认证和基于角色的安全。只有当你走出SEAM基于角色的安全,才会需要附加的依赖。在11.4中讲述。
In fact, projects created using the seam-gen tool already have the authentication
routine configured. It just leaves out one critical detail. The default configuration
accepts any username and password. To heighten security (to put it lightly) and keep
out the imposters, the user’s login credentials need to be validated against the database
of registered members.
事实上,用seam-gen建立的项目已经有了配置好的认证路线。它只是没有具体认证细节。默认的配置接受任何用户名和密码。为了让安全更强、更聪明、远离骗子。用户的登录凭据需要匹配数据库中已注册用户信息。
STEP 1: SWITCHING ON AUTHENTICATION
Enabling security in Seam is a bit of a misnomer. Security is enabled by default, unless
you purposely disable it (perhaps in a test). However, out of the box, there’s no way
for users to authenticate themselves. To make that possible, you first need to tell Seam
which method handles the authentication logic (i.e., the authentication delegate).
This method is provided by one of your components. Three requirements must be
met by the authentication method:
步1:认证中的开关
在SEAM中启用安全本身有些误解。安全是默认启用的,除非你有意禁用了它(可能测试时会这样)。不管怎样,刚拿过来时,用户是无法认证他们自己的。要能这样,你必须先告诉SEAM是哪个方法处理认证逻辑(如认证代表)。这个方法由我的一个部件提供。认证方法有三个前提条件:
■ It must take no arguments.
■ It must return a boolean indicating whether the credentials could be verified.
■ It must be accessible via the EL (which isn’t much of a problem in Seam).
没有参数
返回布尔值,指示是否通过
必须可以通过EL访问(这对SEAM来说毫无问题)
The authentication method can have any name, it can reside on any class, and that
class doesn’t have to implement any special security interfaces. Seam plugs your
authentication method into JAAS, but hides the complexity of JAAS in the SeamLogin-
Module. Internally, JAAS invokes your authentication method and adds the appropriate
principals to the security subject if the method returns true. This authentication
routine is activated through Seam’s identity component, freeing you from having to
interact with the colossus that lies beneath.
认证方法可以用任何名,可存在于任何类,这个类也不用实现任何特殊的安全接口。SEAM插入你的认证方法到JAAS,但隐藏JAAS的复杂性于
SeamLoginModule。内部,如果返回为true,JAAS调用你的认证方法并增加适当的规则到安全主题,认证路线被SEAM身份部件激,使你不必与底层的细节打交道。
The built-in Identity component, named identity, maintains a reference to the
authentication method as a method-binding expression. The identity component
lives in the component namespace http://jboss.com/products/seam/security,
declared in the component descriptor using the prefix identity. The authentication
method can be assigned to the identity component using component configuration:
名为identity的内建的身份部件用一个方法绑定表达式维护一个到认证方法的参考。identity部件存在于名字空间http://jboss.com/products/seam/security,在部件描述文件中声明,使用identity前缀。使用部件配置,认证方法可以被指定到identity部件:
authenticate-method="#{authenticationManager.authenticate}"/>
Here, the authentication method is provided by the authenticationManager component.
The next step is to implement this method.
这里,认证方法由authenticationManager部件提供。下一步则实现这一方法。
STEP 2: AUTHORING AN AUTHENTICATION METHOD
As I mentioned earlier, the authentication method can reside on any class. We use a
JavaBean component in this example. Here’s a naïve, but valid, implementation of the
method:
步2:写认证方法
前面提过,认证方法不附着在任何类上。本例中我们用JavaBean部件。这是个简单但有效的方法实现:
@Name("authenticationManager")
public class AuthenticationManager {
public boolean authenticate() {
return true;
}
}
To get serious, we need credentials to validate. If the authenticate() method doesn’t
take any arguments, where do the credentials come from? One of the roles of the
identity component, which is scoped to the session context and instantiated when
the user’s session begins, is to capture the credentials being challenged. The credentials
are stored in the username and password properties on this component, and are
typically populated by a JSF form. Thus, you get to the credentials by obtaining a reference
to the identity component.
更认真的话,我们需要验证凭据。如果authenticate()没有带任何参数,凭据从哪儿来?这是identity部件的一个作用,其范围定为session上下文,当用户session开始时,其初始化以得到凭据。凭据保存在这个部件的username 和password属性,典型地,由JSF表单产生。因此你通过获得identity部件来得到凭据的参考。
NOTE
If your authentication routine requires additional credentials, you can
extend Seam’s security infrastructure to capture them. In Seam 2.0, you
extend the Identity class and register it using the component name
org.jboss.seam.security.identity. In Seam 2.1, you extend the Credentials
class and register it using the component name org.jboss.
seam.security.credentials. The credentials component was introduced
in Seam 2.1 to hold the credentials. Although the credentials can
still be accessed using the identity component, the credentials component
is the preferred means of access.
如果你的认证路线需要额外的凭证,你可以扩展SEAM基础安全架构来获得它们。在Seam 2.0扩展Identity类并使用部件名org.jboss.seam.security.identity对其注册。在Seam 2.1,你扩展Credentials类,用部件名org.jboss.seam.security.credentials来注册。credentials部件在Seam 2.1被引入来存储凭证。虽然凭证仍然可以通过identity部件访问,但通过凭证部件访问会更好。
To pull the credentials into the authentication method, you simply inject identity
(or credentials in Seam 2.1) into the authentication component using @In:
向认证方法中加入凭证,你可以简单地注入identity(或Seam 2.1的credentials)到认证部件。
@Name("authenticationManager")
public class AuthenticationManager {
@Logger private Log log;
@In private Identity identity;
public boolean authenticate() {
log.info("username: #0, password: #1",
identity.getUsername(), identity.getPassword());
identity.addRole("member");
return true;
}
}
As you see here, the role of the identity component in the authentication method is
twofold. It delivers the login credentials and it’s used to store a set of roles. In this
implementation, we assign all users the member role. JAAS transfers the roles to the
security subject during the post login routine. Let’s consider how that works.
如你所见,认证方法中identity部件的作用是双重的。它发出了登录凭据,并且那用于保存一套角色。在这个实现中,我们指定所有用户的成员角色。在登录路线的后末尾,JAAS转换角色到安全主体。让我们考虑一下是如何工作的。
When the authentication method is called, the user principal and roles group
haven’t yet been established on the Subject instance (because the user hasn’t been
authenticated). That initialization takes place inside the JAAS login module after the
authentication method returns true. In the interim, the identity component provides
temporary storage for roles—appended using the addRoles() method—that
need to be transferred to the user’s group identity. During the post-authentication
routine, Seam converts the role names into role principals and adds them to the roles
group on the Subject instance.
当认证方法被调用,用户规则和角色组还没有在Subject实例上建立(因为用户还没有被认证)。在认证方法返回true后,初始化在JAAS登录模块内部发生。在这期间,identity部件为角色提供临时的存储,这由addRoles()方法加入,需要被转化为用户组身份。在认证路线后期,SAM转换用户名到角色规则,并将其加入Subject实例的角色组。
NOTE
Seam doesn’t impose a naming convention for roles, so feel free to use
your own scheme.
SEAM不对角色强加命名转换,所以可以自在地使用自己的scheme.
The authentication routine produces a standard Java security principal, meaning Servlet
security just works. You can use the HttpServletRequest#isUserInRole() method
to check if a user has been granted a role, and it enables transparent integration with
libraries that depend on this method. To get all of this, you only have to write a couple
lines of code (even counting the XML). That number grows when you add the authentication
logic.
认证路线产生一个标准和JAVA安全规则,这意味着Servlet安全可用。你可以使用HttpServletRequest#isUserInRole()方法来检查是否用户被授与了一个角色,它与依赖于这个方法的库有透明的集成。要得到这些,你只要写几行代码(甚至可以依赖XML)。
The authentication logic for Open 18 is presented in listing 11.1. The username
on the identity component is used to look up a matching Member entity in the database
using the EntityManager. If an instance is found, the password is validated by
comparing its hash to the hashed password from the database. If both checks succeed,
the roles are added and the method returns true, returning control to JAAS to establish
the security principals. If either check fails, a return value of false sends the user
back to the login page with a failure message. We examine the details of the failure
scenario a bit later.
Open 18的认证逻辑列于表11.1,identity部件的用户名用于EntityManager查找数据库中匹配的Member实体。如果找到,密码也对,角色会被增加并用方法返回true,控制返回JAAS以建立安全规则。如果任何一下检查失败,返回值false将用户送到登录页面并给出失败信息。稍后我们再看失败场景的细节。
If the member is a golfer, currentGolfer is outjected for convenience. To ensure it
hangs around for the duration of the session, we define a role for it on the Golfer
class:
如果成员是球员,可以方便地注出currentGolfer。为了确保在session中有效,我们在Golfer类中定义一个角色。
@Role(name = "currentGolfer", scope = ScopeType.SESSION)
All that’s left is to create a form for the user to enter credentials and attempt a login.
剩下的是建立一个表单,让用户录入凭证信息。
STEP 3: CREATING THE LOGIN FORM
You’ll be thrilled to discover that the j_username and j_password request parameters
and the /j_security_check servlet path, defined in the Servlet specification for implementing
form-based logins, have finally been retired under Seam. And for those of
you who have had to invent a custom handoff to get a JAAS login module to play nicely
with JSF, you’ll be happy to know that you can use a native JSF form on the login page.
The login form boils down to two value-binding expressions, #{identity.username}
and #{identity.password}, which capture the user’s login cred
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/21802202/viewspace-1027642/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/21802202/viewspace-1027642/