闫闰阖's profile阎王本纪PhotosBlogListsMore Tools Help

Blog


Spring Web Flow

May 2005

Discuss this Article


Introduction

Have you found as your web application gets more complex, understanding and managing the page flow – the orchestration that drives your application use cases – gets harder and harder? Are you tired of being forced into very particular ways of doing things that don’t give you much reuse? Do you feel you’re spending too much time developing your own approaches to generic problems like session state management?

Enter Spring Web Flow.

What is Spring Web Flow?

Spring Web Flow (SWF) is an emerging module of The Spring Framework. The module is part of Spring’s web application development stack, which includes Spring MVC.

Spring Web Flow aims to be the best solution for the management of web application page flow. It is a powerful controller for use when your applications demand complex controlled navigations, such as wizards, to guide the user through a series of steps within a larger application transaction.

An example of such a controlled navigation is illustrated as a UML State Diagram below:

Figure 1 - An Example Flight Booking Flow

Astute readers will recognize this as a typical flight booking flow – the kind you participate every time you book an airline reservation on-line.

Why Does Spring Web Flow Exist?

In traditional web applications, page flows like the one above are not explicit—they are not first class citizens. Take a webapp built on Struts, for example. To implement a page flow in Struts, most developers build on what the framework provides them: actions and views. In this case, a single action is associated with a specific request URL. When a request comes in at that URL, the action is executed. During execution, the action performs some processing and then selects an appropriate result view for display. It’s that simple.

So to implement a multi-step page flow in Struts, individual actions are chained together through the various views. Action URLs to process different events like “back” or “submit” are hard-coded into each view. Some form of ad-hoc session storage is used to manage flow state. Redirect after post is used to prevent duplicate submissions, etc.

Although this is a simple and functional approach, it has a major disadvantage: the overall page flow of the web application is not clear from looking at the action definitions in the struts-config.xml file. You can’t see the forest – the flow – from the trees – the many action and view definitions. Flexibility also suffers since actions and views cannot be easily reused. Finally, you simply have to do too much work—it should be easier!

Spring MVC offers a slightly higher level of functionality: form controllers that implement a predefined page flow. Two such controllers are provided out of the box: SimpleFormController and AbstractWizardFormController. However, these are still specific examples of a more general page flow concept.

Tapestry and JSF use an event-driven approach at the page level, rather than the request level, where each page and its backing controller logic are kept together. However, neither provides first-class support for a logical page flow with a well-defined lifecycle that spans several pages and potentially different paths. As you’ll see, the lifecycle of such a page flow is longer than a single request, but shorter than a session.

This is where Spring Web Flow comes in, allowing you to represent the page flow of a web application in a clear and simple way, and reuse it anywhere, including environments like Struts, Spring MVC, Tapestry, JSF, and even Portlets.

Advantages

As you will see, Spring Web Flow offers several advantages:

  • The page flow in a web application is clearly visible by looking at the corresponding web flow definition (in an XML file or Java class).
  • Web flows are designed to be self contained. This allows you to see a part of your application as a module you can reuse in multiple situations.
  • Web flows capture any reasonable page flow in a web application always using the same consistent technique. You're not forced into using specialized controllers for very particular situations.
  • Finally, a web flow is a first-class citizen with a well-defined contract for use. It has a clear, observable lifecycle that is managed for you automatically. Simply put, the system manages the complexity for you and as a result is very easy to use.

How does Spring Web Flow Work?

For now it suffices to say that a web flow is composed of a set of states. A state is a point in the flow where something happens; for instance, displaying a view or executing an action. Each state has one or more transitions that are used to move to another state.

A transition is triggered by an event .

The Book Flight Sample Web Flow

To demonstrate what a web flow definition looks like, the following piece of XML captures the flight booking process illustrated in the UML state diagram above:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
	"http://www.springframework.org/dtd/spring-webflow.dtd">

<webflow id="bookflight" start-state="obtainTripInfo">

	<action-state id="obtainTripInfo">
		<action bean="bookingActions" method="bindAndValidate"/>
		<transition on="success" to="suggestItineraries"/>
		<transition on="error" to="tryAgain"/>
	</action-state>

	<action-state id="suggestItineraries">
		<action bean="bookingActions"/>
		<transition on="success" to="displaySuggestedItineraries"/>
	</action-state>

	<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
		<transition on="startOver" to="cancel"/>
		<transition on="select" to="selectItinerary"/>
	</view-state>

	<action-state id="selectItinerary">
		<action bean="bookingActions"/>
		<transition on="success" to="isPassengerInfoRequired"/>
	</action-state>

	<decision-state id="isPassengerInfoRequired">
		<if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
		<if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
			then="enterPassengerInformation" else="displayReservationVerification"/>
	</decision-state>

	<subflow-state id="enterPassengerInformation" flow="passenger">
		<attribute-mapper>
			<input value="${requestScope.passenger.id}" as="passengerId"/>
		</attribute-mapper>
		<transition on="finish" to="displayReservationVerification"/>
	</subflow-state>

	<view-state id="displayReservationVerification" view="reservationVerification">
		<transition on="startOver" to="cancel"/>
		<transition on="assignSeats" to="chooseSeatAssignments"/>
		<transition on="book" to="book"/>
	</view-state>

	<subflow-state id="chooseSeatAssignments" flow="seatAssignments">
		<attribute-mapper>
			<input value="${requestScope.passenger.id}" as="passengerId"/>
			<input name="itinerary"/>
		</attribute-mapper>
		<transition on="finish" to="displayReservationVerification"/>
	</subflow-state>

	<action-state id="book">
		<action bean="bookingActions"/>
		<transition on="success" to="displayConfirmation"/>
	</action-state>

	<end-state id="displayConfirmation" view="reservationConfirmation"/>

	<end-state id="tryAgain" view="tryAgain"/>

	<end-state id="cancel" view="home"/>

</webflow>

Figure 2 – A XML-based Flight Booking Flow definition

As you can see, just from scanning the XML definition, the logical flow driving the booking process is clearly discernable, even if you don’t yet know about Spring Web Flow implementation details.

And if you look a bit closer, you’ll see two subflows that spawn child processes of the booking flow. The first subflow guides the user through entering his passenger information. The second has the user make his seat assignments. The ability to nest flows that act as “mini application modules” is one of the most powerful capabilities of Spring Web Flow.

You could show the definition above to a business analyst and she’d probably get it. Better yet, you could engineer a visual diagram from this definition and present that to a business analyst for review. Tools to do exactly this are already appearing.

The Book Flight Flow Explained

The next part of this article breaks down the key parts of the above Book flight definition, and provides supporting dialog that illustrates how Spring Web Flow works.The Flow Definition

Starting with line 1 of the XML-based flow definition:

<webflow id="bookflight" start-state="obtainTripInfo">
	...
</webflow>

The webflow element defines the flow, specifying its id and start-state. The id is simply a unique identifier. The start state is the first state to transition to when a new flow session is activated at runtime.

So for this business case, when a new bookflight session is activated, it transitions to the obtainTripInfo state.The Obtain Trip Info Action State

Moving on to the obtainTripInfo state definition.

<action-state id="obtainTripInfo">
	<action bean="bookingActions" method="bindAndValidate"/>
	<transition on="success" to="suggestItineraries"/>
	<transition on="error" to="tryAgain"/>
</action-state>

Recall that when states are entered, behavior happens. As you’ll see, there are different state types that execute different behaviors. An action state, like obtainTripInfoabove, executes an action when entered. That action returns the logical result of its execution, and that result is mapped to a state transition. It’s that simple.

So for this business case, obtainTripInfo, when entered, executes the bindAndValidate method on the Action implementation with the bookingActions identifier. This method binds form input from the browser to a Trip domain object and validates it. If that process is successful, the suggestItineraries state is entered. If an error occurs, the tryAgain state is entered.The Booking Action

When using Spring Web Flow with Spring IoC, the bean attribute of the action element refers to the name of an Action implementation exported in the Spring Application Context. Here, the bookingActions bean definition looks like this:

web-context.xml

<bean id="bookingActions"
class="org.springframework.samples.bookflight.BookingActions">
    <property name="bookingAgent" ref="myBookingAgent"/>
</bean>

This allows our action implementation to be managed by Spring and configured via dependency injection.The Suggest Itineraries Action State

Now take a look at the next action state that, given a bound and validated Trip object as input, returns a collection of suggested itineraries:

<action-state id="suggestItineraries">
	<action bean="bookingActions"/>
	<transition on="success" to="displaySuggestedItineraries"/>
</action-state>

The actual implementation code required to make this happen is straightforward:

public class BookingActions extends FormAction {
    ...
    public Event suggestItineraries(RequestContext context) {
        Trip trip = (Trip)context.getRequestScope().getAttribute("trip");
        Collection<Itinerary> itineraries = bookingAgent.suggestItineraries(trip);
        context.getRequestScope().setAttribute("itineraries", itineraries);
        return success();
    }
}

When the suggestItineraries state is entered, the suggestItineraries method is invoked. The other action states work in exactly the same way: entering the state invokes a method on the target action bean.The Display Suggested Itineraries View State

Once a collection of suggested itineraries is returned, the next step has the user review them so she may select the best one. This is accomplished by the following state definition:

<view-state id="displaySuggestedItineraries" view="suggestedItenaries">
	<transition on="startOver" to="cancel"/>
	<transition on="select" to="selectItinerary"/>
</view-state>

As you can see, displaySuggestedItineraries is a view state—a state type we have not yet discussed. A view state, when entered, causes the executing flow to pause, and returns control back to the client with instruction to render the configured view. Later, after some user think-time, the client signals an event describing what action the user took. That resumes the flow, and the event that occurred is mapped to a state transition, which takes the user to the next step in the flow. Again, it’s that simple.

So for this business case, when the displaySuggestedItineraries state is entered the suggestedIteneraries view is rendered and control returns to the browser. The user then decides which itinerary she wants and clicks the “select” button. That signals the select event, passing in the id of the selected itinerary as an event parameter.

The user may also choose to startOver, at which time the flow transitions to the cancel state.

Note it is the responsibility of the client environment the flow is hosted in to map the requested view name, like suggestedItineraries, to a renderable view template, like /WEB-INF/jsp/suggestedIternaries.jsp. For example, in Spring MVC, the FlowController does this using the familiar ModelAndView and ViewResolver constructs. In Struts, the FlowAction does this using the familiar ActionForward.Client Side State

At this point you might ask:

“… since the executing flow is paused when a ViewState is entered, and control is returned to the browser, how is the same flow picked up and resumed on subsequent events?”

The answer is the client tracks the unique id of the executing flow, and provides it as input when the next event is signaled. This is typically done using a hidden form field.

For example, in a jsp:

<input type="hidden" value="<c:out value="${flowExecution.id}"/>">
The “Is Passenger Info Required?” Decision State

After the user selects the Itinerary she wants, the flow has to make a contextual decision about where to go next.

Specifically, if the user has not logged in, or she has logged in but wishes to confirm her passenger information – like the credit card she will use – the flow should allow her to enter that information. On the other hand, if she has already logged in and wishes to go straight to the booking page, the flow should skip this optional step.

Basically, a dynamic decision has to be made that takes into account the user’s information and preferences.

The decision state is perfect for this. See the definition below:

