/**
* 从数据库中读取菜单配置信息
*
* @return
*/
private Map[] getMenuComponents() {
ArrayList list = new ArrayList();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rest = null;
String sql = "select name,parent_name,title,location,description from menu_item order by id";
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:mysql://localhost/myexamples?user=root&password=mywangya&useUnicode=true&characterEncoding=UTF-8");
pstmt = conn.prepareStatement(sql);
rest = pstmt.executeQuery();
while (rest.next()) {
int i = 1;
HashMap map = new HashMap();
map.put("name", rest.getString(i++));
map.put("parent_name", rest.getString(i++));
map.put("title", rest.getString(i++));
map.put("location", rest.getString(i++));
map.put("description", rest.getString(i++));
list.add(map);
}
} catch (SQLException ex) {
ex.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (null!=rest) rest.close();
if (null!=pstmt) pstmt.close();
if (null!=conn) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return (Map[]) list.toArray(new HashMap[0]);
}
/**
* 构造菜单权限
*
* @param request
*/
private void buildMenuPermissions(HttpServletRequest request) {
PermissionsAdapter permession = new PermissionsAdapter() {
public boolean isAllowed(MenuComponent menu) {
// 名称等于StandaloneMenu的菜单不显示
return !"StandaloneMenu".equalsIgnoreCase(menu.getName());
}
};
request.setAttribute("examplesPermession", permession);
}
/**
* 构造菜单显示标题
*
* @param request
*/
private void buildMenuResourceBundle(HttpServletRequest request) {
MenuResourceBundle resourceBundle = new MenuResourceBundle();
request.setAttribute("examplesBundle", resourceBundle);
}
/**
* MenuResourceBundle树状菜单国际语言显示
*
* @author wenbin.zhang
*
*/
class MenuResourceBundle extends ListResourceBundle {
private ArrayList list = new ArrayList();
public MenuResourceBundle() {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rest = null;
String sql = "select title,titleCN from menu_item order by id";
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:mysql://localhost/myexamples?user=root&password=mywangya&useUnicode=true&characterEncoding=UTF-8");
pstmt = conn.prepareStatement(sql);
rest = pstmt.executeQuery();
while (rest.next()) {
int i = 1;
String[] message = new String[2];
message[0] = rest.getString(i++);
try {
message[1] = new String(rest.getString(i++).getBytes("latin1"), "gbk");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (message[0] != null && message[1] != null) {
list.add(message);
}
}
} catch (SQLException ex) {
ex.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (null!=rest) rest.close();
if (null!=pstmt) pstmt.close();
if (null!=conn) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public Object[][] getContents() {
return (String[][]) list.toArray(new String[0][0]);
}
}
}
> 将struts-config.xml文件的<action-mappings />部分修改为:
<action-mappings>
<action path="/menuAction" type="cn.appex.menu.MenuAction" >
<forward name="success" path="/struts-menu/dynamic-menu.jsp" />
</action>
</action-mappings>
插入测试数据:
INSERT INTO menu_item
(id, parent_name, name, title, titleCN, location)
VALUES
(1,null,'DatabaseMenu','Database Menu','数据库动态菜单',null),
(2,'DatabaseMenu','Yahoo','Yahoo Mail','雅虎邮件','http://mail.yahoo.com'),
(3,'DatabaseMenu','JavaBlogs','JavaBlogs','Java博客','http://javablogs.com'),
(4,null,'StandaloneMenu','Standalone Menu','独立的菜单','http://www.sohu.com')
> 将数据库驱动程序放到lib目录中,并加入到Build Path,如MySQL的数据库驱动mysql-connector-java-3.1.8-bin.jar,解压缩commons- collections-3.1.zip,将commons-collections-3.1.jar释放到lib目录,并加入Build Path
> 新建一个Struts的Action,代码如下:
package cn.appex.menu;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ListResourceBundle;
import java.util.Map;
import com.mysql.jdbc.Driver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import net.sf.navigator.menu.MenuComponent;
import net.sf.navigator.menu.MenuRepository;
import net.sf.navigator.menu.PermissionsAdapter;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
public class MenuAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
buildMenuRepository(request);
buildMenuResourceBundle(request);
buildMenuPermissions(request);
return (mapping.findForward("success"));
}
/**
* 创建菜单数据结构
*
* @param request
*/
private void buildMenuRepository(HttpServletRequest request) {
MenuRepository repository = new MenuRepository();
// Get the repository from the application scope - and copy the
// DisplayerMappings from it.
MenuRepository defaultRepository = (MenuRepository) request
.getSession().getServletContext().getAttribute(
MenuRepository.MENU_REPOSITORY_KEY);
repository.setDisplayers(defaultRepository.getDisplayers());
Map[] menus = getMenuComponents();
for (int i=0; i < menus.length; i++) {
MenuComponent mc = new MenuComponent();
Map row = menus[i];
String name = (String) row.get("name");
mc.setName(name);
String parent = (String) row.get("parent_name");
System.out.println(name + ", parent is: " + parent);
if (parent != null) {
MenuComponent parentMenu = repository.getMenu(parent);
if (parentMenu == null) {
System.out.println("parentMenu '" + parent + "' doesn't exist!");
// create a temporary parentMenu
parentMenu = new MenuComponent();
parentMenu.setName(parent);
repository.addMenu(parentMenu);
}
mc.setParent(parentMenu);
}
String title = (String) row.get("title");
mc.setTitle(title);
String location = (String) row.get("location");
mc.setLocation(location);
String description = (String) row.get("description");
mc.setDescription(description);
repository.addMenu(mc);
}
request.setAttribute("examplesRepository", repository);
}
4:解压缩struts-menu-2.3.zip,将压缩包中的jstl-1.0.6.jar、standard-1.0.6.jar、struts- menu-2.3.jar释放到lib目录中,将压缩包中的struts-menu.tld、struts-menu-el.tld释放到web\WEB -INF目录中,解压缩commons-lang-2.1.zip,将commons-lang-2.1.jar解压缩到lib目录中,注意,在 Struts-Menu的文档中没有看到需要这个包,但是没有这个包却无法成功加载。在web\WEB-INF目录中新建menu- config.xml,内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<MenuConfig>
<Displayers>
<Displayer name="DropDown"
type="net.sf.navigator.displayer.DropDownMenuDisplayer"/>
<Displayer name="Simple"
type="net.sf.navigator.displayer.SimpleMenuDisplayer"/>
<Displayer name="CoolMenu"
type="net.sf.navigator.displayer.CoolMenuDisplayer"/>
<Displayer name="CoolMenu4"
type="net.sf.navigator.displayer.CoolMenuDisplayer4"/>
<Displayer name="MenuForm"
type="net.sf.navigator.example.PermissionsFormMenuDisplayer"/>
<Displayer name="ListMenu"
type="net.sf.navigator.displayer.ListMenuDisplayer"/>
<Displayer name="TabbedMenu"
type="net.sf.navigator.displayer.TabbedMenuDisplayer"/>
<Displayer name="Velocity"
type="net.sf.navigator.displayer.VelocityMenuDisplayer"/>
</Displayers>
<Menus>
<Menu name="DoorSite" title="DoorSite" description="Some famous doorsite" width="50">
<Item name="Yahoo" title="Yahoo">
<Item name="YahooIndex" title="Yahoo Index" location="http://www.yahoo.com.cn"/>
<Item name="YahooMail" title="Yahoo Mail" location="http://cn.mail.yahoo.com"/>
</Item>
<Item name="Sohu" title="Sohu" location="http://www.sohu.com"/>
<Item name="Sina" title="Sina" location="http://www.sina.com.cn"/>
</Menu>
</Menus>
</MenuConfig>
5:将第四步的示例程序中的struts-menu应用目录下的images、scripts、styles三个目录中的内容复制到web目录下
6:你的目录结构应该类似如下:
%PROJECT_HOME%\classes
%PROJECT_HOME%\lib
%PROJECT_HOME%\lib\antlr.jar
%PROJECT_HOME%\lib\commons-beanutils.jar
%PROJECT_HOME%\lib\commons-digester.jar
%PROJECT_HOME%\lib\commons-fileupload.jar
%PROJECT_HOME%\lib\commons-lang-2.1.jar
%PROJECT_HOME%\lib\commons-logging.jar
%PROJECT_HOME%\lib\commons-validator.jar
%PROJECT_HOME%\lib\jakarta-oro.jar
%PROJECT_HOME%\lib\jstl-1.0.6.jar
%PROJECT_HOME%\lib\standard-1.0.6.jar
%PROJECT_HOME%\lib\struts.jar
%PROJECT_HOME%\lib\struts-menu-2.3.jar
%PROJECT_HOME%\src
%PROJECT_HOME%\src\log4j.properties
%PROJECT_HOME%\src\application.properties
%PROJECT_HOME%\src\application_zh_CN.properties
%PROJECT_HOME%\web
%PROJECT_HOME%\web\images\
%PROJECT_HOME%\web\scripts\
%PROJECT_HOME%\web\styles\
%PROJECT_HOME%\web\WEB-INF
%PROJECT_HOME%\web\WEB-INF\menu-config.xml
%PROJECT_HOME%\web\WEB-INF\struts-bean.tld
%PROJECT_HOME%\web\WEB-INF\struts-config.xml
%PROJECT_HOME%\web\WEB-INF\struts-html.tld
%PROJECT_HOME%\web\WEB-INF\struts-logic.tld
%PROJECT_HOME%\web\WEB-INF\struts-menu.tld
%PROJECT_HOME%\web\WEB-INF\struts-menu-el.tld
%PROJECT_HOME%\web\WEB-INF\struts-nested.tld
%PROJECT_HOME%\web\WEB-INF\struts-tiles.tld
%PROJECT_HOME%\web\WEB-INF\web.xml
六、实战Struts-Menu
1:使用配置文件实现静态菜单
新建JSP文件web/static-menu.jsp,内容如下:
<%@ page contentType="text/html; charset=GBK" %>
<%@ taglib uri="struts-menu" prefix="menu" %>
<menu:useMenuDisplayer name="ListMenu"
bundle="org.apache.struts.action.MESSAGE">
<menu:displayMenu name="DoorSite"/>
</menu:useMenuDisplayer>
运行Tomcat,在IE地址栏输入
http://localhost:8080/mymenu/static-menu.jsp查看
2:实现中文化
> 在src\application_zh_CN.properties中增加下面的内容,Unicode可以通过JDK自带的native2ascii工具得到:
#门户网站
menu.DoorSite=\u95e8\u6237\u7f51\u7ad9
#雅虎
menu.Yahoo=\u96c5\u864e
#雅虎首页
menu.YahooIndex=\u96c5\u864e\u9996\u9875
#雅虎邮件
menu.YahooMail=\u96c5\u864e\u90ae\u4ef6
#搜狐
menu.Sohu=\u641c\u72d0
#新浪
menu.Sina=\u65b0\u6d6a
> 在src\application.properties中增加下面的内容:
#门户网站
menu.DoorSite=DoorSite
#雅虎
menu.Yahoo=Yahoo
#雅虎首页
menu.YahooIndex=Yahoo Index
#雅虎邮件
menu.YahooMail=Yahoo Mail
#搜狐
menu.Sohu=Sohu
#新浪
menu.Sina=Sina
> 修改menu-config.xml文件<Menus></Menus>部分:
<Menus>
<Menu name="DoorSite" title="menu.DoorSite" description="Some famous doorsite" width="50">
<Item name="Yahoo" title="menu.Yahoo">
<Item name="YahooIndex" title="menu.YahooIndex" location="http://www.yahoo.com.cn"/>
<Item name="YahooMail" title="menu.YahooMail" location="http://cn.mail.yahoo.com"/>
</Item>
<Item name="Sohu" title="menu.Sohu" location="http://www.sohu.com"/>
<Item name="Sina" title="menu.Sina" location="http://www.sina.com.cn"/>
</Menu>
</Menus>
重起Tomcat,在IE地址栏输入
http://localhost:8080/mymenu/static-menu.jsp查看
3:结合数据库实现动态菜单
> 新建数据myexamples:
CREATE DATABASE myexamples
> 新建数据库表menu_item:
CREATE TABLE menu_item (
id BIGINT not null,
parent_name VARCHAR(30),
name VARCHAR(30),
title VARCHAR(30),
titleCN VARCHAR(30),
description VARCHAR(50),
location VARCHAR(255),
target VARCHAR(10),
onclick VARCHAR(100),
onmouseover VARCHAR(100),
onmouseout VARCHAR(100),
image VARCHAR(50),
altImage VARCHAR(30),
tooltip VARCHAR(100),
roles VARCHAR(100),
page VARCHAR(255),
width VARCHAR(5),
height VARCHAR(5),
forward VARCHAR(50),
action VARCHAR(50),
primary key (id)
)
2006-11-21 19:13
一、简介
Struts-Menu是一组从基于XML的配置文件中生成多种样式的菜单的JSP Tags,并且可以结合API开发通过数据库生成的动态菜单。Struts-Menu支持国际化和多种权限控制。
二、运行环境
Windows 2000 Professional
JDK 1.4.2_03
Eclipse 3.1
Tomcat 5.0.28
Tomcat Plugin 3.1Beta
Struts 1.2.7
Commons-Lang 2.1
Commons-Collections 3.1
Struts-Menu 2.3
MySQL 4.1.10a-nt
三、下载与安装
1:从http://java.sun.com下载J2SDK,当前1.4.x系列的最新版本为1.4.2_08
2:从http://www.eclipse.org下载Eclipse,当前最新版本为3.1正式版
3:从http://jakarta.apache.org/tomcat下载Tomcat,当前5.x系列的最新版本为5.0.28
4:从http://www.sysdeo.com/eclipse/tomcatplugin下载Eclipse的Tomcat插件,对应Eclipse3.1x的最新版本为3.1Beta
5:从http://struts.apache.org下载Struts,当前最新版本为1.2.7
6:从http://jakarta.apache.org/commons/下载Commons-Lang,当前最新版本为2.1,下载Commons-Collections,当前最新版本为3.1
7:从http://struts-menu.sourceforge.net下载Struts Menu,当前最新版本为2.3
8:从http://www.mysql.com下载MySQL数据库,4.x系列的最新版本是4.1.12a
9:MySQL、JDK、Eclipse、Tomcat和TomcatPlugin的安装及配置请参考相关资料
四、运行示例程序
1:安装好Tomcat后,解压缩struts-menu-2.3.zip,将struts-menu.war释放到Tomcat安装目录下的webapps下,运行Tomcat
2:在地址栏输入http://localhost:8080/struts-menu
五、安装与配置
1:在Eclipse中新建Tomcat项目,Context为/mymenu,Subdirectory为/web
2:在项目目录下面新建lib目录和web目录及WEB-INF,在web/WEB-INF目录下新建web.xml,内容如下:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>My Example Application -- Vinton Lee</display-name>
<!-- ============= The Struts ActionServlet Configuration ============= -->
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- ================================================================== -->
<!-- ============= The Struts Action Servlet Mapping ================== -->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- ================================================================== -->
<!-- The Welcome File List -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- =============== The Struts Taglib Definition ===================== -->
<taglib>
<taglib-uri>struts-bean</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-html</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-logic</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-nested</taglib-uri>
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>struts-tiles</taglib-uri>
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
</taglib>
<!-- ================================================================== -->
<!-- ============= The Struts-Menu Taglib Definition ================== -->
<taglib>
<taglib-uri>struts-menu</taglib-uri>
<taglib-location>/WEB-INF/struts-menu.tld</taglib-location>
</taglib>
<!-- ================================================================== -->
</web-app>
3:解压缩struts-1.2.7.zip,将压缩包中的lib目录下所有的8个jar释放到lib目录中,将5个tld文件释放到web\WEB-INF目录中,在web\WEB-INF目录中新建struts-config.xml,内容如下:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
"http://struts.apache.org/dtds/struts-config_1_2.dtd">
<struts-config>
<!-- ========== Data Source Configuration =============================== -->
<data-sources />
<!-- ========== Form Bean Definitions =================================== -->
<form-beans />
<!-- ========== Global Exception Definitions ============================ -->
<global-exceptions />
<!-- ========== Global Forward Definitions ============================== -->
<global-forwards />
<!-- ========== Action Mapping Definitions ============================== -->
<action-mappings />
<!-- ========== Controller Configuration ================================ -->
<!-- ========== Message Resources Definitions =========================== -->
<message-resources parameter="application" />
<!-- ========== Plug Ins Configuration ================================== -->
<plug-in className="net.sf.navigator.menu.MenuPlugIn">
<set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/>
</plug-in>
</struts-config>
|
1.web.xml的配置
web.xml加入如下Filter的配置
<filter>
<filter-name>securityFilter</filter-name>
<filter-class>org.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>org.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
FilterToBeanProxy就是Acegi过滤器Bean的代理,功能是把http请求按顺序分派给过滤器Bean;
说明:Acegi对WEB应用的支持主要是依靠servlet 过滤器(filter) 来实现的。每一个http request都将被这┕ 似鞑悴憷菇?并进行安全处理(包括认证和授权)。针对不同的安全处理,Acegi提供了不同的过滤器。过滤器的配置信息理论上应位于web.xml,但是我们又希望把Acegi的过滤器配置信息放在SpringFramework的配置文件里(applicationContext-security-acegi.xml),从而实现对这些过滤器的“控制反转”。解决这个问题的方法是采用Acegi提供的FilterToBeanProxy。FilterToBeanProxy顾名思义就是对Acegi过滤器Bean的代理,它的主要功能就是将http请求依次分派给对应的过滤器Bean。
这个Filter需要一个参数targetClass(目标类),意思是说代理哪个bean,为它配置的值是FilterChainProxy,就是由这个FilterChainProxy类负责将需要代理的这些Filter组成Filter代理“链”(这些Filter在Security.xml这个配置文件里以bean的形式进行配置了)
web.xml加入如下内容
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>/j_security_check</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>securityFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
这个就好理解了,就是这样的url都让他被securityFilter这个过滤器过滤处理
web.xml加入如下内容
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-*.xml,/WEB-INF/security.xml</param-value>
</context-param>
这个就是让上下文载入/WEB-INF/applicationContext-*.xml,/WEB-INF/security.xml这些配置文件
2.security.xml的配置
首先加入filterChainProxy这个bean的定义,因为前面在web.xml配置的filter需要这个bean
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON定义了url比较前先转为小写, PATTERN_TYPE_APACHE_ANT定义了使用Apache ant的匹配模式
这个bean将httpSessionContextIntegrationFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor这些bean组成了filter链,当发生需要web.xml配置的过滤器的对应的需要过滤的url的请求时,这些filter将会按照定义的顺序被调用,这个过滤器的mapping是“/*”。
既然配置了这些需要被调用的给filterChainProxy,那么就要对这些filter进行逐个配置,以便filterChainProxy能找到并调用他们。
httpSessionContextIntegrationFilter的配置及作用
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/>
每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象并组装ContextHolder。ContextHolder主要用于存放SecureContext,包括用户的权限信息,在request完后, 又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用,使之能跨越多个请求。
authenticationProcessingFilter的配置及作用
<bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/login.jsp?error=true"/> 登录失败的url
<property name="defaultTargetUrl" value="/"/> 登录成功后的url
<property name="filterProcessesUrl" value="/j_security_check"/> 需要这个filter处理的url
<property name="rememberMeServices" ref="rememberMeServices"/>
</bean>
该Filter负责处理登陆身份验证。当接受到与filterProcessesUrl所定义相同的请求时,它会首先通过 AuthenticationManager来验证用户身份。如果验证成功,则重定向到defaultTargetUrl所定义的成功登陆页面。如果验证失败,则再从rememberMeServices中获取用户身份,若再获取失败,则重定向到authenticationFailureUrl所定义登陆失败页面。
securityContextHolderAwareRequestFilter的配置及作用
<bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>
该Filter负责通过Decorate Model(装饰模式),装饰的HttpServletRequest对象。其Wapper是ServletRequest包装类 HttpServletRequestWrapper的子类(SavedRequestAwareWrapper或 SecurityContextHolderAwareRequestWrapper),附上获取用户权限信息,request参数,headers, Date headers 和 cookies 的方法。
在appfuse中使用的struts-menu.xml对用户菜单权限的控制参数role进行配置时必须结合该Filter,因为该filter有isUserInRole方法,而struts menu也是通过request.isUserInRole方法来控制菜单是否显示的
RememberMeProcessingFilter的配置及作用
<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="rememberMeServices" ref="rememberMeServices" />
</bean>
该Filter负责在用户登录后在本地机上记录用户cookies信息,免除下次再次登陆。检查AuthenticationManager 中是否已存在Authentication对象,如果不存在则会调用RememberMeServices的aotoLogin方法来从cookies中获取Authentication对象
AnonymousProcessingFilter的配置及作用
<bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="anonymous"/>
<property name="userAttribute" value="anonymous,ROLE_ANONYMOUS"/>
</bean>
该Filter负责为当不存在任何授权信息时,自动为Authentication对象添加userAttribute中定义的匿名用户权限
ExceptionTranslationFilter的配置及作用
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl" value="/login.jsp" />
<property name="forceHttps" value="false" />
</bean>
</property>
<property name="accessDeniedHandler">
<bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/accessDenied.jsp" />
</bean>
</property>
</bean>
该过滤器负责处理各种异常,然后重定向到相应的页面中。
filterInvocationInterceptor的配置及作用
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/>
<property name="accessDecisionManager" ref="accessDecisionManager"/>
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/signup.html*=ROLE_ANONYMOUS,admin,user
/passwordHint.html*=ROLE_ANONYMOUS,admin,user
/**/*.html*=admin,user
/clickstreams.jsp*=admin
</value>
</property>
</bean>
或
<bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager"/> 认证服务
<property name="accessDecisionManager">
<bean class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="false"/>
<property name="decisionVoters">
<list>
<bean class="org.acegisecurity.vote.RoleVoter">
<property name="rolePrefix" value=""/> //这里定义数据库中存放的角色和我们在这里声明的角色间是否需要加个前缀?这里没加
</bean>
</list>
</property>
</bean>
</property>
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/admin.htm*=a 这里就是数据库中对应的tyep a
/student*=s 由于没有前缀和数据库里一样
/teacher*=t
</value>
</property>
</bean>
按照多数的ACEGI的配置文档,权限表的权限标识多以AUTH_或ROLE_开头,那么如果你的表中是这么存储的就可以在rolePrefix处配置一个开头标识(其实这仅仅是个标识,没有任何实际意义,appfuse就没有这么配置)
该过滤器会首先调用AuthenticationManager判断用户是否已登陆认证,如还没认证成功,则重定向到登陆界面。认证成功,则并从 Authentication中获取用户的权限。然后从objectDefinitionSource属性获取各种URL资源所对应的权限。最后调用 AccessDecisionManager来判断用户所拥有的权限与当前受保华的URL资源所对应的权限是否相匹配。如果匹配失败,则返回403错误 (禁止访问)给用户。匹配成功则用户可以访问受保护的URL资源。
sitemesh应用Decorator模式,用filter截取request和response,把页面组件head,content,banner结合为一个完整的视图。通常我们都是用include标签在每个jsp页面中来不断的包含各种header, stylesheet, scripts and footer,现在,在sitemesh的帮助下,我们可以开心的删掉他们了。如下图,你想轻松的达到复合视图模式,那末看完本文吧。
|
一、在WEB-INF/web.xml中copy以下filter的定义:
<?xml version="1.0" encoding="GBK"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<filter>
<filter-name>sitemesh</filter-name>
<filter-class>com.opensymphony.module.sitemesh.filter.PageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
二、copy所需sitemesh-2.3.jar到WEB-INF\lib下。(这里可以下载http://www.opensymphony.com/sitemesh/)
三、 建立WEB-INF/decorators.xml描述各装饰器页面。
<decorators defaultdir="/decorators">
<decorator name="main" page="main.jsp">
<pattern>*</pattern>
</decorator>
</decorators>
|
上面配置文件指定了装饰器页面所在的路径,并指定了一个名为main的装饰器,该装饰器默认装饰web应用根路径下的所有页面。
四、 建立装饰器页面 /decorators/main.jsp
<%@ page contentType="text/html; charset=GBK"%>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator"%> <html>
<head>
<title><decorator:title default="装饰器页面..." /></title>
<decorator:head />
</head>
<body>
sitemesh的例子<hr>
<decorator:body />
<hr>chen56@msn.com
</body>
</html>
|
五、建立一个的被装饰页面 /index.jsp(内容页面)
<%@ page contentType="text/html; charset=GBK"%>
<html>
<head>
<title>Agent Test</title>
</head>
<body>
<p>本页只有一句,就是本句.</p>
</body>
</html>
|
最后访问index.jsp,将生成如下页面:
而且,所有的页面也会如同index.jsp一样,被sitemesh的filter使用装饰模式修改成如上图般模样,却不用再使用include标签。
|
装饰器 decorator概念 |
为了建立可复用的web应用程序,一个通用的方法是建立一个分层系统,如同下面一个普通的web应用:
- 前端:JSP和Servlets,或jakarta的velocity 。。。
- 控制层框架 Controller : (Struts/Webwork)
- 业务逻辑 Business :主要业务逻辑
- 持久化框架 :hibernate/jdo
可糟糕的是前端的页面逻辑很难被复用,当你在每一个页面中用数之不尽的include来复用公共的header, stylesheet, scripts,footer时,一个问题出现了-重复的代码,每个页面必须用copy来复用页面结构,而当你需要创意性的改变页面结构时,灾难就爱上了你。
sitemesh通过filter截取request和response,并给原始的页面加入一定的装饰(可能为header,footer...),然后把结果返回给客户端,并且被装饰的原始页面并不知道sitemesh的装饰,这也就达到了脱耦的目的。
据说即将新出台的Portlet规范会帮助我们标准的实现比这些更多更cool的想法,但可怜的我还不懂它到底是一个什末东东,有兴趣的人可以研究
jetspeed,或JSR (Java Specification Request) 168,但我想sitemesh如此简单,我们不妨先用着。
|
让我们看看怎样配置环境 |
除了要copy到WEB-INF/lib中的sitemesh.jar外,还有2个文件要建立到WEB-INF/:
- sitemesh.xml (可选)
- decorators.xml
sitemesh.xml 可以设置2种信息:
Page Parsers :负责读取stream的数据到一个Page对象中以被SiteMesh解析和操作。(不太常用,默认即可)
Decorator Mappers : 不同的装饰器种类,我发现2种比较有用都列在下面。一种通用的mapper,可以指定装饰器的配置文件名,另一种可打印的装饰器,可以允许你当用http://localhost/aaa/a.html?printable=true方式访问时给出原始页面以供打印(免得把header,footer等的花哨的图片也搭上)
(但一般不用建立它,默认设置足够了:com/opensymphony/module/sitemesh/factory/sitemesh-default.xml):
范例:
<sitemesh>
<page-parsers>
<parser default="true" class="com.opensymphony.module.sitemesh.parser.DefaultPageParser" />
<parser content-type="text/html" class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
<parser content-type="text/html;charset=ISO-8859-1" class="com.opensymphony.module.sitemesh.parser.FastPageParser" />
</page-parsers>
<decorator-mappers>
<mapper class="com.opensymphony.module.sitemesh.mapper.ConfigDecoratorMapper">
<param name="config" value="/WEB-INF/decorators.xml" />
</mapper>
<mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
<param name="decorator" value="printable" />
<param name="parameter.name" value="printable" />
<param name="parameter.value" value="true" />
</mapper>
</decorator-mappers>
</sitemesh> |
decorators.xml :定义构成复合视图的所有页面构件的描述(主要结构页面,header,footer...),如下例:
<decorators defaultdir="/decorators">
<decorator name="main" page="main.jsp">
<pattern>*</pattern>
</decorator>
<decorator name="printable" page="printable.jsp" role="customer" webapp="aaa" />
</decorators> |
- defaultdir: 包含装饰器页面的目录
- page : 页面文件名
- name : 别名
- role : 角色,用于安全
- webapp : 可以另外指定此文件存放目录
- Patterns : 匹配的路径,可以用*,那些被访问的页面需要被装饰。
|
最重要的是写出装饰器本身(也就是那些要复用页面,和结构页面)。 |
其实,重要的工作就是制作装饰器页面本身(也就是包含结构和规则的页面),然后把他们描述到decorators.xml中。
让我们来先看一看最简单的用法:其实最常用也最简单的用法就是我们的hello例子,面对如此众多的技术,我想只要达到功能点到为止即可,没必要去研究太深(除非您有更深的需求)。
<%@ page contentType="text/html; charset=GBK"%>
<%@ taglib uri="http://www.opensymphony.com/sitemesh/decorator" prefix="decorator" %>
<html>
<head>
<title><decorator:title default="装饰器页面..." /></title>
<decorator:head />
</head>
<body>
sitemesh的例子<hr>
<decorator:body />
<hr>chen56@msn.com
</body>
</html>
|
我们在装饰器页面只用了2个标签:
<decorator:title default="装饰器页面..." /> : 把请求的原始页面的title内容插入到<title></title>中间。
<decorator:body /> : 把请求的原始页面的body内的全部内容插入到相应位置。
然后我们在decorator.xml中加入以下描述即可:
<decorator name="main" page="main.jsp">
<pattern>*</pattern>
</decorator>
这样,请求的所有页面都会被重新处理,并按照main.jsp的格式重新展现在你面前。
|
让我们看看更多的用法。(抄袭sitemesh文档) |
以下列着全部标签:
<decorator:head />
插入原始页面(被包装页面)的head标签中的内容(不包括head标签本身)。
<decorator:body />
插入原始页面(被包装页面)的body标签中的内容。
<decorator:title [ default="..." ] />
插入原始页面(被包装页面)的title标签中的内容,还可以添加一个缺省值。
例:
/decorator/main.jsp中 (装饰器页面): <title><decorator:title default="却省title-hello" /> - 附加标题</title>
/aaa.jsp中 (原始页面):<title>aaa页面</title>
访问/aaa.jsp的结果:<title>aaa页面 - 附加标题</title>
<decorator:getProperty property="..." [ default="..." ] [ writeEntireProperty="..." ]/>
在标签处插入原始页面(被包装页面)的原有的标签的属性中的内容,还可以添加一个缺省值。
sitemesh文档中的例子很好理解:
The decorator: <body bgcolor="white"<decorator:getProperty property="body.onload" writeEntireProperty="true" />>
The undecorated page: <body onload="document.someform.somefield.focus();">
The decorated page: <body bgcolor="white" onload="document.someform.somefield.focus();">
注意,writeEntireProperty="true"会在插入内容前加入一个空格。
<decorator:usePage id="..." />
象jsp页面中的<jsp:useBean>标签一样,可以使用被包装为一个Page对象的页面。 (懒的用)
例:可用<decorator:usePage id="page" /> :<%=page.getTitle()%>达到<decorator:title/>的访问结果。
<page:applyDecorator name="..." [ page="..." title="..." ] >
<page:param name="..."> ... </page:param>
<page:param name="..."> ... </page:param>
</page:applyDecorator>
应用包装器到指定的页面上,一般用于被包装页面中主动应用包装器。这个标签有点不好理解,我们来看一个例子:
包装器页面 /decorators/panel.jsp:<p><decorator:title /></p> ... <p><decorator:body /></p>
并且在decorators.xml中有<decorator name="panel" page="panel.jsp"/>
一个公共页面,即将被panel包装:/public/date.jsp:
... <%=new java.util.Date()%> ...<decorator:getProperty property="myEmail" />
被包装页面 /page.jsp :
<title>page的应用</title>
.....
<page:applyDecorator name="panel" page="/_public/date.jsp" >
<page:param name="myEmail"> chen_p@neusoft.com </page:param>
</page:applyDecorator>
最后会是什末结果呢?除了/page.jsp会被默认的包装页面包装上header,footer外,page.jsp页面中还内嵌了date.jsp页面,并且此date.jsp页面还会被panel.jsp包装为一个title加body的有2段的页面,第1段是date.jsp的title,第2段是date.jsp的body内容。
另外,page:applyDecorator中包含的page:param标签所声明的属性值还可以在包装页面中用decorator:getProperty标签访问到。
|
可打印的界面装饰 |
前面说过有1种可打印的装饰器,可以允许你当用http://localhost/aaa/a.html?printable=true方式访问时,应用其他的装饰器(自己指定),给出原始页面以供打印(免得把header,footer等的花哨的图片也搭上)。
让我们来看一看怎样实现他:
1.首先在WEB-INFO/sitemesh.xml中设置:
<mapper class="com.opensymphony.module.sitemesh.mapper.PrintableDecoratorMapper">
<param name="decorator" value="printable" />
<param name="parameter.name" value="printable" />
<param name="parameter.value" value="true" />
</mapper>
这样就可以通过?printable=true来使用名为printable的装饰器,而不是用原来的装饰器。
2.在WEB-INFO/decorators.xml中定义相应的printable装饰器
<decorator name="printable" page="printable.jsp"/>
3.最后编写printable装饰器/decorators/printable.jsp
<%@ taglib uri="sitemesh-decorator" prefix="decorator" %>
<html>
<head>
<title><decorator:title /></title>
<decorator:head />
</head>
<body>
<h1><decorator:title /></h1>
<p align="right"><i>(printable version)</i></p>
<decorator:body />
</body>
</html> |
这样就可以让一个原始页面通过?printable=true开关来切换不同的装饰器页面。
|
中文问题 |
由于sitemesh内部所使用的缺省字符集为iso-8859-1,直接使用会产生乱码,我们可以通过以下方法纠正之:
- 方法1:可以在您所用的application server的配置文件中找一找,有没有设置encoding或
charset的项目,然后设成gbk或gb2312即可
方法2:这也是我们一直使用的方法。
1.在每一个jsp页里设置: <%@ page contentType="text/html; charset=gbk"%> 来告诉server你所要求的字符集。
2.在每个jsp页的head中定义:<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=gbk"> 来告诉浏览器你所用的字符集。
|
总结:使用sitemesh最通常的途径: |
1.配置好环境,
2.在WEB-INFO/decroators.xml中描述你将建立的包装器。
3.开发在decroators.xml中描述的包装器,最好存放在/_decorators目录下
4.ok ,可以看看辛勤的成果了 :)
|
1.导入STRUTS2-core-2.0.X.jar,xwork-2.0.X.jar,ognl-2.6.x.jar
2.配置web.xml文件
加入org.apache.struts2.dispatcher.FilterDispatcher过滤器的配置
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
FilterDispatcher是STRUTS2核心控制器。负责拦截所有的用户请求。
用户请求的到达时,Filter过滤用户请求。如果用户请求以action结尾,请求将被转入struts2框架处理。
<filter>可以加入以下参数
<init-param>
<param-name>config</param-name>
<param-value>以逗号隔开的多个struts配置文件的名,没有XML后缀</param-value>
</init-param>
<init-param>
<param-name>actionPackages</param-name>
<param-value>
org.apache.struts2.showcase.person包空间名,多个用逗号隔开
</param-value>
</init-param>
<init-param>
<param-name>configProviders</param-name>
<param-value>
一个或多个实现ConfigurationProvider接口的配置提供类的类全名,多个类用逗号隔开
</param-value>
</init-param>
3.在classes下加入struts.xml文件
Struts.xml文件用来配置action,拦截器等。
<?xml version=”1.0” encoding=”GBK”?>
<!—DTD信息-->
<!DOCTYPE struts PUBLIC
“-//Apache Software Foundation//DTD Struts Configuration 2.0//EN”
“http://struts.apache.org/dtds/struts-2.0.dtd”>
<struts>
<package name=”packagename” extends=”struts-default”>
<action name=”Login” class=”lee.LoginAction”>
<result name=”input”>login.jsp</result>
</action>
</package>
</struts>
i.Struts-default.xml配置文件位于struts-core-2.0.x.jar包中。是struts2最核心的配置文件。
ii.从中可以看到整个的struts的核心功能的全貌.
iii.Struts可以在result元素中指定相应的type属性(默认jsp).从而支持其他的视图技术。
v.包可以继承其他的包。包可以定义命名空间,用来区别其他配置文件中的相同的包名。
vi.包可以定义成abstract=true 这样包就只能被继承,不能被框架直接实例化。
vii.struts可以将一个struts.xml文件分解成多个配置文件,然后在struts.xml文件中包含其他配置文件。<struts><include file=”struts-partOne.xml”/></struts>
注意:也可以在web.xml中配置filter时加入config参数来加载多个配置文件。
3.struts.properties配置文件
该文件定义了struts2框架的大量属性。只要将该文件放在web应用的CLASSPATH下,struts2框架就会自动加载。
stuts.locale
指定Web应用的默认Locale
struts.i18n.encoding
指定应用的默认编码相当于调用HttpServletRequest的setCharacterEncoding方法。
struts.custom.i18n.resources
指定struts应用所需要的国际化资源文件的名称(message_zh_cn.properties只需要指定名为message)
struts.mutipart.parser
该属性处理mutipart/form-data的MIME类型请求的框架(cos|pell|jakarta)默认是jakarta(common-fileupload)
struts.multipart.saveDir
指定上传文件的临时保存路径,默认是javax.servlet.context.tempdir;
struts.multipart.maxSize
指定文件上传中整个请求内容允许的最大字节数。
struts.action.extension
指定Struts处理请求后缀,默认是action(login.action)
struts.serve.static.browserCache
指定浏览器是否缓存静态内容
struts.enable.SlashesInActionNames
该属性设置struts2是否允许在Action名中使用斜线,该属性的默认值是false。
struts.devMode
设置struts2是否使用开发模式(true|false)
struts.dispatcher.parametersWorkaround
对于不支持HttpServlertRequest调用getParameterMap()方法的服务器,可以设置该属性值为true
来解决该问题。不支持getParameterMap()方法的服务器:weblogic,orion,oc4j
struts.i18n.reload
设置是否每次HTTP请求到达时,系统都重新加载资源文件。默认false
struts.ui.theme
设置视图标签的默认主题。默认是xhtml
struts.ui.templateDir
设置视图主题所需模板文件的位置,默认是template,默认加载template路径下的模板文件。
struts.ui.templateSuffix
指定模板文件的后缀(ftl|vm|jsp)
struts.configuration.xml.reload
设置当struts.xml文件改变后,系统是否自动重新加载。
struts.configuration.files
指定默认加载的配置文件,默认值是:struts-default.xml,struts-plugin.xml,struts.xml
struts.objectFactory
指定struts2默认的ObjectFactoryBean,该属性默认是spring
struts.objectFactory.spring.autoWire
指定Spring框架自动装配模式,默认是name.即根据Bean的name属性自动装配。
struts.objectFactory.spring.userClassCache
指定spring框架是否缓存Bean实例。默认是true
struts.objectTypeDeterminer (tiger|notiger)
1. 先杀掉mysqld的进程:
>kill –TERM mysqld
2. 使用skip-grant-tables这个选项启动MySQL:
/usr/local/mysql/bin/mysqld_safe –skip-grant-tables
2. 登录修改密码:
>mysql –u root –p
Mysql >use mysql
Mysql>update user set ….
Mysql>flush privileges;
Mysql>exit
3. 关掉MySQL
>mysqladmin – u root –p shutdown
5. 启动MySQL
==手支启动Apache2
shell> /usr/local/apache2/bin/apachectl start <=激活
apachectl 还有其它的参数如下:
apachectl stop:停止 WWW 的服务;
apachectl restart:重新激活 WWW 的服务,这个指令比较常用在你修改了 apache 的参数后,重新激活用的。
apachectl status:侦测 WWW 的状态。
==删除目录
shell> rm –rf 目录
==新建目录
shell> mrdir 目录
==Copy文件
shell>cp /home/weixubin/mysqltest.php /usr/local/apache2/htdocs
==Copy目录
shell>cp -R /home/xubin.wei/crm /usr/local/apache/htdocs/crm
==Copy目录下指定格式
shell>cp /home/test/*.zip /usr/pwservices/
==更改文件所属
shell>chown -R nobody.nobody /usr/local/apache2/htdocs/crm
==更改文件属性
shell>chmod -R 777 /usr/local/apache2/htdocs/crm
==查看是否已安装程序
shell>rpm –q mysql
==删除一程序
shell>rpm –e mysql-3.23
==文件夹 Rename
shell>mv apache-ant-1.5.4 ant
==启动postgresql
shell> /usr/local/pgsql/bin/postmaster –I (小写) -D /home/postgre/data
==侦测端口是否被占用
shell>netstat -an | grep 389
==从根目录查找文件,并显示。
Shell>find / -name alert*.log –print
==查看进程
Shell> ps -ef | grep mysql
==查看磙盘空间大小
Shell>df –h
==修复
Shell>fsck –t ext2 /dev/ext2
==定位文件
shell> locate my.cnf
shell>vi my.cnf 编辑文件
编辑:Insert
命令状态;ESC
:wq保存
==增加组
shell>groupadd mysql
==查看全路径
shell>pwd