本人是菜鸟,E文也很烂,偏偏spring security更新很神速,版本之间相差特别大.网上有限的几个教材,也无法拿来就用.硬着头皮,借着spring security 2.x中文使用手册,再结合孙宁振的博文等,经过几天的奋战,终算能跑起来了.下面是根据自已理解,特写的教材.
本人表达能力有限,水平有限,只是由于spring security中文实例资料难找,特凭着脸皮厚,发表本教程,希引出大虾,作出权威的中文教材.是我等菜鸟之福了.
在这里,特别鸣谢spring security 2.x中文手册翻译者,孙宁振网友.
说明:本实例用到了 spring framework 2.5
spring security 2.04
开发工具:netbeans 6.5 cr2中文版.
附件,是本人亲自调试通过的工程.
需要参考的网友,下载后,可用开发工具,按文件方式打开.能看见所有的源代码,
还有lib.至少netbeans 6.5是这样.
用户名:admin;密码123
菜鸟为了方便菜鸟,把spring security 2.04的下载地址也附上:
http://downloads.sourceforge.net/springframework/spring-security-2.0.4.zip?modtime=1223032765&big_mirror=0
spring官方网站:
www.springsource.org
目录:
1 Dao方式的认证授权原理
1.1 认证授权简略过程:
1.2 Spring security配置思路
2 Dao 形式的认证授权实例
2.1 Web.xml配置
2.2 spring securtity配置
2.2.1 命名空间
2.2.2 默认设定
2.2.3 认证设定
2.2.4 授权设定
2.3 完整的: spring-security.xml
2.4 Login.jsp
=========================正 文=========================
1 Dao方式的认证授权原理
1.1 认证授权简略过程:
(1) 用户输入用户名,密码
(2) 认证--------Authentication
根据用户输入信息,查询《用户权限资料表》,得到用户权限名
其中:
《用户权限资料表》的基本形式为:
用户名 密码 权限名1,权限名2,..权限名n
(3) 授权--------Invocation
根据权限名,查询《权限明细资料表》,得到具体权限.
其中:
《权限明细资料表》,基本形式为:
网址(或网址加方法) 权限名1, 权限名2,.. 权限名n
从中看出,权限,最终是某个网址可以由哪些角色的用户访问.
(4) Spring Security根据得到的网址,确定是否允许访问
(5) 其他处理(略)
1.2 Spring security配置思路
揣摩官方的配置思路:
官方设定完整的默认配置。关键环节,允许用户更改默认配置。有一部份默认配置是核心的(如认证与授权),不允许用户替换官方默认配置,但允许用户在这些默认配置的开始或结束后运行用户的配置;有一部份默认配置不是核心的,完全允许用户替代。
在这种假设情况下:
spring security2的这项操作“http标签中,让用户选择官方提供的默认配置”得到解释。
spring security2的这条标签“<custom-filter before="过滤器假名"/>”得到合理解释。同时,认证过滤器与授权过滤器不允许替换默认过滤器也得到解释。
2 Dao 形式的认证授权实例
所谓Dao形式,指认证中所需要的用户权限资料表,权限明细资料表,都放在数据库中,这两个资料表的更新、删除、查询等维护操作由用户编写的dao 完成。这个dao与spring security一起完成用户认证授权工作。这里,为了突出spring security使用方法,这里的dao不与数据库发生联系,仅仅返回spring security所需的数据(数据在dao内中是固定的。),在实际使用中,由用户自行扩展。
2.1 Web.xml配置
Spring Security 是用servlet的过滤器实现认证的,因此需要在web.xml中进行配置。格式是固定的。最好位于所有过滤器前面。
代码:---------web.xml部份代码.
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>
- org.springframework.web.filter.DelegatingFilterProxy
- </filter-class>
- </filter>
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
由于spring security是由spring进行配置,还需在web.xml指定spring security的配置文件。
代码:---------------web.xml部份代码.
- <context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/spring-security.xml
- </param-value>
- </context-param>
- <listener>
- <listener-class>
- org.springframework.web.context.ContextLoaderListener
- </listener-class>
- </listener>
说明:
A 指定了spring监听器,启动spring.
B spring securtity的配置文件为: spring-security.xml
2.2 spring securtity配置
注意:配置文件为spring-security.xml
2.2.1 命名空间
核心代码:----- spring-security.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <b:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:b="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
说明:
A ”b:beans”,中的b是命名空间的别名,可由用户自行取名;官方的实例是这样的形式。
B spring-security.xml 中所有的bean的定义,都将前缀”b:bean”.
2.2.2 默认设定
所谓默认设定,指spring security 2 已经认定好的了一系列过滤器。省去了早先版本设定一长串过滤器的烦恼。
(1) 最省事的设定之一
- <http auto-config='true'>
- <intercept-url pattern="/**" access="ROLE_USER" />
- </http>
说明:
A 这里,要求spring security 2 完全按默认处理。通常内存权限设定很管用。
其中:<intercept-url pattern="/**" access="ROLE_USER" />,规定了:名为ROLE_USER的权限,可以访问整个网站。
B 权限名称有规定。至少:前缀“ROLE_”的由大写字母构成的字符串,符合规定。如:ROLE_XXX。
(2) 最省事的设定之二
- <http>
- <intercept-url pattern="/**" access="ROLE_USER" />
- <form-login />
- <anonymous />
- <http-basic />
- <logout />
- <remember-me />
- </http>
说明:
A 这里与前面的方式完全等同。
B form-login 指定了登陆界面。如果没具体指定,spring security将用自身的登陆界面。
C anonymous ,允许匿名访问网站(估计没保护的网页, 匿名者都能访问).
D http-basic 加载系列默认的过滤器。
至少有:AuthenticationProcessingFilter(认证过滤器),
FilterSecurityInterceptor(授权过滤器)
E 凡是这里加载了的过滤器,用户都不能自行设定过滤器替代它们。也就是说AuthenticationProcessingFilter(认证过器) 与FilterSecurityInterceptor(授权过滤器)用户不能替代。
F 凡是这里加载了的过滤器,用户可以自行定义它们中同类的过滤器,挂在相应默认过滤器的前或者后,但是不能替代。
G logout,设定默认的LogoutFilter过滤器。
H remember-me,设定RememberMeProcessingFilter过滤器。
I E与F适用于D、G、H
(3) 本例中http设定-------- spring-security.xml
- <http>
- <intercept-url pattern="/**" access="ROLE_USER"/>
- <intercept-url pattern="/login.jsp*" filters="none"/>
- <form-login login-page='/login.jsp'/>
- <http-basic />
- <logout /><!--加载离开过滤器-->
- </http>
说明:
A 好像,非得要指定一个内存权限。
B
- <intercept-url pattern="/login.jsp*" filters="none"/>
- <form-login login-page='/login.jsp'/>
上面这两句,是指定用户自已的登陆界面,是固定形式.前者,表示login.jsp暴露给所有用户,无需保护。如果不要前句,将出现浏览器不允许cookie加载之类的错误提示。
2.2.3 认证设定
spring security 2提供了一个专门的认证设定。本节中用户权限,都是指为一个用户设定一个权限名。而这个权限名的定义,不在认证设定处理范围之内,将由授权处理器处理。
(1) 内存设定用户权限例子
- <authentication-provider>
- <user-service>
- <user name="张三" password="123" authorities="ROLE_USER,ROLE_DDD" />
- </user-service>
- </authentication-provider>
说明:这里为张三指定了密码,以及两个权限:“ROLE_USER”与“ROLE_DDD”。
(2) Dao形式设定用户权限的例子--------- spring-security.xml
- <authentication-provider user-service-ref='userDetailsService'/>
- <b:bean id="userDetailsService"
- class="fyh.pub.login.security.UserDetailsSerivceImpl">
- </b:bean>
其中:fyh.pub.login.security.UserDetailsSerivceImpl是用户自已写的类。这个类实现了spring security 2的UserDetailsService接口。源代码如下:
(3) UserDetailsService源代码UserDetailsSerivceImpl.java
- package fyh.pub.login.security;
- import fyh.pub.login.model.User;
- import org.springframework.dao.DataAccessException;
- import org.springframework.security.userdetails.UserDetails;
- import org.springframework.security.userdetails.UserDetailsService;
- import org.springframework.security.userdetails.UsernameNotFoundException;
- public class UserDetailsSerivceImpl implements UserDetailsService {
- public UserDetails loadUserByUsername(String username)
- throws UsernameNotFoundException, DataAccessException {
- UserDetails user=new User();
- return user;
- }
- }
package fyh.pub.login.security;
import fyh.pub.login.model.User;
import org.springframework.dao.DataAccessException;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
public class UserDetailsSerivceImpl implements UserDetailsService {
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
UserDetails user=new User();
return user;
}
}
说明:User是用户自已写的类,这个类实现了spring security2的UserDetails接口。
(4) User源代码
User.java
- package fyh.pub.login.model;
- import java.util.ArrayList;
- import java.util.List;
- import org.springframework.security.GrantedAuthority;
- import org.springframework.security.GrantedAuthorityImpl;
- import org.springframework.security.userdetails.UserDetails;
- public class User implements UserDetails {
- private String id="0";
- private String username="admin";
- private String password="123";
- //省略getter and setter
- public GrantedAuthority[] getAuthorities() {
- List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
- list.add(new GrantedAuthorityImpl("ROLE_XXX"));
- list.add(new GrantedAuthorityImpl("ROLE_DDD"));
- return list.toArray(new GrantedAuthority[list.size()]);
- }
- public boolean isAccountNonExpired() {return true;}
- public boolean isAccountNonLocked() {return true;}
- public boolean isCredentialsNonExpired() { return true; }
- public boolean isEnabled() {return true; }
- }
package fyh.pub.login.model;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetails;
public class User implements UserDetails {
private String id="0";
private String username="admin";
private String password="123";
//省略getter and setter
public GrantedAuthority[] getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new GrantedAuthorityImpl("ROLE_XXX"));
list.add(new GrantedAuthorityImpl("ROLE_DDD"));
return list.toArray(new GrantedAuthority[list.size()]);
}
public boolean isAccountNonExpired() {return true;}
public boolean isAccountNonLocked() {return true;}
public boolean isCredentialsNonExpired() { return true; }
public boolean isEnabled() {return true; }
}
2.2.4 授权设定
这里主要是用了FilterSecurityInterceptor过滤器。
(1) 引子--------用户自行设定过滤器
设定格式形如:
- <beans:bean id="myFileterName" class="myFileterClass">
- <custom-filter position="过滤器假名"/>
- </beans:bean>
<beans:bean id="myFileterName" class="myFileterClass">
<custom-filter position="过滤器假名"/>
</beans:bean>
其中:
A ” position”表示本过滤器,要替代其后的过滤器. "过滤器假名"指示了被替代的过滤器名。
B 除了” position”,还有”before” 与”after”两种方式。分别表示将要把本过滤器挂在其后的过滤器前或后。
例:这里设定了一个abc的过滤器,该过滤器,将挂在http中设定的过滤器-----FilterSecurityInterceptor的前面。
- <b:bean id="abc"
- class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
- <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
- </b:bean>
<b:bean id="abc"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
</b:bean>
(2) 引子--------标准过滤器假名和顺序
- 假名 过滤器类
- CHANNEL_FILTER ChannelProcessingFilter
- CONCURRENT_SESSION_FILTER ConcurrentSessionFilter
- SESSION_CONTEXT_INTEGRATION_FILTER HttpSessionContextIntegrationFilter
- LOGOUT_FILTER LogoutFilter
- X509_FILTER X509PreAuthenticatedProcessigFilter
- PRE_AUTH_FILTER AstractPreAuthenticatedProcessingFilter Subclasses
- CAS_PROCESSING_FILTER CasProcessingFilter
- AUTHENTICATION_PROCESSING_FILTER AuthenticationProcessingFilter
- BASIC_PROCESSING_FILTER BasicProcessingFilter
- SERVLET_API_SUPPORT_FILTER SecurityContextHolderAwareRequestFilter
- REMEMBER_ME_FILTER RememberMeProcessingFilter
- ANONYMOUS_FILTER AnonymousProcessingFilter
- EXCEPTION_TRANSLATION_FILTER ExceptionTranslationFilter
- NTLM_FILTER NtlmProcessingFilter
- FILTER_SECURITY_INTERCEPTOR FilterSecurityInterceptor
- SWITCH_USER_FILTER SwitchUserProcessingFilter
(3) 定义自已的授权过滤器
由于前面, http中已经设定了默认的过滤器。因此,这里自行设定的过滤器将挂在滤认过滤器FilterSecurityInterceptor 的前面。注意:无法替代。
源代码: spring-security.xml
- <b:bean id="filterSecurityInterceptor11"
- class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
- <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
- <b:property name="authenticationManager" ref="authenticationManager" />
- <b:property name="accessDecisionManager" ref="accessDecisionManager" />
- <b:property name="objectDefinitionSource"
- ref="databaseFilterInvocationDefinitionSource" />
- </b:bean>
<b:bean id="filterSecurityInterceptor11"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
<b:property name="authenticationManager" ref="authenticationManager" />
<b:property name="accessDecisionManager" ref="accessDecisionManager" />
<b:property name="objectDefinitionSource"
ref="databaseFilterInvocationDefinitionSource" />
</b:bean>
其中:authenticationManager 是认证管理器。accessDecisionManager是决策管理器。databaseFilterInvocationDefinitionSource是授权管理器(是根据我的理解起的名)。它们随后设定。
(4) authenticationManager设定
源代码: spring-security.xml
- <b:bean id="authenticationManager"
- class="org.springframework.security.providers.ProviderManager">
- <b:property name="providers">
- <b:list>
- <b:ref local="daoAuthenticationProvider" />
- </b:list>
- </b:property>
- </b:bean>
- <b:bean id="daoAuthenticationProvider"
- class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
- <b:property name="userDetailsService" ref="userDetailsService" />
- </b:bean>
其中:authenticationManager实例----引用了---->daoAuthenticationProvider实例,
daoAuthenticationProvider实例----引用了----> userDetailsService实例,
而userDetailsService实例在前面的《2.2.3认证设定》(2)中已经设定。
(5) accessDecisionManager设定
决策管理器, 经过投票机制来决定是否可以访问某一资源 allowIfAllAbstainDecisions为false时,如果有一个以上的decisionVoters投票通过,则授权通过。 可选的决策机制: ConsensusBased和 UnanimousBased。
源代码: spring-security.xml
- <b:bean id="accessDecisionManager"
- class="org.springframework.security.vote.AffirmativeBased">
- <b:property name="decisionVoters">
- <b:list>
- <b:bean class="org.springframework.security.vote.RoleVoter">
- <b:property name="rolePrefix" value="" />
- </b:bean>
- </b:list>
- </b:property>
- </b:bean>
(6) databaseFilterInvocationDefinitionSource-----是它获得权限明细资料
该类,是通过LinkedHashMap对象,传递权限明细资料的。
源代码: spring-security.xml
- class="org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource">
- <!-- 匹配url的matcher -->
- <b:constructor-arg type="org.springframework.security.util.UrlMatcher"
- ref="antUrlPathMatcher" />
- <!-- url对应authority的map -->
- <b:constructor-arg type="java.util.LinkedHashMap" ref="requestMap" />
- </b:bean>
说明:
A antUrlPathMatcher是定义的url格式,随后定义。
B requestMap 装载着权限明细资料。随后定义。
(7) antUrlPathMatcher源代码: spring-security.xml
- <b:bean id="antUrlPathMatcher"
- class="org.springframework.security.util.AntUrlPathMatcher" />
(8) requestMap------它身上有权限明细资料
源代码: spring-security.xml
- <b:bean id="requestMap"
- class="fyh.pub.login.security.RequestMapFactoryBean"
- init-method="init">
- </b:bean>
说明:RequestMapFactoryBean是由用户自定义的一个类,该类的init方法,返回一个LinkedHashMap对象。随后给出RequestMapFactoryBean的源代码
(9) RequestMapFactoryBean
RequestMapFactoryBean.java
- package fyh.pub.login.security;
- import java.util.LinkedHashMap;
- import org.springframework.beans.factory.FactoryBean;
- import org.springframework.security.ConfigAttribute;
- import org.springframework.security.ConfigAttributeDefinition;
- import org.springframework.security.SecurityConfig;
- import org.springframework.security.intercept.web.RequestKey;
- public class RequestMapFactoryBean implements FactoryBean {
- private LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap;
- public void init() {
- requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();
- RequestKey key = new RequestKey("/**");
- ConfigAttribute attribute = new SecurityConfig("ROLE_XXX");
- ConfigAttributeDefinition definition = new ConfigAttributeDefinition(
- attribute);
- requestMap.put(key, definition);
- key = new RequestKey("/login.jsp");
- attribute = new SecurityConfig("ROLE_EEE");
- definition = new ConfigAttributeDefinition(
- attribute);
- requestMap.put(key, definition);
- }
- public Object getObject() throws Exception {
- if (requestMap == null) {init();}
- return requestMap;
- }
- public Class getObjectType() {return LinkedHashMap.class; }
- public boolean isSingleton() {return true;}
- }
package fyh.pub.login.security;
import java.util.LinkedHashMap;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.security.ConfigAttribute;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.SecurityConfig;
import org.springframework.security.intercept.web.RequestKey;
public class RequestMapFactoryBean implements FactoryBean {
private LinkedHashMap<RequestKey, ConfigAttributeDefinition> requestMap;
public void init() {
requestMap = new LinkedHashMap<RequestKey, ConfigAttributeDefinition>();
RequestKey key = new RequestKey("/**");
ConfigAttribute attribute = new SecurityConfig("ROLE_XXX");
ConfigAttributeDefinition definition = new ConfigAttributeDefinition(
attribute);
requestMap.put(key, definition);
key = new RequestKey("/login.jsp");
attribute = new SecurityConfig("ROLE_EEE");
definition = new ConfigAttributeDefinition(
attribute);
requestMap.put(key, definition);
}
public Object getObject() throws Exception {
if (requestMap == null) {init();}
return requestMap;
}
public Class getObjectType() {return LinkedHashMap.class; }
public boolean isSingleton() {return true;}
}
说明:
A 这里,在类中固定了两个权限。
整个网站可被ROLE_XXX 访问。
/login.jsp 可被ROLE_EEE访问。
B 网址作为 LinkedHashMap的key,权限名放在ConfigAttributeDefinition 中,作为LinkedHashMap的值。
2.3 完整的spring-seurity.xml
- <?xml version="1.0" encoding="UTF-8"?>
- <b:beans xmlns="http://www.springframework.org/schema/security"
- xmlns:b="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
- <http>
- <intercept-url pattern="/**" access="ROLE_USER"/>
- <intercept-url pattern="/login.jsp*" filters="none"/>
- <form-login login-page='/login.jsp'/>
- <http-basic />
- <logout /><!--加载离开过滤器-->
- </http>
- <!--设用户权限, 权限表示方式为:用户名 密码 权限名列表.
- 内存设置方式如下例:
- <authentication-provider>
- <user-service>
- <user name="张三" password="123" authorities="ROLE_USER,ROLE_DDD" />
- </user-service>
- </authentication-provider>
- -->
- <!--dao方式设置用户权限-->
- <authentication-provider user-service-ref='userDetailsService'/>
- <b:bean id="userDetailsService"
- class="fyh.pub.login.security.UserDetailsSerivceImpl">
- </b:bean>
- <!--***********************************************************************
- *************************************************************************-->
- <!--
- 负责授权的filter,检查Authentication所授予的权限是否可以访问被访问的资源
- -->
- <b:bean id="filterSecurityInterceptor11"
- class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
- <custom-filter before="FILTER_SECURITY_INTERCEPTOR"/>
- <b:property name="authenticationManager" ref="authenticationManager" />
- <b:property name="accessDecisionManager" ref="accessDecisionManager" />
- <b:property name="objectDefinitionSource"
- ref="databaseFilterInvocationDefinitionSource" />
- </b:bean>
- <b:bean id="authenticationManager"
- class="org.springframework.security.providers.ProviderManager">
- <b:property name="providers">
- <b:list>
- <b:ref local="daoAuthenticationProvider" />
- </b:list>
- </b:property>
- </b:bean>
- <b:bean id="daoAuthenticationProvider"
- class="org.springframework.security.providers.dao.DaoAuthenticationProvider">
- <b:property name="userDetailsService" ref="userDetailsService" />
- </b:bean>
- <b:bean id="accessDecisionManager"
- class="org.springframework.security.vote.AffirmativeBased">
- <b:property name="decisionVoters">
- <b:list>
- <b:bean class="org.springframework.security.vote.RoleVoter">
- <b:property name="rolePrefix" value="" />
- </b:bean>
- </b:list>
- </b:property>
- </b:bean>
- <b:bean id="databaseFilterInvocationDefinitionSource"
- class="org.springframework.security.intercept.web.DefaultFilterInvocationDefinitionSource">
- <b:constructor-arg type="org.springframework.security.util.UrlMatcher"
- ref="antUrlPathMatcher" />
- <!-- url对应authority的map -->
- <b:constructor-arg type="java.util.LinkedHashMap" ref="requestMap" />
- </b:bean>
- <b:bean id="antUrlPathMatcher"
- class="org.springframework.security.util.AntUrlPathMatcher" />
- <b:bean id="requestMap"
- class="fyh.pub.login.security.RequestMapFactoryBean"
- init-method="init">
- </b:bean>
- </b:beans>
2.4 Login.jsp说明: 如果用户,要用自已的登陆界面,需要
(1) 把form的action设成j_spring_security_check
(2) 用户名设成j_username
(3) 密码名设成j_password
以上三点,是spring security的约定。
login.jsp核心代码
- <form action="j_spring_security_check" method="post">
- <table>
- <tr>
- <td><label for="username">用户名:</label></td>
- <td><input type="text" id="username" name="j_username"
- value="${SPRING_SECURITY_LAST_USERNAME}"/></td>
- </tr>
- <tr>
- <td><label for="password">密码:</label></td>
- <td><input type="password" id="password" name="j_password" value=""/></td>
- </tr>
- <tr><td></td>
- <td><input type="checkbox" name="_spring_security_remember_me">两周内记住我</td>
- </tr>
- <tr><td colspan="2"><input type="submit" value="提交"/>
- <input type="reset" value="重置"/></td></tr>
- </table>
- </form>