<decision-state id="isPassengerInfoRequired">
	<if test="${requestScope.passenger == null}" then="enterPassengerInformation"/>
	<if test="${requestScope.passenger.preferences.alwaysConfirmPassengerInfo}"
		then="enterPassengerInformation" else="displayReservationVerification"/>
</decision-state>
The Enter Passenger Information SubFlow State

The process of managing passenger information is logically independent of the booking process. It is one part within that process, yes, but it certainly makes sense a user would want to edit her information outside of the booking context.

Subflow states facilitate this. When a subflow state is entered, a child flow is spawned. The parent flow is suspended until the child flow ends. This lets you view your application as a set of self-contained modules – flows – that you can easily embed in multiple situations in a consistent manner.

Take a look at the enterPassengerInformation subflow state:

<subflow-state id="enterPassengerInformation" flow="passenger">
 	<attribute-mapper>
		<input value="${requestScope.passenger.id}" as="passengerId"/>
	</attribute-mapper>
	<transition on="finish" to="displayReservationVerification"/>
</subflow-state>

The flow attribute is the id of the flow to spawn when this state is entered. The attribute-mapper element maps attributes to and from the subflow. Input mappings map attributes down to the subflow. Output mappings map attributes back up to the parent flow when the subflow ends. As you can see, expressions (in this case OGNL) are also supported.

So for this business case, when the enterPassengerInformation state is entered, the passenger flow is spawned. The passengerId attribute is passed down to the flow as input. From there, the subflow does whatever it wants. It’s a black box as far the parent flow is concerned. When the subflow ends, the parent flow resumes, responding to the ending result to determine where to go next—in this case, to reservation verification. The Display Confirmation End State

There is one last core state type that has yet to be discussed: the end state. When an end state is entered, the active flow session terminates. Upon termination, all resources associated with the flow are cleaned up for you automatically.

Below is the displayConfirmation end state that displays confirmation after an itinerary is successfully booked:

<end-state id="displayConfirmation" view="reservationConfirmation"/>

When this state is entered, the bookflight flow ends and the reservationConfirmation view displays.  Because the bookflight flow was acting as the root flow, and not a subflow, it and any allocated resources are automatically cleaned up.

Note: had the ending flow been acting as a subflow, the entered end state is treated as a subflow result the resuming parent flow can respond to.  More specifically, the entered end state ID is used as grounds for a state transition in the resuming parent flow's subflow state.  You can see this in action by taking a look at the "enterPassengerInformation" subflow state definition.  Note how it responds to the "finish" result of the subflow, which corresponds to a "finish" end state within the passenger flow.

Flow Deployment

So far you’ve learned what Spring Web Flow is all about, and you’ve seen an example of a realistic flow definition. What you haven’t seen yet is how to deploy that flow definition for execution in a particular environment, like Spring MVC in a servlet environment.

Doing this is a cinch. Here’s all you have to do with Spring MVC:

<bean name="/booking.htm" class="org.springframework.web.flow.mvc.FlowController">
    <property name="flow">
        <ref bean="bookingFlow"/>
    </property>
</bean>
 
<bean id="bookingFlow" class="org.springframework.web.flow.config.XmlFlowFactoryBean">
   <property name="location" value="classpath:bookflight-flow.xml"/>
</bean>

This automatically exports the bookingFlow at the /booking.htm URL for use in a servlet environment.

Advanced Topics

The following section introduces some of the more advanced features of Spring Web Flow.

Flow Execution Listeners

The FlowExecutionListener construct is an observer that allows you to listen and respond to the lifecycle of an executing flow. You can use this feature to do anything from state precondition and post condition checks, to auditing and security.

Flow Execution Storage Strategies

The mechanism by which the state of an executing flow is saved and restored is fully pluggable. HttpSession-based storage is the default, and SWF provides two other storage strategies out of the box: one using server-side continuation-based session storage, another using full client-side serialization. Defining your own custom storage, for example to store flow state in a Database, is trivial.

When Is Spring Web Flow Right For You?

It should be noted that Spring Web Flow is not a one-size-fits-all solution. As you’ve seen, it’s a stateful system that automates the management of page flows that drive business processes. It should not be used when simpler, stateless solutions are more appropriate. For example, it should not be used where sites require free navigations, where the user is free to “click around” anywhere they please. Spring Web Flow is designed to power controlled navigations, where the user is guided through a process with a clear business goal and lifecycle.

To further make the use case more concrete, here are some examples of “good flows”, where the SWF system would be appropriate:

  • Book a flight
  • Pay your taxes
  • Apply for a loan

Here are some examples where Spring Web Flow would not be appropriate:

  • Index pages
  • Welcome pages
  • Menus
  • Simple form flows (one page)

Spring Web Flow is meant to be used as a compliment to traditional controllers within any web environment, such as Spring MVC, Struts, Tapestry, Web Work, JSF, or Portlets. A single site should combine use of simple controllers with web flows where appropriate.

Road Map

Spring Web Flow 1.0 final will be released with Spring 1.3, scheduled right before JavaOne in June. Between now and then, expect regular, stable-for-development-use preview releases. The offering is already quite mature in terms of feature set and sample applications.

As the development team pushes closer to a final release, here are some of the most important features we will be working on:

Integration

As a standalone library, Spring Web Flow is a strong fit for integration with other frameworks. Out of the box Spring MVC, Struts, and Portlet MVC integration is already provided. JSF and Tapestry integration are expected by the final release.

Flow Management

With Spring 1.2, exporting beans in an MBeanServer for management and monitoring is easy. A strongly typed FlowExecutionMBean management interface already exists, and we plan to extend that so global statistics on all flows executing on the server can be centrally monitored through a JMX console.

Pluggability

Every construct in the system will be made pluggable for easy extension and customization, even from the xml definition. This includes States and Transitions, among other concepts.

Compensating Transactions

Supporting features and sample applications demonstrating use of compensating transactions to rollback previously committed work during execution of a flow is of high interest to us.

Conclusion

Spring Web Flow is a powerful solution for managing controlled navigations that drive business processes. And it’s fun to work with. If you haven’t tried it already, what are you waiting for?

References

Spring Web Flow is covered in the Core Spring training course offered by Interface21 - http://www.springframework.com/training

The Spring Framework, http://www.springframework.org

The Spring Web Flow Wiki, http://opensource.atlassian.com/confluence/spring/display/WEBFLOW/Home

The kdonald blog, http://www.jroller.com/page/kdonald

Struts, http://struts.apache.org

Java Server Faces, http://java.sun.com/j2ee/javaserverfaces/

Tapestry, http://jakarta.apache.org/tapestry

WebWork, http://www.opensymphony.com/webwork/

JMX, http://java.sun.com/jmx

JavaOne, http://java.sun.com/javaone/

Biographies

Keith Donald is an Interface21 principal and a core Spring Framework project member. An experienced developer and mentor, Keith has built applications for customers spanning a diverse set of industries including banking, network management, information assurance, education, and retail. He specializes in translating business requirements into technical solutions. Keith is the founder of the Spring Rich Client Project and co-lead of Spring Web Flow with Erwin Vervaet. Lately you can find him leading Spring Training courses across the US and abroad, and as a guest speaker on Spring with the NoFluffJustStuff (NFJS) tour.

Erwin Vervaet is a software engineer with a keen interest in applying modern IT concepts and tools. He has been using the Java language since 1996, and has a master's degree in computer science from the Katholieke Universiteit Leuven in Belgium. He has been involved in IT research, e-commerce projects, open source initiatives, and industrial software systems. Erwin currently works as an independent consultant.

1/7/2008

每日一博 感觉OpenJPA的实现比较烂

现在java的动态代理技术已经很成熟了,居然还在使用代码预编译的方式,不稀饭
1/6/2008

英语是“一维文字”,汉字是“二维文字”

 

英语是“一维文字”,汉字是“二维文字”

——关于汉字改革问题的一些思考

黄佶

1,英语是一维的,而汉字是二维的

  英语仅通过水平方向的字母变化传递信息,例如“one”和“two”,而汉字可以在垂直方向通过笔划的变化,传递不同的信息,例如“一”和“二”。

  在电脑中,每一个英文字母需要一个字节(byte)来代表,因此,一个单词中包含的字母越多,占用的字节数也越多。不幸的是,学过英语的人都知道,一个英语单词的含义越复杂,单词中包含的字母也越多,例如我的专业:metallurgy(冶金学)。这个英语单词在电脑里需要十个字节来存储。而它对应的汉语“冶金学”只占用六个字节,因为一个汉字不论多么复杂,含义多么丰富,只需要两个字节来代表。根本原因就在于汉字来自“图画”(象形文字),是一种“二维文字”。

  归纳起来可以这么说:汉字是二维文字,在同样的“宽度”中,包含了较多的信息,因为它在“高度”方向也可以变化,也可以携带信息。在印刷品中可以看得很清楚,汉语的字体稍大,但是表达同样含义的句子较短;而英语的字体可以稍小,但是行数较多,页面上很多空间用于行的分隔。

  汉语拼音化,实际上是把汉语从“二维文字”改造成“一维文字”。也许从这个角度讨论汉字改造问题会有新的思路?

2,汉字的“二维”信息被预先存储在电脑里了

  参加汉字通讯的电脑,预先都会安装汉化平台,例如中文windows。安装汉化平台时,我们实际上已经把汉字的二维信息存储在电脑里了。电脑接受到两个字节后,会自动地、迅速地把这两个字节还原成它们对应的那个汉字。

  也就是说,在汉字的传输过程中,有很大一部分信息根本不需要传输。汉字虽然书写困难,但是传输汉字时并非传输它的写法,而是仅仅传输它的代码。它的写法早已在安装汉化平台时就已经输入电脑了,代码到达接受方电脑时,电脑自动地给它们“对号入座”。

  我在这里创造一个新词:“字节文字”。字节文字的基本特征是用不同的数字组合来代表文字。例如当我们把代表“冶金学”这三个汉字的六个字节,从左到右顺序排列,我们就得到了对应“冶金学”的“字节文字”。

  我们在电脑上输入汉字后,电脑随即把汉字转化成“字节文字”加以存储。

  实际上,从方便书写、方便输入、方便存储、方便传输等方面考虑,把汉字从“象形文字”改造成“字节文字”比改造成“拼音文字”更加合理。“字节文字”还可以彻底解决汉字的同音字问题,而这是汉字拼音化遇到的最大问题。

  然而,文字的最主要功能是供人类识别,拼音文字以发音帮助阅读者识别,象形文字以形象帮助阅读者识别,而字节文字只能靠预先死记硬背存储在大脑中的记忆识别,因此非常难以掌握。所以“字节文字”没有被人类用于相互交流,而是用于擅长死记硬背的电脑。

