安全拦截?/b>
拦截器如何工?br /> MethodInvocation拦截?br /> FilterInvocation拦截?br />认证
认证h
认证理?br /> Authentication Provider
授权
Access Decision Manager
Voting Decision Manager
授权理推荐
ContextHolder的用h?br /> 用户接口目标
HTTP会话认证
HTTP Basic认证
1、Log4j的概?br /> Log4j中有三个主要的组Ӟ它们分别是Logger、Appender和LayoutQLog4j 允许开发h员定义多个LoggerQ每个Logger拥有自己的名字,Logger之间通过名字来表明隶属关pR有一个LoggerUCؓRootQ它永远存在Q且不能通过名字索或引用Q可以通过Logger.getRootLogger()Ҏ获得Q其它Logger通过 Logger.getLogger(String name)Ҏ?br /> Appender则是用来指明所有的log信息存放C么地方,Log4j中支持多UappenderQ如 console、files、GUI components、NT Event Loggers{,一个Logger可以拥有多个AppenderQ也是你既可以Log信息输出到屏q,同时存储C个文件中?br /> Layout的作用是控制Log信息的输出方式,也就是格式化输出的信息?br /> Log4j中将要输出的Log信息定义?U别,依次为DEBUG、INFO、WARN、ERROR和FATALQ当输出Ӟ只有U别高过配置中规定的U别的信息才能真正的输出Q这样就很方便的来配|不同情况下要输出的内容Q而不需要更改代码,q点实在是方便啊?br />
2、Log4j的配|文?br /> 虽然可以不用配置文gQ而在E序中实现配|,但这U方法在如今的系l开发中昄是不可取的,能采用配|文件的地方一定一定要用配|文件。Log4j支持两种格式的配|文ӞXML格式和Java的property格式Q本人更喜欢后者,首先看一个简单的例子吧,如下Q?br />
log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
首先Q是讄rootQ格式ؓ log4j.rootLogger=[level],appenderName, ...Q其中level是讄需要输Z息的U别Q后面是appender的输出的目的圎ͼappenderName是指定日志信息输出到哪个地斏V您可以同时指定多个输出目的地。配|日志信息输出目的地AppenderQ其语法?br /> log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1
...
log4j.appender.appenderName.option = valueN
Log4j提供的appender有以下几U:
org.apache.log4j.ConsoleAppenderQ控制台Q?br /> org.apache.log4j.FileAppenderQ文Ӟ
org.apache.log4j.DailyRollingFileAppenderQ每天生一个日志文Ӟ
org.apache.log4j.RollingFileAppenderQ文件大到达指定尺寸的时候生新文gQ?br /> org.apache.log4j.WriterAppenderQ将日志信息以流格式发送到L指定的地方)
配置日志信息的格式(布局Q,其语法ؓQ?br /> log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
....
log4j.appender.appenderName.layout.option = valueN
Log4j提供的layout有以下几U:
org.apache.log4j.HTMLLayoutQ以HTML表格形式布局Q,
org.apache.log4j.PatternLayoutQ可以灵zd指定布局模式Q,
org.apache.log4j.SimpleLayoutQ包含日志信息的U别和信息字W串Q,
org.apache.log4j.TTCCLayoutQ包含日志生的旉、线E、类别等{信息)
3、Log4j在程序中的?br /> 要在自己的类中用Log4jQ首先声明一个静态变量Logger logger=Logger.getLog("classname")Q在使用之前Q用PropertyConfigurator.configure ("配置文g")配置一下,现在可以用了Q用法如下:logger.debug("debug message")或者logger.info("info message")Q看下面一个小例子Q?br />
import com.foo.Bar;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class MyApp {
static Logger logger = Logger.getLogger(MyApp.class.getName());
public static void main(String[] args) {
// BasicConfigurator replaced with PropertyConfigurator.
PropertyConfigurator.configure(args[0]);
logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}
[介]
对于一个典型的Web应用Q完善的认证和授权机制是必不可少的,在SpringFramework中,Juergen Hoeller提供的范例JPetStorel了一些这斚w的介l,但还q远不够QAcegi是一个专门ؓSpringFramework提供安全机制的项目,全称为Acegi Security System for SpringQ当前版本ؓ0.5.1Q就其目前提供的功能Q应该可以满绝大多数应用的需求?br />
本文的主要目的是希望能够说明如何在基于Spring构架的Web应用中用AcegiQ而不是详l介l其中的每个接口、每个类。注意,即对已l存在的Spring应用Q通过下面介绍的步骤,也可以马上n受到Acegi提供的认证和授权?br />
[基础工作]
在你的Web应用的lib中添加Acegi下蝲包中的acegi-security.jar
[web.xml]
实现认证和授权的最常用的方法是通过filterQAcegi亦是如此Q通常Acegi需要在web.xmld以下5个filter:
<filter>
<filter-name>Acegi Channel Processing Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.securechannel.ChannelProcessingFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Acegi Authentication Processing Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
<filter-class>net.sf.acegisecurity.ui.AutoIntegrationFilter</filter-class>
</filter>
<filter>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
</init-param>
</filter>
最先引赯惑的是net.sf.acegisecurity.util.FilterToBeanProxyQAcegi自己的文上解释是: “What FilterToBeanProxy does is delegate the Filter's methods through to a bean which is obtained from the
Spring application context. This enables the bean to benefit from the Spring application context lifecycle support and configuration flexibility.”,如希望深I的话,ȝ看源代码应该不难理解?br />
再下来就是添加filter-mapping了:
<filter-mapping>
<filter-name>Acegi Channel Processing Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Authentication Processing Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
q里Q需要注意以下两点:
1) q几个filter的顺序是不能更改的,序不对无法正常工作;
2) 如果你的应用不需要安全传输,如httpsQ则?Acegi Channel Processing Filter"相关内容注释掉即可;
3) 如果你的应用不需要Spring提供的远E访问机Ӟ如Hessian and BurlapQ将"Acegi HTTP BASIC Authorization
Filter"相关内容注释掉即可?br />
[applicationContext.xml]
接下来就是要dapplicationContext.xml中的内容了,从刚才FilterToBeanFactory的解释可以看出,真正的filter?br />在Spring的applicationContext中管理:
1) 首先Q你的数据库中必d有保存用户名和密码的tableQAcegi要求table的schema必须如下Q?br />
CREATE TABLE users (
username VARCHAR(50) NOT NULL PRIMARY KEY,
password VARCHAR(50) NOT NULL,
enabled BIT NOT NULL
);
CREATE TABLE authorities (
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL
);
CREATE UNIQUE INDEX ix_auth_username ON authorities ( username, authority );
ALTER TABLE authorities ADD CONSTRAINT fk_authorities_users
foreign key (username) REFERENCES users
(username);
2) d讉K你的数据库的datasource和Acegi的jdbcDaoQ如下:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="jdbcDaoImpl" class="net.sf.acegisecurity.providers.dao.jdbc.JdbcDaoImpl">
<property name="dataSource"><ref bean="dataSource"/></property>
</bean>
3) dDaoAuthenticationProvider:
<bean id="daoAuthenticationProvider"
class="net.sf.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="authenticationDao"><ref bean="authenticationDao"/></property>
<property name="userCache"><ref bean="userCache"/></property>
</bean>
<bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="minutesToIdle"><value>5</value></property>
</bean>
如果你需要对密码加密Q则在daoAuthenticationProvider中加入:<property name="passwordEncoder"><ref
bean="passwordEncoder"/></property>QAcegi提供了几U加密方法,详细情况可看?br />net.sf.acegisecurity.providers.encoding
4) dauthenticationManager:
<bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref bean="daoAuthenticationProvider"/>
</list>
</property>
</bean>
5) daccessDecisionManager:
<bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions">
<value>false</value>
</property>
<property name="decisionVoters">
<list><ref bean="roleVoter"/></list>
</property>
</bean>
<bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>
6) dauthenticationProcessingFilterEntryPoint:
<bean id="authenticationProcessingFilterEntryPoint"
class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl"><value>/acegilogin.jsp</value></property>
<property name="forceHttps"><value>false</value></property>
</bean>
其中acegilogin.jsp是登陆页面,一个最单的d面如下Q?br />
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core' %>
<%@ page import="net.sf.acegisecurity.ui.AbstractProcessingFilter" %>
<%@ page import="net.sf.acegisecurity.AuthenticationException" %>
<html>
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form action="<c:url value='j_acegi_security_check'/>" method="POST">
<table>
<tr><td>User:</td><td><input type='text' name='j_username'></td></tr>
<tr><td>Password:</td><td><input type='password' name='j_password'></td></tr>
<tr><td colspan='2'><input name="submit" type="submit"></td></tr>
<tr><td colspan='2'><input name="reset" type="reset"></td></tr>
</table>
</form>
</body>
</html>
7) dfilterInvocationInterceptor:
<bean id="filterInvocationInterceptor"
class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager">
<ref bean="authenticationManager"/>
</property>
<property name="accessDecisionManager">
<ref bean="accessDecisionManager"/>
</property>
<property name="objectDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/sec/administrator.*\Z=ROLE_SUPERVISOR
\A/sec/user.*\Z=ROLE_TELLER
</value>
</property>
</bean>
q里h意,要objectDefinitionSource中定义哪些页面需要权限访问,需要根据自q应用需求进行修改,我上面给?br />的定义的意思是q样的:
a. CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON意思是在比较请求\径时全部转换为小?br />b. \A/sec/administrator.*\Z=ROLE_SUPERVISOR意思是只有权限为ROLE_SUPERVISOR才能讉K/sec/administrator*的页?br />c. \A/sec/user.*\Z=ROLE_TELLER意思是只有权限为ROLE_TELLER的用h能访?sec/user*的页?br />
8) dsecurityEnforcementFilter:
<bean id="securityEnforcementFilter"
class="net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter">
<property name="filterSecurityInterceptor">
<ref bean="filterInvocationInterceptor"/>
</property>
<property name="authenticationEntryPoint">
<ref bean="authenticationProcessingFilterEntryPoint"/>
</property>
</bean>
9) dauthenticationProcessingFilter:
<bean id="authenticationProcessingFilter"
class="net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager">
<ref bean="authenticationManager"/>
</property>
<property name="authenticationFailureUrl">
<value>/loginerror.jsp</value>
</property>
<property name="defaultTargetUrl">
<value>/</value>
</property>
<property name="filterProcessesUrl">
<value>/j_acegi_security_check</value>
</property>
</bean>
其中authenticationFailureUrl是认证失败的面?br />
10) 如果需要一些页面通过安全通道的话Q添加下面的配置:
<bean id="channelProcessingFilter"
class="net.sf.acegisecurity.securechannel.ChannelProcessingFilter">
<property name="channelDecisionManager">
<ref bean="channelDecisionManager"/>
</property>
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/sec/administrator.*\Z=REQUIRES_SECURE_CHANNEL
\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL
\A.*\Z=REQUIRES_INSECURE_CHANNEL
</value>
</property>
</bean>
<bean id="channelDecisionManager"
class="net.sf.acegisecurity.securechannel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref bean="secureChannelProcessor"/>
<ref bean="insecureChannelProcessor"/>
</list>
</property>
</bean>
<bean id="secureChannelProcessor"
class="net.sf.acegisecurity.securechannel.SecureChannelProcessor"/>
<bean id="insecureChannelProcessor"
class="net.sf.acegisecurity.securechannel.InsecureChannelProcessor"/>
[~少了什么?]
Acegi目前提供了两U?secure object"Q分别对面和方法进行安全认证管理,我这里介l的只是利用
FilterSecurityInterceptor对访问页面的权限控制Q除此之外,Acegiq提供了另外一个Interceptor―?br />MethodSecurityInterceptorQ它l合runAsManager可实现对对象中的Ҏ的权限控Ӟ使用Ҏ可参看Acegi自带的文?br />和contact范例?br />
[最后要说的]
本来以ؓ只是说明如何使用Acegi而已Q应该非常简单,但真正写h才发现想要条理清楚的理顺所有需要的beanq是?br />困难的,但愿我没有遗漏太多东西,如果我的文章有什么遗漏或错误的话Q还请参看Acegi自带的quick-start范例Q但?br />注意Q这个范例是不能直接拿来用的?br />分析和学习Spring中的jpetstore用户理
存在用户的系l,必然需要用Ld和认证,今天通过分析Spring中自带的jpetstore的例子来学习一下如何实现在Spring构架的系l中用户d?br />1、首先从注册用户开始,先看看jpetstore-servlet.xml中关于注册用Lbean定义Q从定义命名中就可以看出下面q段是注册用户的:
<bean name="/shop/newAccount.do"
class="org.springframework.samples.jpetstore.web.spring.AccountFormController">
<property name="petStore"><ref bean="petStore"/></property>
<property name="validator"><ref bean="accountValidator"/></property>
<property name="successView"><value>index</value></property>
</bean>
1). formView呢?从AccountFormController的构造函C得到Q原来ؓEditAccountFormQ ?br />2). EditoAccountForm.jsp中显得非ؕQ其实没有多难理解的地方,最主要的是q个form既是d新用LQ又是编辑用户信息的Q所以显得有点ؕp糟的?br />2、添加好了新用户Q接下来看看如何dQ在jpetstore-servlet中发现这两个相关bean定义Q如下:
<bean name="/shop/signon.do"
class="org.springframework.samples.jpetstore.web.spring.SignonController">
<property name="petStore"><ref bean="petStore"/></property>
</bean>
<bean name="/shop/signonForm.do"
class="org.springframework.web.servlet.mvc.ParameterizableViewController">
<property name="viewName"><value>SignonForm</value></property>
</bean>
1). W二个bean是在q行时用戯入用户名和密码的formQ叫做SignonFormQ对于这?ParameterizableViewControllerQ用文里的话说q是最单的ControllerQ其作用是在运行中指向 Controller而不是直接指向jsp文gQ仅此而已?br />2). SignonForm.jspQ里面就是一个简单的formQ其action是W一个beanQ即/shop/signon.doQ最需要注意的?signonForwardActionQ其主要作用是forward到需要输入用户名和密码的那个面上去Q这个变量哪里来的呢Q看看下面:
<bean id="secureHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="signonInterceptor"/>
</list>
</property>
<property name="urlMap">
<map>
<entry key="/shop/editAccount.do"><ref local="secure_editAccount"/></entry>
<entry key="/shop/listOrders.do"><ref local="secure_listOrders"/></entry>
<entry key="/shop/newOrder.do"><ref local="secure_newOrder"/></entry>
<entry key="/shop/viewOrder.do"><ref local="secure_viewOrder"/></entry>
</map>
</property>
</bean>
原来Q上面的signonInterceptor实现了preHandleQ因此在h上面的map面Ӟ首先要经q这个InterceptorQ看?SignonInterceptor的源码,原来在其中ؓsignon.jsp赋予一个signonForwardAction对象Q呵呵,ȝ明白了?br />3). 接下来去学习一下SignonControllerQ其M部分中可以看出,首先取出用户输入的username和passwordQ然后到数据库中验证有没有这个用P如果没有q个用户Q返回各错误面Q如果成功,首先生成一个UserSession对象Q在request的session加入q个 userSessionQ注意这部分代码中给ZPagedListHolder分页的简单用方法,关于分页昄Q以后再学习吧?br />3、登录成功后Q就可以Ҏ不同的用戯施不同的行ؓ了,取得用户信息Q无非就是从session取出userSession卛_?br />
http://www.javaresearch.org/article/showarticle.jsp?column=33&thread=50953
]]>