3,技术的进步具有根本性质的影响

  电脑因为预先存储汉字的二维信息而减少了汉字占用的存储空间和传输带宽。

  电脑的存储器(内存和硬盘)越来越便宜,预先存储更多的东西已经成为可能,例如字组(汉字输入的联想功能)、汉字字形(汉字自动识别——OCR)等等。

  电脑的处理能力越来越大,原来不可能的事情现在已经可以实现了,而且成本很低,能够普及。例如手写汉字识别,语音识别等。

  即将到来的传输线路带宽突破性的拓宽,不仅将使文字型文件(文件大小在100Kbyte以内)的传输不再成为问题,而且图象文件(文件大小在1Mbyte以内)的全球传输也可在瞬间完成,传输瓶颈将上移到音频和视频文件(文件大小在1Gbyte量级)。

  这一切都应该在文字改革研究者的视野里。

  当年电脑的发明者为了节约两个字节的存储空间,而省略年份的前两位,结果造成后人全球大战千年虫。

  为了克服眼前的一个小困难,而给后人留下一个大麻烦,肯定不会是大家希望发生的事情吧。

  网上有一则幽默说,在解决千年虫问题时,应该再增加一位,以免我们的后人大战“万年虫”。虽然是笑话,但充分证明说笑话的人有着常人所没有的高瞻远瞩。

1999年10月19日于上海

返回目录

每日一博 Spring2.x之AOP

可以在xml使用特有标签进行描述
aop:config aop配置
aop:pointcut 一个切点的配置,可以使用aspectJ的表达式或者正则表达式苗粟
aop:advice 声明一个advice
aop:advisor 声明一个advisor (spring特有aop概念)
aop:aspect 声明一个切面
 
使用spring ide最酷的是,xml文件中定义好之后,类源码文件被pointcut定义的类方法左侧会出现箭头标记,so cool! 
 
aop之introduction, 通过aop的方法为target类添加接口或者成员
1/4/2008

每日一博 Jan 4, 2008 Eclipse 3.3 RCP 之新特性

提供了扩展点,可以在启动的splash界面中添加UI元素并响应操作
注意点:
1、eclipse product 必须设置启动splash属性为login ....
2、eventLoop中抛出运行时异常则该扩展点失效,eclipse rcp直接跳过splash进入workbench界面
 
1/3/2008

Jan 3, 2008 之日志 java特性之新发现-序列化

序列化inner class,则outter class也必须是可序列化的,否则序列化失败
public class OutterClass implements Serializable {
 /**
  *
  */
 private static final long serialVersionUID = -5954189492075374737L;
 Entry<String, byte[]> getEntry(String key, byte[] value) {
  return new SeriliazableMapEntry<String, byte[]>(key, value);
 }
 class SeriliazableMapEntry<K extends Serializable, V extends Serializable>
   implements Entry<K, V>, Serializable {
  private static final long serialVersionUID = -523966016744401526L;
  private K key;
  private V value;
  public SeriliazableMapEntry(K key, V value) {
   this.key = key;
   this.value = value;
  }
  public K getKey() {
   return key;
  }
  public V getValue() {
   return value;
  }
  public V setValue(V value) {
   this.value = value;
   return this.value;
  }
 }
}
 
public static void main(String[] args) throws IOException,
   ClassNotFoundException {
  byte[] b = new byte[1000];
  for (int i = 0; i < 1000; i++) {
   b[i] = (byte) (i % 128);
  }
  Entry<String, byte[]> entry = new OutterClass().getEntry(UUID
    .randomUUID().toString(), b);
  System.out.println(entry.getKey());
  System.out.println(entry.getValue());
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(bos);
  oos.writeObject(entry);
  ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
  ObjectInputStream ois = new ObjectInputStream(bis);
  Entry<String, byte[]> newEntry = (Entry<String, byte[]>) ois
    .readObject();
  System.out.println(newEntry.getKey());
  System.out.println(newEntry.getValue());
  byte[] nb = newEntry.getValue();
  System.out.println(nb.length);
  for (int i = 0; i < nb.length; i++) {
   System.out.println(nb[i]);
  }
 }
5/18/2007

兔子的故事完整版

一天,一只兔子在山洞前写文章,
>>>一只狼走了过来,问:“兔子啊,你在干什么?”
>>>答曰:“写文章。”问:“什么题目?”答曰:“《浅谈兔子是怎样吃掉狼的》。”
>>>狼哈哈大笑,表示不信,于是兔子把狼领进山洞。
>>>过了一会,兔子独自走出山洞,继续写文章。
>>>一只野猪走了过来,问:“兔子你在写什么?”答:“文 章。”问:“题目是什么?
>>”
>>>答:“《浅谈兔子是如何把野猪吃掉的》。”野猪不信,于是同样的事情发生。
>>>最后,在山洞里,一只狮子在一堆白骨之间,满意的剔着牙读着兔子交给它的文
章,
>>>题目:“《一只动物,能力大小关键要看你的老板是谁》。”
>>>
>>>这只兔子有次不小心告诉了他的一个兔子朋友,这消息逐渐在森林中传播;
>>>狮子知道后非常生气,他告诉兔子:“如果这个星期没有食物进洞,我就吃你。”
>>>于是兔子继续在洞口写文章
>>>一只小鹿走过来,“兔子,你在干什么啊?”
>>>“写文章”“什么题目”““《浅谈兔子是怎样吃掉狼的》”
>>>“哈哈,这个事情全森林都知道啊,你别胡弄我了,我是不会进洞的”
>>>“我马上要退休了,狮子说要找个人顶替我,难道你不想这篇文章的兔子变成小鹿

>>”
>>>小鹿想了想,终于忍不住诱惑,跟随兔子走进洞里。
>>>过了一会,兔子独自走出山洞,继续写文章
>>>一只小马走过来,同样是事情发生了。
>>>最后,在山洞里,一只狮子在一堆白骨之间,满意的剔着牙读着兔子交给它的文章
>>>题目是:如何发展下线动物为老板提供食物
>>>
>>>随着时间的推移,狮子越长越大,兔子的食物已远远不能填饱肚子。
>>>一日,他告诉兔子:“我的食物量要加倍,例如:原来4天一只小鹿,现在要2天一

>>,如果一周之内改变不了局面我就吃你。
>>>于是,兔子离开洞口,跑进森林深处,他见到一只狼
>>>“你相信兔子能轻松吃掉狼吗”
>>>狼哈哈大笑,表示不信,于是兔子把狼领进山洞。
>>>过了一会,兔子独自走出山洞,继续进入森林深处
>>>这回他碰到一只野猪----“你相信兔子能轻松吃掉野猪吗”
>>>野猪不信,于是同样的事情发生了。
>>>原来森林深处的动物并不知道兔子和狮子的故事
>>>最后,在山洞里,一只狮子在一堆白骨之间,满意的剔着牙读着兔子交给它的文章
>>>题目是:如何实现由坐商到行商的转型为老板提供更多的食物
>>>
>>>
>>>时间飞快,转眼之间,兔子在森林里的名气越来越大
>>>因为大家都知道它有一个很历害的老板
>>>这只小兔开始横行霸道,欺上欺下,没有动物敢惹
>>>它时时想起和乌龟赛跑的羞辱
>>>它找到乌龟说:“三天之内,见我老板!”扬长而去
>>>乌龟难过的哭了
>>>这时却碰到了一位猎人
>>>乌龟把这事告诉了他
>>>猎人哈哈大笑
>>>于是森林里发生了一件重大事情
>>>猎人披着狮子皮和乌龟一起在吃兔子火锅
>>>地下丢了半张纸片歪歪扭扭的写着:山外青山楼外楼,强中还有强中手啊!!
>>>
>>>
>>>在很长一段时间里森林里恢复了往日的宁静,兔子吃狼的故事似乎快要被大家忘记

>>>不过一只年轻的老虎在听说了这个故事后,被激发了灵感
>>>于是他抓住了一只羚羊,对羚羊说,如果你可以象以前的兔子那样为我带来食物那

>>就不吃你。
>>>于是,羚羊无奈的答应了老虎,而老虎也悠然自得的进了山洞。
>>>可是三天过去了,也没有见羚羊领一只动物进洞。他实在憋不住了,想出来看看情
>>况。
>>>羚羊早已不在了,他异常愤怒。正在他暴跳如雷的时候突然发现了羚羊写的一篇文

>>>题目是:《想要做好老板先要懂得怎样留住员工》
4/17/2007

作文

今天是星期天,天气真不错呀,万里无云的天空上飘着朵朵白云,火红的太阳照着大地。我怀着愉快的心情出了门,拿着妈妈给我的2块钱去买酱油,买酱油要到百货商店,我站在家门口的21路车站等公交车。
    等了好久,公交车终于来了,我象兔子般的窜上了公交车,刷了学生卡,啊,只剩一个座位了,我好幸运呀,我飞快的做了上座。
  车开动了,我看着窗外的高楼大厦,心里想∶祖国的变化真是日新月遗呀,我们生活在这个年代真是性福啊!
     正在我暗自思考的时候,车又靠站了,没有人下车,却上来了一个人,我定精一看,是一个老奶奶,只见她满头白发,满脸大汗,脸色发黄,好象是有病了,我再一看,她左手住着拐杖,原来她左腿没了,右手拿着一个竹竿,不停的在地上敲打,原来她是个瞎子。我又一看,她背后还背着一个小宝宝。我最后一看,她的肚子大大的,圆圆的,还不停的吐酸水,啊,她是一个孕妇。
    车上做着的其他叔叔阿姨们,看到她都把脸扭到外面,假装没看见。车开动了,看着她一晃一晃的,我的心难受起来,司机叔叔按了一下广播,“各位乘客,如果车有老、弱、病、残、孕以及报小孩的乘客,请您将座位让给他们。”我心里想∶爸爸妈妈和老师从小就教育我要做个懂礼貌的好孩子,我应该把座位让给这位老奶奶。可我又一想,只有一个座位了呀,我还要做2个小时才能到百货商店呢!这时∶雷锋、董存瑞、罗生教、黄继光、刘胡兰、焦裕禄、赵春峨、陈思德、孔融、马克思的形象出现在我的脑海里。他们的事迹激励了我,是呀,我不能为了我自己舒服尔不顾他人的性福呀!但是我又下不了决心。
    就这样,我不停的做着激烈的思想斗争。不知过了多久,我终于下定了决心,把座位让给了老奶奶。老奶奶激动的对我说∶“小朋友,谢谢你呀,你叫什么名字呀?”
    刚好车停了,我到站了。我一边下车一边说∶“老奶奶,我叫红领巾!”
     透过远去的车窗,我看到老奶奶流下了激动的泪水。
3/1/2007

acegi参考手册(v1.0.4)[译]-第四章 信道安全

关键字: Java   acegi 安全 翻译    

第四章. 信道安全

4.1. 概述

Acegi Security不仅能满足你的认证和授权的请求,而且能够保证你的未认证的web请求也能拥有某些属性。这些属性可能包括使用特定的传输类型,在HttpSession设置特定的属性等等。Web请求的最普遍的需求是使用特定的传输协议,例如HTTPS。

在传输安全中的一个重要议题就是会话劫持(session hijacking)。Web容器通过一个jsessionid来引用一个HttpSession,这个jsessionid通过cookie 或者URL重写转向(URL rewriting)发送到到客户端。如果jsessionid是通过HTTP发送的,那么就存在被劫持以及在认证过程之后冒充被认证用户的可能。这是因 为大部分的web容器为特定的用户维护同一个会话标识符,即便是用户从HTTP 切换到 HTTPS页面。

如果对于你的特定应用来说,会话劫持(session hijacking)是一个很严重的风险,那么唯一的解决方法就是对每一个请求都使用HTTPS。这意味着jsessionid不会使用非安全信道传输。 你要保证你的web.xml中定义,把它指向一个HTTPS位置,同时应用程序不把用户指向一个HTTP位置。 Acegi Security提供一个解决方案帮助你实现后者。

4.2. 配置

启用Acegi Security的信道安全服务,需要在web.xml中增加如下行:

xml 代码
 
  1. <filter>  
  2.     <filter-name>Acegi Channel Processing Filter</filter-name>  
  3.     <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>  
  4.     <init-param>  
  5.         <param-name>targetClass</param-name>  
  6.         <param-value>org.acegisecurity.securechannel.ChannelProcessingFilter</param-value>  
  7.     </init-param>  
  8. </filter><filter-mapping>  
  9.     <filter-name>Acegi Channel Processing Filter</filter-name>  
  10.     <url-pattern>/*</url-pattern>  
  11. </filter-mapping>  

和平时一样,你同样需要在application context中配置filter

java 代码
 
  1. "channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter">  
  2.     "channelDecisionManager">"channelDecisionManager"/>  
  3.     "filterInvocationDefinitionSource">  
  4.           
  5.             CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
  6.             \A/secure/.*\Z=REQUIRES_SECURE_CHANNEL  
  7.             \A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL  
  8.             \A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL  
  9.             \A.*\Z=REQUIRES_INSECURE_CHANNEL  
  10.           
  11.       
  12.   
  13.   
  14. "channelDecisionManager" class="org.acegisecurity.securechannel.ChannelDecisionManagerImpl">  
  15.     "channelProcessors">  
  16.           
  17.             "secureChannelProcessor"/>  
  18.         "insecureChannelProcessor"/>  
  19.       
  20.       
  21.   
  22.   
  23. "secureChannelProcessor" class="org.acegisecurity.securechannel.SecureChannelProcessor"/>  
  24.   
  25. "insecureChannelProcessor" class="org.acegisecurity.securechannel.InsecureChannelProcessor"/>  

ChannelProcessingFilter和FilterSecurityInterceptor一样支持Apache Ant style paths。

ChannelProcessingFilter的工作方式是过滤所有的web请求,并将判断将适合的配置属性应用于其上。然后它代理到 ChannelDecisionManager。默认的实现类ChannelDecisionManagerImpl应该能够满足大多数需求。它就代理到 配置好的ChannelProcessor实例列表。ChannelProcessor会检视请求,如果它不满意请求(例如请求是发送自不正确的传输协 议)它将会重定向,抛出异常或者采取其他任何恰当的措施。

Acegi Security 包括ChannelProcessor两个实体类实现:SecureChannelProcessor 保证配置了REQUIRES_SECURE_CHANNEL 属性的请求都是从HTTPS发送过来的。而InsecureChannelProcessor 保证配置了REQUIRES_INSECURE_CHANNEL 属性的请求都是从HTTP发送过来的。如果没有使用请求的协议,这两个实现都会转到ChannelEntryPoint,而两个 ChannelEntryPoint 实现所作的就是简单的把请求相应按照HTTP 和 HTTPS重定向。

要注意重定向是绝对(例如http://www.company.com:8080/app/page) 而不是相对的(例如 /app/page)。在测试中发现Internet Explorer 6 Service Pack 1 有一个bug,因此如果在重定向的时候也改变使用的端口,它就不能正确响应。对应这个bug,在很多Acegi Security bean中都会使用的PortResolverImpl也使用绝对URL。请参阅PortResolverImpl的JavaDoc以获取更多信息。

你要注意使用为了在登录过程中保证用户名和密码的安全,要使用安全信道。如果你配合基于表单的登录使用 ChannelProcessingFilter,请记得一定要把你的登录页面设置为REQUIRES_SECURE_CHANNEL,并且 AuthenticationProcessingFilterEntryPoint.forceHttps属性设置为true。

4.3. 结论

一旦配置好了,使用安全信道是非常简单的。只要请求页面,不用管使用什么协议(HTTP 或 HTTPS)或什么端口(80, 8080, 443, 8443等)。显然你只要确定初始请求(获取通过在web.xml 中的 或一个众所周知的主页URL),完成以后filter会执行你application context定义的重定向。

你也可以在ChannelDecisionManagerImpl中增加自己的ChannelProcessor实现。例如,你可能通过"输入图片中的内容"检测到一个个人类用户,然后在HttpSession中设置一个属性。

要判断一个安全检查应该是或者ChannelProcessor或是 AccessDecisionVoter 记得前者是设计用来处理认证或者未认证的请求,而后者是设计用来处理已认证的请求。因此后者可以访问已认证的principal被授予的权限。

另外,ChannelProcessor检测到问题后一般是引发一个HTTP/HTTPS重定向这样他的请求可以被满足,而 AccessDecisionVoter将则会跑出一个AccessDeniedException异常(取决于支配的 AccessDecisionManager)。

acegi参考手册(v1.0.4)[译]-第三章 协助系统

关键字: Java   acegi 安全 翻译    

第三章. 协助系统

本章介绍一些Acegi Security使用的附加和协助系统。那些和安全无关,但是包含在Acegi Security项目中的部分,将会在本章中讨论

3.1. 本地化

Acegi Security支持对终端客户可能会看到的异常信息进行本地化。如果你的应用是为英文用户设计的,那么你什么都不用做,因为Acegi Security的所有消息默认都是英文的。如果你要支持其他区域用户,那么本节包含了你所需要了解的所有东西。

包括认证失败或者访问被拒绝(授权失败)的所有异常消息都可以被本地化。提供给开发者或者系统部署人员的异常或者日志信息(包括错误的属性、接口不符、构造器错误、debug级日志)没有被本地化,它们硬编码在Acegi Security的代码中。

在acegi-security-xx.jar(译注:xx代表版本号)的org.acegisecurity包中包含了一个 messages.properties文件。这个文件会被你的application context引用,因为Acegi Security实现了Spring的MessageSourceAware接口,它期待在application context启动的时候注入一个message resolver。通常你所需要做的是在你的application context中注册一个引用这个消息的bean,如下所示:

xml 代码
 
  1. <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  2.     <property name="basename"><value>org/acegisecurity/messages<!--</span-->value><!--</span-->property>  
  3. <!--</span-->bean>  

messages.properties是按照资源包标准命名的,它代表了Acegi Securtiy支持的默认语言。文件默认是英文的。如果你不注册一个消息源,Acegi Security仍然可以正常工作,它会用回硬编码的英文消息。

如果你想定制messages.properties文件,或者支持其他语言,那么你应该copy这个文件,然后重命名,并在上述的bean定义中 注册。因为文件中的key并不多,因此本地化花不了多少工夫。如果你针对消息文件进行了本地化,那么请和社区分享,你可以添加一个JIRA任务,将你正确 命名的messages.properties本地化文件作为附件添加。

为了完善关于本地化的讨论需要知道Spring的ThreadLocal org.springframework.context.i18n.LocaleContextHolder。你应该为每个用户设置代表他区域的 LocaleContextHolder。Acegi Security会尝试从这个ThreadLocal中获取的Locale来从消息源中获取消息。请参考Spring的文档以获取更多使用 LocaleContextHolder和能够帮你自动设置它的辅助类(例如

AcceptHeaderLocaleResolver, CookieLocaleResolver, FixedLocaleResolver, SessionLocaleResolver 等)的详细信息。

3.2. Filters

正如你在整个手册中看到的那样,Acegi Security使用很多filter。你可以使用FilterToBeanProxy或者FilterChainProxy来确定这些是怎样加入到你的web应用中的,下面我们来看看。

大部分filter使用FilterToBeanProxy来配置。例如下面web.xml中配置所示:

xml 代码
 
  1. <filter>  
  2.     <filter-name>Acegi HTTP Request Security Filter</filter-name>  
  3.     <filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>  
  4.     <init-param>  
  5.         <param-name>targetClass</param-name>  
  6.         <param-value>org.acegisecurity.ClassThatImplementsFilter</param-value>  
  7.     </init-param>  
  8. </filter>  

注意在web.xml中的filter实际上是一个FilterToBeanProxy,而不是真正实现filter逻辑的filter。 FilterToBeanProxy所作的是代理Filter的方法到一个从Spring的application context 获取的bean。这使得这个bean可以享受Spring application context的生命周期支持以及配置灵活性。这个bean必须实现javax.servlet.Filter。

FilterToBeanProxy只需要一个简单的初始化参数,targetClass或者targetBean。targetClass会定位 application context中指定的类的第一个对象,而FilterToBeanProxy按照bean的名字定位对象。象标准的Spring web应用一样,FilterToBeanProxy使用 WebApplicationContextUtils.getWebApplicationContext(ServletContext)来访问 application context,所以你应该在web.xml中配置一个ContextLoaderListener。

 

在IoC容器而不是servlet容器中部署Filter会有一个生命周期的问题。特别是,哪个容器应该负责调用Filter的"startup" 和 "shutdown"方法?注意到Filter的初始化和析构顺序随servlet容器不同而不同,如果一个Filter依赖于由另一个更早初始化的 Filter的配置,这样就会出现问题。另一方面,Spring IoC具备更加完善的生命周期/IoC接口(例如InitializingBean, DisposableBean, BeanNameAware, ApplicationContextAware以及其他许多)以及一个容易理解的接口契约(interface contract),可预见的方法调用顺序,自动装配支持,以及可以避免实现Spring接口的选项(例如Spring XML中的destroy-method 属性)。因此,我们推荐尽可能使用Spring生命周期服务而不是servlet容器生命周期服务。FilterToBeanProxy默认不会将 init(FilterConfig) 和 destroy()方法委派到被代理的bean。如果你需要这些调用被委派,那么将lifecycle初始化参数设置为servlet- container-managed。

我们强烈推荐你使用FilterChainProxy而不是FilterToBeanProxy。虽然FilterToBeanProxy是一个非 常有用的类FilterToBeanProxy,问题是当web.xml中filter变多时, 和 项就会太多而变得臃肿不堪。为了解决这个问题,Acegi Security提供一个FilterChainProxy类。它在FilterToBeanProxy中被装配(正如上面例子中所示),但目标类 (target class)是org.acegisecurity.util.FilterChainProxy。这样过滤器链(filter chain)可以在application context中按照如下代码配置:

xml 代码
 
  1. <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">  
  2.     <property name="filterInvocationDefinitionSource">  
  3.         <value>  
  4.             CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON  
  5.             PATTERN_TYPE_APACHE_ANT  
  6.             /webServices/*=httpSessionContextIntegrationFilterWithASCFalse,basicProcessingFilter,exceptionTranslationFilter,  
  7.             /*=httpSessionContextIntegrationFilterWithASCTrue,authenticationProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor  
  8.         <!--</span-->value>  
  9.     <!--</span-->property>  
  10. <!--</span-->bean>  
   

你可能注意到FilterSecurityInterceptor定义方式的相似之处。同时支持正则表达式和Ant Paths格式,越对应的URI越早出现。在运行时,FilterChainProxy会定位符合当前的web请求的第一个URI模式。每个对应的配置属 性代表了在application context中定义的一个bean的名字。接着fiter会按照它们被指定的顺序,按照FilterChain的标准行为模式被调用(如果一个 Filter决定停止处理,它可以不在chain中执行)。

如你所见,FilterChainProxy需要为不同的请求模式重复配置filter的名字(在上面的例子中,, exceptionTranslationFilter 和 filterSecurityInterceptor 是重复的)。这样的设计是为了让FilterChainProxy能够为不同的URI配置不同的filter调用顺序,同时也提高了表达力(针对正则表达 式、Ant Paths、以及任何FilterInvocationDefinitionSource的特定实现)和清晰度,可以知道是哪个filter应该被调用。

你可能注意到了我们在filter chain定义了两个HttpSessionContextIntegrationFilter (ASC是allowSessionCreation的缩写,是HttpSessionContextIntegrationFilter的一个属性)。 因为web服务不会为将来的请求提供一个jsessionid,为这样的用户创建HttpSessions是浪费的。如果你有一个需要最大限度的伸缩性的 高容量的应用,我们建议你使用上述的方法。对于小的应用,使用单一的HttpSessionContextIntegrationFilter (默认的allowSessionCreation设为true)应该足够了。

说到生命周期问题,如果对FilterChainProxy自身调用init(FilterConfig) 和 destroy()方法,它会把它代理到底层的filter。这样FilterChainProxy保证只初始化和析构每个filter一次,不论它在 FilterInvocationDefinitionSource中定义了多少次。你可以通过FilterToBeanProxy的lifecycle 初始化参数来控制这些方法是否被调用。如上面所讨论的那样,默认所有servlet容器生命周期调用是不被代理到FilterChainProxy的。

在web.xml中定义的filter的顺序是非常重要的。不管你实际用到哪个filter,的顺序应该是如下所示的:

1.ChannelProcessingFilter,因为可能要重定向到另一种协议。

2.ConcurrentSessionFilter 因为不使用任何SecurityContextHolder的功能,但是需要更新SessionRegistry来表示当前的发送请求的principal。

3. HttpSessionContextIntegrationFilter, 这样当一个web请求开始的时候就可以在SecurityContextHolder中设置一个SecurityContext,当web请求结束的时候 任何对SecurityContext的改动都会被copy到HttpSession(以备下一个web请求使用)。

4.Authentication processing mechanisms - AuthenticationProcessingFilter, CasProcessingFilter, BasicProcessingFilter, HttpRequestIntegrationFilter, JbossIntegrationFilter 等 - 修改SecurityContextHolder,使其中包含一个有效的认证请求令牌(token)。

5.SecurityContextHolderAwareRequestFilter, 如果你使用它来在你的servlet容器中安装一个Acegi Security aware HttpServletRequestWrapper。

6.RememberMeProcessingFilter, 如果早期的认证处理过程没有更新SecurityContextHolder,并且请求(request)提供了一个cookie启用remember- me服务,一个合适的被记住的Authentication对象会被放到SecurityContextHolder那里。

7.AnonymousProcessingFilter, 如果早期的认证处理过程没有更新SecurityContextHolder,, 一个匿名Authentication 对象会被放到SecurityContextHolder那里。

8.ExceptionTranslationFilter, 捕获所有的Acegi Security 异常,这样要么返回一个HTTP错误响应或者加载一个对应的AuthenticationEntryPoint。

9.FilterSecurityInterceptor, 保护 web URIs

所有上述的filter使用FilterToBeanProxy或FilterChainProxy。建议在一个应用中使用一个单个的 FilterToBeanProxy代理到一个单个的FilterChainProxy。,在FilterChainProxy中定义所有的Acegi Security Filters。如果你使用SiteMesh,确保Acegi Security filters 在 SiteMesh filters调用前调用。这样使SecurityContextHolder在SiteMesh decorator

acegi参考手册(v1.0.4)[译]-第二章 技术概览[下]

关键字:   acegi 安全 翻译    

2.4. 安全对象

如果你熟悉AOP,你会知道有很多种advice可用:before, after, throws aroundaround advice非常有用,因为它能够选择是否选择是否执行一个方法调用,是否修改返回值,以及是否抛出异常。Acegi Security对方法调用和web请求都提供around advice。我们使用AOP联盟实现对方法调用的around advice,对于web请求的around advice则是使用标准的过滤器(Filter)。

 

对于那些不熟悉AOP的人来说,关键是要理解Acegi Security能够帮助你保护方法调用以及web请求。大多数人对保护他们服务层的方法调用感兴趣。这是因为在当前的J2EE应用中,服务层包含了大多数的业务逻辑(声明,作者不赞成这种设计,反而支持正确封装的领域模型以及DTOassembly, facade 以及 transparent persistence patterns,而不是当前主流的贫血模型,我们将在这里讨论)。如果你需要保护service层的方法调用,使用标准的Spring AOP平台(或者被成为AOP 联盟(AOP Alliance))就足够了。如果你需要直接对领域模型进行保护,那么可以考虑使用AspectJ

 

你可以选择对使用AspectJ 或者AOP联盟(AOP Alliance)对方法进行授权,或者你可以选择使用过滤器(filter)来对web请求进行授权。你将0个,1个,2个或者3个这些方法一起使用。主流的用法是执行一些web请求授权,以及在服务层使用AOP联盟(AOP Alliance)对一些方法调用授权。

 

Acegi Security使用“安全对象”(secure object)这个词来指任何能够将安全应用于其上的对象。每个Acegi Security支持的安全对象都有自己的类,它是AbstractSecurityInterceptor的子类。重要的一点是,如果一个principal通过认证,当AbstractSecurityInterceptor执行的时候,SecurityContextHolder中要包含一个有效的Authentication

 

AbstractSecurityInterceptor提供一个固定的工作流程来处理安全对象请求。这个工作流程包括查找和当前请求相关联的“配置属性(configuration attributes)”。配置属性(configuration attributes)可以被认为是对被AbstractSecurityInterceptor使用的类有特殊含义的字符串。他们通常针对AbstractSecurityInterceptor使用XML进行配置。反正,AbstractSecurityInterceptor会询问AccessDecisionManager “这是配置属性(configuration attributes),这是当前的认证对象(Authentication object),这是当前请求的详细信息-那么这个特定的principal可以执行这个特定的操作吗?”。

 

假如AccessDecisionManager判定允许这个请求,那么AbstractSecurityInterceptor一般来说就继续执行请求。虽然这样,用户在少数情况之下可能需要替换SecurityContext中的Authentication可以通过AccessDecisionManager调用一个RunAsManager来实现。在某些不常见的情形下这将非常有用,例如服务层的方法需要用另一种标识(身份)来调用远程系统。这可能有所帮助,因为Acegi Security自动在不同的服务器之间传播安全标识(假设你正确配置了RMI或者HttpInvoker remoting protocol client)。

 

随着安全对象处理和返回-意味着方法调用完毕或者过滤器链(filter chain)处理完毕-AbstractSecurityInterceptor有最后的机会来处理调用。这时,AbstractSecurityInterceptor可能会修改返回的对象。我们可能要这样做,因为授权判断不能在安全对象调用途中执行。由于高度的可插拔性,如果需要AfterInvocationManager将控制权交给AfterInvocationManager来实际修改对象。这个类甚至可以彻底替换对象,或者抛出异常,或者根本不修改它。

 

因为是AbstractSecurityInterceptor中心模版类,看起来第一副插图该献给它。(译注:原手册里的图画的太丑陋了,我用jude重新画了一遍

<!--[if gte vml 1]><v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"> <v:stroke joinstyle="miter" /> <v:formulas> <v:f eqn="if lineDrawn pixelLineWidth 0" /> <v:f eqn="sum @0 1 0" /> <v:f eqn="sum 0 0 @1" /> <v:f eqn="prod @2 1 2" /> <v:f eqn="prod @3 21600 pixelWidth" /> <v:f eqn="prod @3 21600 pixelHeight" /> <v:f eqn="sum @0 0 1" /> <v:f eqn="prod @6 1 2" /> <v:f eqn="prod @7 21600 pixelWidth" /> <v:f eqn="sum @8 21600 0" /> <v:f eqn="prod @7 21600 pixelHeight" /> <v:f eqn="sum @10 21600 0" /> </v:formulas> <v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect" /> <o:lock v:ext="edit" aspectratio="t" /> </v:shapetype><v:shape id="_x0000_i1025" type="#_x0000_t75" style='width:423pt; height:270.75pt'> <v:imagedata src="file:///C:\WINDOWS\TEMP\msohtml1\01\clip_image001.jpg" o:title="ch2-1" /> </v:shape><![endif]--><!--[if !vml]--><!--[endif]-->

ch2-1

1 关键“安全对象”模型

 

只有那些希望实现全新的对请求进行截取截取和授权方式的开发者才需要直接使用安全对象。例如,可能构建一个新的安全对象安全调用一个消息系统。任何需要安全并且能够提供一种截取调用的方式(例如AOP around advice semantics)的东西都可以成为安全对象。虽然如此,大部分的Spring应用都会只是透明应用当前支持的三种安全对象类型(AOP Alliance MethodInvocation, AspectJ JoinPoint web request FilterInterceptor)。

 

2.5. 结论

恭喜!你已经获取了Acegi Security足够的概括性的图景来开始着手你的项目。我们探究了共享组件,认证过程,以及对“安全对象”的通用授权概念。手册中的余下部分你可能用到也可能用不到,可以按照任意顺序阅读。

acegi参考手册(v1.0.4)[译]-第二章 技术概览[上]

关键字:   acegi 安全 翻译    

第二章. 技术概览

2.1. 运行时环境

Acegi Security可以在JRE1.3中运行。这个发行版本中支持也Java 5.0,尽管对应的Java类型被分开打包到一个后缀是“tiger”的包中。因为Acegi Security致力于以一种自包含的方式运行,因此不需要在JRE中放置任何特殊的配置文件。特别无需配置Java Authentication and Authorization Service (JAAS)策略文件或者将Acegi Security放置到通用的classpath路径中。

 

同样的,如果你使用EJB容器或者Servlet容器,同样无需放置任何特别的配置文件或者将Acegi Security包含在服务器的类加载器(classloader)中。

 

上述的设计提供了最大的部署灵活性,你可以直接把目标工件(JAR, WAR 或者 EAR))直接从一个系统copy到另一个系统,它马上就可以运行起来。

 

2.2. 共享组件

让我们来看看Acegi Security中最重要的一些共享组件。所谓共享组件是指在框架中处于核心地位,系统脱离了它们之后就不能运行。这些Java类型代表了系统中其他部分的构建单元,因此理解它们是非常重要的,即使你不需要直接和它们打交道。

 

最基础的对象是SecurityContextHolder在这里存储了当前应用的安全上下文(security context),包括正在使用应用程序的principal的详细信息。SecurityContextHolder默认使用ThreadLocal来存储这些详细信息,这意味着即便安全上下文(security context)没有被作为一个参数显式传入,它仍然是可用的。如果在当前principal的请求处理后清理线程,那么用这种方式使用ThreadLocal是非常安全的。当然, Acegi Security自动为你处理这些,所以你无需担心。

 

有些应用程序由于使用线程的方式而并不是完全适用ThreadLocal。例如,Swing客户端可能需要一个Java Virtual Machine中的所有线程都使用同样的安全上下文(security context)。在这种情况下你要使用SecurityContextHolder.MODE_GLOBAL模式。另外一些应用程序可能需要安全线程产生的线程拥有同样的安全标识符security identity)。这可以通过SecurityContextHolder.MODE_INHERITABLETHREADLOCAL来实现。你可以通过两种方法来修改默认的SecurityContextHolder.MODE_THREADLOCAL。第一种是设置一个系统属性。或者,调用SecurityContextHolder的一个静态方法。大部分的应用程序不需要修改默认值,不过如果你需要,那么请查看SecurityContextHolderJavaDocs获取更多信息。

 

我们在SecurityContextHolder中存储当前和应用程序交互的principal的详细信息。Acegi Security使用一个Authentication对象来代表这个信息。尽管你通常不需要自行创建一个Authentication对象,用户还是经常会查询Authentication对象

 

你可以在你的应用程序中的任何地方使用下述的代码块:

java 代码
 
  1. Object obj = SecurityContextHolder.getContext().getAuthentication().getPrincipal();  
  2. if (obj instanceof UserDetails) {  
  3. String username = ((UserDetails)obj).getUsername();  
  4. else {  
  5. String username = obj.toString();  
  6. }  
 

上述的代码展示了一些有趣的联系和关键的对象。首先,你会注意到在SecurityContextHolderAuthentication之间有一个媒介对象。SecurityContextHolder.getContext() 方法实际上返回一个SecurityContextAcegi Security使用若干个不同的SecurityContext实现,以备我们需要存储一些和principal无关的特殊信息。一个很好的例子就是我们的Jcaptcha集成,它需要知道一个需求是否是由人发起的。这样的判断和principal是否通过认证完全没有关系,因此我们将它保存在SecurityContext中。

 

从上述的代码片段可以看出的另一个问题是你可以从一个Authentication对象中获取一个principalPrincipal只是一个对象。通常可以把它cast为一个UserDetails对象。UserDetailsAcegi Security中是一个核心接口,它以一种扩展以及应用相关的方式来展现一个principal。可以把UserDetails看作是你的用户数据库和Acegi SecuritySecurityContextHolder所需要的东西之间的一个适配器(adapter)。作为你自己用户数据库的一个展现,你可能经常要把它cast到你应用程序提供的原始对象,这样你就可以调用业务相关的方法(例如 getEmail(), getEmployeeNumber())

 

现在你可能已经开始疑惑,那我什么时候提供UserDetails对象呢?我要如何提供呢?

我想你告诉过我这个东西是声明式的,我不需要写任何Java代码-那怎么做到呢?对此的简短回答就是有一个叫做UserDetailsService的特殊接口。这个接口只有一个方法,接受一个Sring类型的参数并返回一个UserDetailsAcegi Security提供的大多数认证提供器将部分认证过程委派给UserDetailsServiceUserDetailsService用来构建保存在SecurityContextHolder中的Authentication对象。好消息是我们提供若干个UserDetailsService的实现,包括一个使用in-memory map和另一个使用JDBC的。

大多数用户还是倾向于写自己的实现,这样的实现经常就是简单的构建于已有的Data Access Object (DAO)上,这些DAO展现了他们的雇员、客户、或者其他企业应用程序中的用户。要记得这样做的好处,不论你的UserDetailsService返回什么,它总是可以从SecurityContextHolder中获取,象上面的代码显示的那样。

 

除了principalAuthentication提供的另一个重要方法就是getAuthorities()。这个方法返回一个GrantedAuthority对象数组。GrantedAuthority,毫无疑问,就是授予principal的权限。这些权限通常是“角色”,例如ROLE_ADMINISTRATOR 或者ROLE_HR_SUPERVISOR这些角色稍后配置到web授权,方法授权和领域对象授权。Acegi Security的其他部分能够处理这些权限,并且期待他们被提供。通常你会从UserDetailsService中返回GrantedAuthority对象。

 

通常GrantedAuthority对象都是应用范围的权限。它们都不对应特定的领域对象。因此,你应该不会有一个代表54号员工对象的GrantedAuthority因为这样会有数以千计的authority,你马上就会用光所有内存(或者,至少会让系统花太长时间来认证一个用户)。当然,Acegi Security会高效的处理这种普遍的需求,但是你不会使用领域对象安全功能来实现这个目的。

 

最后,但不是不重要,你有时候需要在HTTP 请求之间存储SecurityContext。另外有些时候你在每次请求的时候都会重新认证principal,不过大部分时候你会存储SecurityContextHttpSessionContextIntegrationFilterHTTP之间存储SecurityContext。正如类名字显示的那样,它使用HttpSession来进行存储。基于安全原因,你永远都不要直接和HttpSession交互。没有理由这么做,所以记得使用SecurityContextHolder来代替。

 

让我们回忆一下,Acegi Security的基本组成构件是:

SecurityContextHolder,提供对SecurityContext的所有访问方式。

SecurityContext, 存储Authentication以及可能的请求相关的安全信息。

HttpSessionContextIntegrationFilter, web请求之间把SecurityContext存储在HttpSession中。

Authentication, Acegi Security的方式表现principal

GrantedAuthority, 表示赋予一个principal的应用范围的权限。

UserDetails, 为从你的应用程序DAO中获取必要的信息来构建一个Authentication 对象。

UserDetailsService,用传入的String类型的username(或者认证ID,或类似)来创建一个UserDetails

 

现在你已经理解了这些重复使用的组件,让我们仔细看看认证过程吧。

 

2.3. 认证

正如我们在手册开始部分所说的那样,Acegi Security适用于很多认证环境。虽然我们建议大家使用Acegi Security自身的认证功能而不是和容器管理认证(Container Managed Authentication)集成,但是我们仍然支持这种和你私有的认证系统集成的认证。让我们先从Acegi Security完全自行管理管理web安全的角度来探究一下认证,这也是最复杂和最通用的情形。

 

想象一个典型的web应用的认证过程:

 

1.你访问首页,点击一个链接。

2.一个请求被发送到服务器,服务器判断你是否请求一个被保护的资源。

3.因为你当前未被认证,服务器发回一个回应,表明你必须通过认证。这个回应可能是一个HTTP回应代码,或者重定向到一个特定的网页。

4.基于不同的认证机制,你的浏览器会重定向到一个网页好让你填写表单,或者浏览器会用某种方式获取你的身份认证(例如一个BASIC认证对话框,一个cookie,一个X509证书等等。)。

5.浏览器会发回给服务器一个回应。可能是一个包含了你填写的表单内容的HTTP POST,或者一个包含你认证详细信息的HTTP header

6.接下来服务器会判断提供的认证信息是否有效。如果它们有效,你进入到下一步。如果它们无效,那么通常请求你的浏览器重试一次(你会回到上两步)。

7.你引发认证的那个请求会被重试。但愿你认证后有足够的权限访问那些被保护的资源。如果你有足够的访问权限,请求就会成功。否则,你将会受到一个意味“禁止”的HTTP403错误代码。

 

Acegi Security中,对应上述的步骤,有对应的类。主要的参与者(按照被使用的顺序)是:ExceptionTranslationFilter AuthenticationEntryPoint 认证机制(authentication mechanism) 以及AuthenticationProvider

 

ExceptionTranslationFilterAcegi Security用来检测任何抛出的安全异常的过滤器(filter)。这种异常通常是由AbstractSecurityInterceptor抛出的,它是授权服务的主要提供者。我们将会在下一部分讨论AbstractSecurityInterceptor现在我们只需要知道它产生Java异常,并且对于HTTP或者如何认证一个principal一无所知。反而是ExceptionTranslationFilter提供这样的服务,它负责要么返回403错误代码(如果principal通过了认证,只是缺少足够的权限,象上述第7步那样),要么加载一个AuthenticationEntryPoint (如果principal还没有被认证,那么我们要从第3步开始)

 

AuthenticationEntryPoint负责上述的第3步。如你所想,每个web应用都有一个默认的认证策略(象Acegi Security中几乎所有的东西一样,它也是可配置的,不过我们现在还是还是从简单开始)。每个主流的认证系统都有它自己的AuthenticationEntryPoint实现,负责执行第3步中描述的动作。

 

当浏览器确定要发送你的认证信息(HTTP 表单或者HTTP header),服务器上需要有什么东西来“收集”这些认证信息。现在我们在上述的第6步。在Acegi Security中对从用户代理(通常是浏览器)收集认证信息有一个特定的名字,这个名字是“认证机制(authentication mechanism)”。当认证信息从客户代理收集过来以后,一个“认证请求(Authentication request)”对象被创建,并发送到AuthenticationProvider

 

Acegi Security中认证的最后一环是一个AuthenticationProvider非常简单,它的职责是取用一个认证请求(Authentication request)对象,并且判断它是否有效。这个provider要么抛出一个异常,要么返回一个组装完毕的Authentication对象。还记得我们的好朋友UserDetails UserDetailsService吧?如果没有,回到前一部分重新回忆一下。大部分的AuthenticationProviders都会要求UserDetailsService提供一个UserDetails对象。如前所述,大部分的应用程序会提供自己的UserDetailsService,尽管有些会使用Acegi Security提供的JDBC或者 in-memory实现。作为成品的UserDetails 对象,特别是其中的GrantedAuthority[]s,在构建完备的Authentication对象时会被使用。

 

当认证机制(authentication mechanism)取回组装完全的Authentication对象后,它将会相信请求是有效的,将Authentication放到SecurityContextHolder中,并且将原始请求取回(上述第7步)。反之,AuthenticationProvider则拒绝请求,认证机制(authentication mechanism)会请求用户重试(上述第2步)。

 

在讲述典型的认证流程的同时,有个好消息是Acegi Security不关心你是如何把Authentication放到SecurityContextHolder内的。唯一关键的是在AbstractSecurityInterceptor授权一个请求之前,在SecurityContextHolder中包含一个代表了principalAuthentication

 

你可以(很多用户确实)实现自己的过滤器(filter)或者MVC控制器(controller)来提供和不是基于Acegi Security的认证系统交互。例如,你可能使用使用容器管理认证(Container Managed Authentication),从ThreadLocal 或者JNDI中获取当前用户信息,使得它有效。或者,你工作的公司有一个遗留的私有认证系统,而它是公司“标准”,你对它无能为力。在这种情况之下也是非常容易让Acegi Security运作起来,提供认证能力。你所需要做的是写一个过滤器(或等价物)从某处读取第三方用户信息,构建一个Acegi Security式的Authentication对象,把它放到SecurityContextHolder中。这非常容易做,也是一种广泛支持的集成方式。

acegi参考手册(v1.0.4)[译]-第一章 简介

关键字: 企业应用   acegi 安全 翻译     

Part I. 架构概览

象其他的软件一样,Acegi Security也有在整个框架中都会使用的特定核心接口,类,和概念抽象。在手册的这一部分,在检视这些规划和执行Acegi Security集成所必须的核心要素之前,我们先介绍Acegi Security

第一章. 简介

1.1. Acegi Security是什么?

Acegi Security为基于J2EE的企业软件应用提供全面的安全服务。特别是使用领先的J2EE解决方案-Srping框架开发的项目。如果您不是使用Spring开发企业应用,我们温馨提醒您仔细研究一下。熟悉Spring,尤其是依赖注射原理,会极大的帮助你快速掌握Acegi Security

人们使用Acegi Security有很多种原因,不过通常吸引他们到这个项目的原因是他们在J2EE Servlet Specification 或者 EJB Specification中找不到迫切需要的典型企业应用场景。提到这些规范,特别要提出的是他们不是在WAR或者EAR级别可移植的。这样,如果你切换服务器环境,一般来说你要在目标环境中花费很多工夫来重新配置你的应用安全。使用Acegi Security解决了这些问题,并且为你提供了很多其他有用的,完全可定制的安全特性。

如你所知,安全包含两个主要操作。第一个被称为“认证”,是为用户建立一个它所声明的principalPrincipal通常代表用户,设备,或者其他能在你的应用中执行操作的其他系统。“授权”指判定一个principal能否在你的系统中执行某个操作。在到达授权判断之前,principal的的身份认证已经由认证过程执行过了。这些概念是通用的,不是Acegi Security特有的。

在认证层面,Acegi Security广泛支持各种认证模块。这些认证模块绝大多数是第三方提供,或者相关的标准组织开发的,例如Internet Engineering Task Force。作为补充,Acegi Security自己也提供了一些认证功能。Acegi Security当前支持如下的认证技术。

• HTTP BASIC authentication headers (an IEFT RFC-based standard)

• HTTP Digest authentication headers (an IEFT RFC-based standard)

• HTTP X.509 client certificate exchange (an IEFT RFC-based standard)

• LDAP (a very common approach to cross-platform authentication needs, especially in large environments)

• Form-based authentication (for simple user interface needs)

• Computer Associates Siteminder

• JA-SIG Central Authentication Service (otherwise known as CAS, which is a popular open source single sign on system)

• Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (a Spring remoting protocol)

• Automatic "remember-me" authentication (so you can tick a box to avoid re-authentication for a predetermined period of time)

• Anonymous authentication (allowing every call to automatically assume a particular security identity)

• Run-as authentication (which is useful if one call should proceed with a different security identity)

• Java Authentication and Authorization Service (JAAS)

• Container integration with JBoss, Jetty, Resin and Tomcat (so you can still use Container Manager Authentication if desired)

你自己的认证系统 (如下所示)

很多独立软件供应商(ISVs)选择Acegi Security是因为它具有丰富的认证模块。这样无论他们的终端客户需要什么,他们都可以快速集成到他们的系统中,不用花很多工夫或者让终端客户改变环境。如果Acegi Security System for Spring7个认证模块还没有满足你的需求的话,Acegi Security是一个开放的系统,很容易写你自己的认证机制。许多Acegi Security的企业用户需要和“遗留”系统集成,这些遗留系统不遵循任何安全标准,Acegi Security能够和这样的系统“合作愉快”。

有时候基本的认证是不够的。有时候你需要根据principal和应用交互的方式来应用不同的安全措施。例如,你可能为了防止密码被窃取,或者防止终端用户受到“中间人”攻击,需要保证到达的是请求通过HTTPS的。或者,你要确保是一个真正的人而不是某种机器人或者自动进程在发送请求。这对于保护密码恢复不受暴力破解攻击,或者防止他人很容易的复制你应用的关键内容。为了帮助你实现这些目标,Acegi Security完全支持自动“通道安全”("channel security"),以及集成Jcaptcha来检测是否是真正人类用户。

Acegi Security不仅提供了认证功能,而且提供了完备的授权功能。在授权方面主要有三个领域,授权web请求,授权方法调用,授权存取单个领域对象实例。为了帮助你理解这些区别,对照考虑一下Servlet 规范中的web模式安全的授权功能,EJB容器管理安全以及文件系统安全。Acegi Security提供了所有这些重要领域的完备功能,我们将在本手册的后面介绍。

1.2. 历史

Acegi Security始于2003年晚期,当时在Spring Developers邮件列表中有人提问是否有人考虑提供一个基于Spring的安全实现。当时,Srping的社区是相对比较小的(尤其是和今天相比!),实际上Spring本身也是2003年早期才作为一个SourceForge项目出现的。对此问题的回应是它确实是一个值得研究的领域,虽然限于时间无法进行深入。

有鉴于此,这个简单的安全实现虽然构建了但是并没有发布。几周以后,Spring社区的其他成员询问了安全框架,代码就被提供给了他们。

随后又有人请求,到了2004年一月,大约有20人左右在使用这些代码。另外一些人加入到这些先行的用户中来,并建议建立一个SourceForge项目,这个项目在20043月建立起来。

在早期,该项目自身并布具备任何认证模块。认证过程依赖容器管理安全(Container Managed Security)而Acegi Security注重授权。在一开始这样是合适的,但是随着越来越多的用户要求额外的容器支持,基于容器的认证的限制就显示出来了。另外一个相关的问题是添加新的JAR文件到容器的classpath,通常会让最终用户感到困惑并且配置错误。

随后,Acegi Security加入了认证服务。大约一年后,Acegi Security成为了一个Spring Framework官方子项目。在2年半多的在多个软件项目中的活跃使用以及数以百计的改进和社区贡献,1.0.0最终版在20065月发布。

今天,Acegi Security成为一个强大而活跃的社区。在支持论坛上有数以千计的帖子。14位开发人员专职开发,一个活跃的社区也定期共享补丁并支持他们的同侪。


1.3. 发行版本号

理解Acegi Security的版本号是非常好处的,它可以帮助你判定升级的到新的版本是否需要花费很大精力。我们的正式发行版本使用Apache Portable Runtime Project版本指引,可以在下述网站查看http://apr.apache.org/versioning.html为了您查看方便,我们引用该页的说明部分如下:

“版本号由三个部分的整数组成:主版本号(MAJOR)、副版本号(MINOR)、补丁版本号(PATCH)。主要的含义是主版本号(MAJOR)是不兼容的,API大规模升级。副版本号(MINOR)在源文件和可执行版和老版本保持兼容,补丁版本号(PATCH)则意味着向前和向后的完全兼容”。

2/27/2007

美物理学家称找到观察六维空间方法

  六维世界是想象还是存在

  有科学家称,生活在三维空间和时间中的人类至今不知还有另外六个空间维度

  -文/本报记者 唐逸 吴洣麓

  威斯康星大学麦迪逊分校的一位物理学家寻找到了观察六维空间的灵感。他提出的观察六维形状的方法被发表在本月的《物理评论快报》上。

  除了四维时空,另有六个人类未知的空间维度

  我们都知道,自己生活在三维空间之中,如果加上时间,那么是四维时空。可有科学家称,还有另外六个空间维度是人类至今不知的。

  来自2007年2月2日的《物理评论快报》的一则消息称:威斯康星大学麦迪逊分校的一位物理学家从太空中寻找灵感,提出了这样的一个假设,在物理学“弦论”的基础下,人类的世界并不完整。除了三维空间和时间之外,还应该存在另外六个空间维度。这些“隐藏”的空间维度以极其微小的几何形状卷曲在我们宇宙的每一个点中。六维空间可以接纳任何可能的形状,而且都与其自身的世界相一致,具有其自身的物理学规律。

  这无疑像一颗重磅炸弹落在物理学界。如果真的有六维空间存在,那么爱因斯坦的“相对论”就显示了其理论自身的不完善。

  对于人类而言,我们习惯了三维空间的概念,如何能想象和接受六维空间?这神秘的六维几何体到底是怎么样的形状?难以捉摸的六维空间确实存在吗?

  人类为什么看不见其它六维

  中国科学院理论物理所朱传界教授告诉记者,“宇宙应该是十维的”是根据一种超弦理论的论证,科学家通过数学方程计算得出的结论。就目前而言,人们只了解一维时间、二维平面、三维空间以及爱因斯坦提及的“四维时空”概念。除此之外,“超弦理论”预测还应该存在另外六个人类未知的空间维度。

  那为什么另外六个空间维度看不见呢?

  朱教授以水管为例说,当人们站在这根水管的正面看时,水管就是一条直线,我们就只看到了它的前后,它就是一维的。当人们站在一个平面里,看这根水管,就能看到水管的上下左右,那么人们就看到了它就是二维的。当人们在一个立体的空间里看这个水管,它的前后、左右、上下都收纳在我们的眼中,那么它就是三维的。

  可如果人们把这根水管放在两维的平面中,然后又把这个两维的平面放在三维空间中,那么会是什么样的呢?于是,科学家把水管想象成像一根头发丝那样细。科学家认为,六个“隐藏”的空间维度,以极其微小的几何形状,卷曲在我们宇宙的每一个点中。

  这种观察六维形状的方法之所以被发表在《物理评论快报》上,是因为这种方法能证明通过实验数据来观察这些难以捉摸的维度形状特征是可行的。同时,六维空间的存在也是证实“超弦理论”的主要方面。

  ◆观点交锋

  六维空间究竟存不存在

  从广袤星系到亚原子微粒,“超弦理论”囊括了所有物体的物理学规律。几十年来,关于“超弦理论”,很多科学家都争论不休,赞同的、反对的,各种声音都有。

  拥护者:

  没有一个意见能够反驳

  不少超弦理论的拥护者表示,目前还没有一个持反对意见者能驳倒它。一旦验证“超弦理论”是正确的,那么人们就能通过解密它们对130亿年前宇宙大爆炸释放的宇宙能有所了解,借助时间机器,穿越黑洞后,“看见”神秘的六维几何体。

  “不过,你也不用为看不见十维的世界而感到担忧。”威斯康星大学麦迪逊分校的这位物理学家说,“因为我们的大脑习惯于只是三维的空间,而对于其他六维空间结构却很难感知。虽然科学家们利用计算机模拟出了类似的六维几何体,但没有人能够确切地知道他们的形状到底是怎么样的。”

  他说,“我们的想法就是回到那个时候看看到底发生了什么事情,当然我们不可能真的回去。”

  很多科幻爱好者都梦想着搭乘时间机器遨游时空,有些科学家也尝试着用最新的原理来证明时间旅行的可行性,也试着用“超弦理论”来讨论它。

  因为缺少必要的时间机器,他们使用了另外一个最好的东西,一幅宇宙大爆炸释放的宇宙能量图。这种爆炸释放的能量在随后的130亿年里其实都没有发生变化,它可以被卫星捕捉到,比如美国的威尔金森微波各向异性探测器。通过绘制出宇宙能量图可以帮助人类对宇宙的雏形有一个大概的印象。

  反对者:

  六维空间仅是人为想象

  “对超弦理论,我不敢兴趣。”记者在采访中国科学院院士何祚庥时,他明确表示,这仅仅是人为的想象推断,根本没有讨论的必要和研究价值。

  “我个人反对弦论研究者用这样肯定的口气说话。也许我们真的掌握了部分真理,也许我们一直以来仅仅是研究一个针尖上能有多少天使跳舞。”中国科学院理论物理所研究员李淼在其个人博客这样说道。

  ◆背景知识

  神奇的万有理论

  中国科学院物理所研究员李淼撰文的《弦论史话》中介绍,所谓“超弦理论”,又叫“弦论”或“万有理论”。

  据李淼介绍,现代弦论的创始人之一是在加州理工学院工作的物理学家史瓦兹,他“十年如一日”,将只有几个人知道的理论做成如今有数千人研究的学问。史瓦兹也因此得以摆脱三年延长一次的位置,终于成了加州理工学院的正教授。

  1968年,一位在麻省理工学院工作的意大利物理学家威尼采亚诺翻了翻数学手册,发现一个简单的函数满足对偶性,这就是著名的威尼采亚诺公式。而超弦理论正是起源于这个公式。

  超弦理论认为,在每一个基本粒子内部,都有一根细细的线在振动,就像琴弦的振动一样,因此这根细细的线就被科学家形象地称为“弦”。我们知道,不同的琴弦振动的模式不同,因此振动产生的音调也不同。类似的道理,粒子内部的弦也有不同的振动模式,不过这种弦的振动不是产生音调,而是产生一个个粒子。换言之,每个基本粒子是由一根弦组成。

  超弦理论认为,粒子并不存在,存在的只是弦在空间运动;各种不同的粒子只不过是弦的不同振动模式而已。自然界中所发生的一切相互作用,所有的物质和能量,都可以用弦的分裂和结合来解释。

  弦的运动是非常复杂,以至于三维空间已经无法容纳它的运动轨迹,必须有高达十维的空间才能满足它的运动,就像人的运动复杂到无法在二维平面中完成,而必须在三维空间中完成一样。

2/24/2007

毕业后你对母校还有感情吗?(zz)

发信人: nid (春秋大梦), 信区: EduReform
标  题: 毕业后你对母校还有感情吗?
发信站: 两全其美网 (Wed Feb 21 23:47:00 2007), 本站(lqqm.net)

毕业后你对母校还有感情吗?

“留恋在学校里同甘共苦的朋友,但对学校的感情不是很深。”在近期中国青年报社会调查
中心与某网站合作的调查(1146人参与)中,有网友这样留言。

面对“你觉得大学毕业之后自己和母校还有关系吗”的提问,回答“毕业后就没什么关系了
”(35.3%)和“说不清”的就占了49.0%,而“一直在关注母校发展”的占51.0%,两种
声音旗鼓相当。

“记得当初我们上学那会儿,有句口号叫‘今天我以北大为荣,明天北大以我为荣’。”黄
先生告诉记者,1978年恢复高考的第二年,他就考上了北京大学,从黑龙江生产建设兵团一
下子就回到了北京,别提多自豪了。后来毕业分配工作,一直到现在即将退休,4年的大学
生活都带给他不少荣耀。“那个年代,只要一提起是大学生,还是北大的,大家都会很尊重
。何况,我这辈子赖以生存的看家本事就是当年在大学里学的。”

北大百年校庆,黄先生特意请了一天假,带着正在上高中的女儿一起,回到母校参加校友活
动,逛逛校园、看看老师,再和当年的同学一起抚今追昔一番。“我们同学啊,到现在都还
一口一个北大的,真是把学校当成自己的家了。还有那些老先生(老师),也跟自己家里人
似的,多少年之后再见面还是觉得特亲。”当时,老同学们拉着黄先生的女儿一个劲儿鼓励
:“努力努力,回头也考北大,跟你爸当校友。”

2000年秋天,小黄考上了另外一所知名大学,现在也已经毕业了。同父亲的自豪不同,每次
说起老爸对母校的感情,她却是一脸的不理解:“至于嘛,不都毕业那么多年了。”

在小黄和她的同学们看来,自打拿到毕业证那一刻起,自己就跟学校一点儿关系都没有了。
“我交学费,学校交给我知识,天经地义,没什么可感激的。况且,我也没觉得我学到了什
么有用的东西。”刚走出校门那一段时间,只要一提起自己的母校,小黄总是以“我们那烂
学校”开头。“我从来都不以母校为荣。”小黄说。

有这样感觉的年轻人并不是少数。调查显示,在描述对大学母校的印象时,除了留恋和感恩
之外,“失望”、“和想象中不一样”、“没感觉”、“可算离开了”这样的词汇使用率相
当高,甚至16.2%的人干脆用了一个“烂”字来形容。

让老一辈大学生留恋终生的大学校园,为什么在现在的大学毕业生眼中变得如此不堪?

调查中,有31.3%的人认为“教育产业化让自己有被盘剥的感觉”,25.1%的人觉得“升学
、就业的压力导致自己和老师同学交流变少”,17.8%的人觉得“老师教课心不在焉”,1
7.2%的人则明确地说“母校不再有母亲一样温暖的感觉了”。

“从我自己的调查来看,这些年大学毕业生对母校的感情确实存在越来越淡的趋势。”高等
教育问题研究学者熊丙奇说。

熊丙奇认为,从大的意义看,学生对母校的感情就是对教育的感情。中国的高等教育分解在
1000多所高校中,学生在这1000多所高校中能学到什么、感受到什么样的学习氛围、遇到什
么样的老师,都会影响到学生对母校、对教育的感情。

“目前的教育让学生对任何人都淡漠,都功利。”熊丙奇认为,我国的家庭教育、学校教育
让孩子只关心自己,只会享受父母无偿的关爱。“因为我们的教育一直都在告诉孩子,只需
要把学习搞好就行。”他认为,现在的教育中普遍缺乏对做人、处世的教育和责任心的培养
。这种淡漠造成整个社会的功利心态。

熊丙奇分析说,目前看来,在基础教育阶段,联系学生和家长的就是钱、分数,在高等教育
阶段,联系学生和老师的就是课时、就业率。除此之外再没有别的联系。

“这些年,受教育产业化的影响,高校的学费越来越高,扩招后师资力量反而不足,学校对
学生的培养也不再像以前那么精细。再加上高校教育从精英教育走向大众教育,更加让学生
感觉不好。从2002年以来,官方发布的高校就业率保持在70%,那就意味着每年至少有30%
左右的大学生不能顺利就业,他们对母校怎么可能有好的印象?”

“学生对学校的感情更多来自老师。”熊丙奇说,现在不论是对本科生还是对研究生,很多
老师上完课就走,跟学生的交流越来越少。不要说教学生做人,就连最基本的教学都很难保
证质量。

过去的大学生对母校有感情,是因为对老师有感情。现在的学生回母校就是为了同学聚会,
甚至在同学聚会的时候都不会邀请当年教过他们的老师。

“因为很多大学生都觉得在大学里没有遇到真正的好老师,没有从老师那里学到什么。”熊
丙奇说。

有一位作家在给《大学有问题》一书作序时曾说:“让我们回忆一下自己读书期间所遇到的
教师,有多少给我们留下深刻的印象?有多少值得我们钦佩?我接触过不少做人做事都很成
功的人,他们都会谈到自己曾遇到好教师;我还接触过另外一些人,他们在回忆过去时,总
觉得最大的遗憾就是没有得到好教师的教诲。事实上,我们国家能够永留学生记忆中的好教
师,真是太少了,按照我的不大完整的估算,不到总数的5%。”

对于5%的说法,曾有人指责这位作家只是估算,并不是“科学的调查”。“结合我的所见
所闻,再估算一下,大学里,真正的好导师,今日绝对不超过总数的5%。”熊丙奇也对这
个说法表示赞同。

记者了解到,某发行量不小的报社曾在教师节前搞了一次“回忆我的母校”征文活动。让组
织者没想到的是,该征文发出一个月后,只收到了5篇来稿,让人哭笑不得。

“不以母校为荣,就不会以自己曾经生活过的文化环境为荣,也就没有了根。”熊丙奇说。
1/29/2007

I like programming, 努力奋斗

做自己喜欢的事,让别人说去吧
1/26/2007

起了个英文名字

昨天晚上我hold 完一个meeting后,manager说我的名字鬼子拼不出来,让我最好起一个英文名字,要不就叫Stone.可是俺不想做石头啊。今天林博士给我想了个Chiton,一查,气死我了,原来还有石鳖的意思,咱当不了海龟,也不能做土鳖王八不是。又找了个chito,据说是芒果的意思。俺可不是吃的啊。Stune,没这个单词。想了半天,还是叫Strong好了。网上一搜,老外还真有叫Strong ***的,行,咱就叫Strong, Yan了。
Master, Doctor, "壮士"。
mentor(Doctor)提议我也可以叫“圣斗士”,谁知道圣斗士怎么翻译啊。
 
1/24/2007

最近不是很爽

1、白天工作,晚上还要做毕业论文工作,一整天都是昏昏沉沉的,眼睛酸死了,身体整天处于亚健康状态。
2、跟鬼子电话会议,除了能听懂leader的chinese english,鬼子的话顶多能听懂一半,更别提自己的哑巴英语了,到口就忘词。碰上个不体贴中国同事的米国鬼子,说话跟机关枪一样,谁都干瞪眼。
3、公司扩照厉害,搞得自己很没有成就感,可是又没有更牛的地方可去或者说自己能力不足。不过仔细想想自己除了某方面技术强,J2EE在南开天大在校生内(定语,早就工作了的不算,省得别人说我不谦虚)能排个top1/2/3之外,其它也没啥可圈可点的地方,进现在的公司算最为match的。ms,google可能更好一些,但自己的技术方向,与之相差甚远,被拒也是顺理成章。原本心仪的unix技术,早被一大堆MIS/WEB系统废掉,从本科时候同龄人中的一流,彻底跌进现在的三流甚至不入流。C/C++还算凑合,就是一直没机会做个大点的项目实践一下,书倒是看了n多,可是除了用MFC的一小部分应付一下作业,就没啥值得一提的地方了。至于英语,因为本科时候那个烂化学专业实在闹心,除了加班加点把自己计算机方面的能力提高之外,还要应付什么有机化学、无机化学、物理化学之类无聊的考试,实在没有精力念什么abcd,把四六级通过也算完事大吉。
4、现在想想,也许走考试这条路也许省事一些,把耗在学技术的时间用在考研之类的事情上,凭自己的智力弄个清北的研究生应该不是什么难事,不过自己对于技术过分执着和热爱,真是不太愿意把时间浪费在那些无聊的玩意上。也许另一个平行世界的我走得就是这条路,maybe混得比这个世界的我好吧。未来的事,很难说,走一步看一步吧。毕竟现在个人的爱好和找到的工作,没有矛盾,这辈子还是做自己喜欢做的事情吧。
5、快奔三的银了,还是一个孤独的北漂者。人真是一个社会性的动物,天生害怕孤独。每次参加婚宴都是个送钱的,希望早日找到自己的另一半吧。
6、还没毕业,就得还银行贷款。其实本来做intern,用不着天天去的,可是又有什么办法,像北京的那帮孩子们三天两头不去的话,抛去房租、吃饭、还贷,收入就是负的了。没有正式on board之前甚至on board 后的几个月内,我就是一个负债两万多的穷光蛋。
7、每次老娘打来电话,总是一成不变的嘱托。尽管北京离家很近,但有时候真是没胆量回家,怕看到母亲粗糙的双手、越来越多的白发、日渐差劲的听力,怕看到父亲日渐苍老的容颜、额头深深的皱纹、佝偻瘦弱的身躯。我生活在一个靠吸血畸形发展的大城市,天天看着车水马龙、灯红酒绿。而我的父母,现在还生活在那个贫困的小县城,住在一个被遗忘的角落过着清贫的生活。他们为国家贡献了半辈子的青春岁月,老了却被这个国家所抛弃。而他们唯一的儿子,除了能够做到不剥削父母之外,一个快到而立之年的所谓男子汉,竟然根本没有能力让父母的生活好过一些。读这个狗屁研究生,真是不孝。
8、为了自己,也为了养育自己的亲人,为了以后可能出现的那个她,努力奋斗,拼命挣钱。平时喜欢嘲笑我财迷的那帮烂人,你们自省去吧,不是老爹老娘供着你,你算个屁呀。请理解一个草根努力改变命运的奋斗吧。