本文转自:http://itblood.com/spring-security-security-framework.html
安全常识:
Acegi介绍:
以声明式方式为基于Spring的web应用添加认证和授权控制
Acegi体系结构:
拦截器
认证管理器、访问控制管理器。
认证:
拦截器:
authenticationProcessingFilter
认证管理器:
AuthenticationManager:
使用多个提供者实施认证:
如:DaoAuthenticationProvider
提供者使用UserDetailsService获得认证用户信息。
URL资源的授权:
拦截器:
FilterSecurityInterceptor:
(1)使用认证管理器判断用户是否通过身份认证。
(2)调用访问控制器判断用户可否访问URL资源
(3)访问控制管理器投票
(4)无权访问,抛出异常,否则开放。
认证管理器:同上
访问控制管理器:
组织投票者投票,投票者依据对象定义源中定义的安全对象信息来投票。
accessDecisionManager有三类
业务方法的访问授权:
拦截器:
MethodSecurityInterceptor:
其它相关过滤器:
ExceptionTranslationFilter:异常转换过滤器
异常转换过滤器必须放在FilterSecurityInterceptor之前。
发现异常,先判断用户是否已经登录,未登录,导向登录;已登录,导向出错页面。
HttpSessionContextIntegrationFilter:
request处理前,该过滤器从Session中获取Authentication对象,在request完后
又把Authentication对象保存到Session中供下次request使用,此filter必须于其他Acegi Filter前使用。
使得多个请求可以共享Authentication对象
LogoutFilter:
退出系统的善后工作,主要是将安全上下文从Session中删除。
anonymousProcessingFilter:
未登录用户可以访问的资源,使用用户可以登录,访问首页等不需要保护的资源。
编程:
一、准备工作:
添加acegi的包
二、认证
1、在web.xml中,配置如下信息:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>AcegiFilterChainProxy</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>
<filter-mapping>
<filter-name>AcegiFilterChainProxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
说明:
FilterToBeanProxy:代理Acegi的过滤器
FilterChainProxy:将多个过滤组成一个过滤器链
2、添加登录页面:
<form name="form1" method="post" action="<c:url value="/j_acegi_security_check"/>">
用户名:<input type="text" name="j_username"/><br/>
密 码:<input type="password" name="j_password"/><br/>
<input type="submit" value="登录"/>
</form>
3、在Spring配置文件中,添加配置信息:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/tarena</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>1234</value>
</property>
</bean>
<bean id="filterChainProxy"
class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_UPPERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,logoutFilter
</value>
</property>
</bean>
<!--
1)request处理前,该过滤器从Session中获取Authentication对象,在request完后
又把Authentication对象保存到Session中供下次request使用
2)此filter必须于其他Acegi Filter前使用
-->
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="authenticationFailureUrl"
value="/index.jsp?login_error=1" />
<property name="defaultTargetUrl" value="/main.jsp" />
<!-- 过滤器处理的url -->
<property name="filterProcessesUrl"
value="/j_acegi_security_check" />
</bean>
<bean id="authenticationManager"
class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider" />
</list>
</property>
</bean>
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
<bean id="userDetailsService"
class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
<property name="dataSource" ref="dataSource" />
<property name="usersByUsernameQuery">
<value>
SELECT username,password,status FROM t_user WHERE status='1'
AND username = ?
</value>
</property>
<property name="authoritiesByUsernameQuery">
<value>
SELECT u.username,p.priv_name FROM t_user u,t_user_priv
p WHERE u.user_id =p.user_id AND u.username = ?
</value>
</property>
</bean>
<bean id="logoutFilter"
class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/index.jsp" />
<constructor-arg>
<list>
<bean
class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" />
</list>
</constructor-arg>
<property name="filterProcessesUrl" value="/j_acegi_logout" />
</bean>
<bean id="orderProcessService" class="service.OrderProcessService" />
4、建表,并插入记录
备注:
org.acegisecurity.util.FilterToBeanProxy:代理Acegi的过滤器
org.acegisecurity.util.FilterChainProxy:将多个过滤组成一个过滤器链
通过filterInvocationDefinitionSource属性定义多个相互链接的过滤器
其属性定义由两部分组成:
指令信息
具体的资源定义信息
httpSessionContextIntegrationFilter:
每次request前 HttpSessionContextIntegrationFilter从Session中获取Authentication对象,在request完后
又把Authentication对象保存到Session中供下次request使用,此filter必须其他Acegi filter前使用
org.acegisecurity.ui.webapp.AuthenticationProcessingFilter:
从请求中提取j_username和j_password参数的值,并使用这个信息构造Authentication实例,并封装成SecurityContext放入到
SecurityContextHolder中,然后启动身份认证的流程。
org.acegisecurity.providers.ProviderManager
将用户身份认证工作委托给多个提供者来完成
补充:
添加密码控制:
step1:
修改daoAuthenticationProvider的配置,添加
<property name="passwordEncoder">
<bean class="org.acegisecurity.providers.encoding.Md5PasswordEncoder"/>
</property>
<property name="saltSource">
<bean class="org.acegisecurity.providers.dao.salt.SystemWideSaltSource">
<property name="systemWideSalt" value="tarena"/>
</bean>
</property>
注意,密码盐的值应该和数据库中加密密码时采用的密码盐一致。
step2:
对数据库中的密码加密
加密方式可用org.acegisecurity.providers.encoding.Md5PasswordEncoder工具类:
比如:String encrtStr = mp.encodePassword("sdd", "tarena");
三、授权
URL资源的访问授权
1、修改spring配置文件
(1)修改filterChainProxy,增加
anonymousProcessingFilter,exceptionTranslationFilter,filterSecurityInterceptor
(2)添加anonymousProcessingFilter配置
<bean id="anonymousProcessingFilter"
class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter">
<property name="key" value="anonymousUser" />
<property name="userAttribute"
value="ANONYMOUSUSER,PRIV_ANONYMOUS" />
</bean>
<bean id="anonymousAuthenticationProvider"
class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
<property name="key" value="anonymousUser" />
</bean>
(3)修改authenticationManager增加认证提供者
<ref local="anonymousAuthenticationProvider" />
(4)添加filterSecurityInterceptor配置
<bean id="filterSecurityInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager"
ref="authenticationManager" />
<property name="accessDecisionManager"
ref="accessDecisionManager" />
<property name="objectDefinitionSource">
<value><![CDATA[
CONVERT_URL_TO_UPPERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/processOrder.jsp=PRIV_1
/viewStock.jsp=PRIV_2
/**=PRIV_ANONYMOUS,PRIV_COMMON
]]>
</value>
</property>
</bean>
<!--
经过投票机制来决定是否可以访问某一资源(URL或方法)
allowIfAllAbstainDecisions属性值是false,意思是如果所有的授权投票是都是弃权,则通不过授权检查
-->
<bean id="accessDecisionManager"
class="org.acegisecurity.vote.AffirmativeBased">
<property name="allowIfAllAbstainDecisions" value="true" />
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<!--必须是以rolePrefix设定的value开头的才会进行投票,否则为弃权-->
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter">
<property name="rolePrefix" value="PRIV_" />
</bean>
(5)添加异常转换过滤器
<bean id="exceptionTranslationFilter"
class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint">
<ref local="authenticationProcessingFilterEntryPoint" />
</property>
<property name="accessDeniedHandler">
<bean
class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
<property name="errorPage" value="/error.jsp" />
</bean>
</property>
</bean>
<bean id="authenticationProcessingFilterEntryPoint"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
<property name="loginFormUrl">
<value>/index.jsp</value>
</property>
</bean>
业务方法的访问授权
1、修改spring配置文件
(1)添加
<bean id="methodSecurityInterceptor"
class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="accessDecisionManager" />
<property name="objectDefinitionSource" ref="objectDefinitionSource" />
</bean>
<bean id="objectDefinitionSource"
class="org.acegisecurity.intercept.method.MethodDefinitionAttributes">
<property name="attributes">
<bean class="org.acegisecurity.annotation.SecurityAnnotationAttributes" />
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list>
<value>methodSecurityInterceptor</value>
</list>
</property>
<property name="beanNames">
<list>
<value>orderProcessService</value>
</list>
</property>
</bean>
2、在业务方法前,添加权限控制标注
@Secured({"PRIV_2"})
表示层的相关标签:
<authz:authorize>标签能够根据当前用户是否拥有恰当权限来决定显示或隐藏Web页面的内容
1、JSP页面引入
<%@ taglib prefix="authz" uri="http://acegisecurity.org/authz" %>
2、对需要控制输出的内容,添加
<authz:authentication operation="username"/>
<authz:authorize ifAllGranted="PRIV_2">
ifAllGranted——是一个由逗号分隔的权限列表,用户必须拥有所有列出的权限才列出;
ifAnyGranted——是一个由逗号分隔的权限列表,用户必须至少拥有其中的一个才列出;
ifNotGranted——是一个由逗号分隔的权限列表,用户必须不拥有其中的任何一个才列出。
添加安全通道功能
1、配置jboss服务器,启动HTTPS
step1:
生成keystore:
采用jdk的keytool命令即可
keytool -genkey -alias tomcat -keyalg RSA -keystore d:/tomcat.keystore
step2:
将tomcat.keystore复制到server/default/conf下
step3:
修改server/default/deploy/jboss-web.deployer/server.xml
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false"
keystoreFile="${jboss.server.home.dir}/conf/tomcat.keystore"
keystorePass="windows" sslProtocol="TLS" />
注意,此处的windows是step1中keystore密码
step4:
访问https://localhost:8443进行测试
2、修改spring配置文件:
step1:添加channelProcessingFilter,注意,其位置应是过滤器链中的第一个。
step2:
<!-- HTTPS安全通道 -->
<bean id="channelProcessingFilter"
class="org.acegisecurity.securechannel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager"/>
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_UPPERCASE_BEFORE_COMPARISON
/A/index.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="org.acegisecurity.securechannel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<bean
class="org.acegisecurity.securechannel.SecureChannelProcessor" />
<bean
class="org.acegisecurity.securechannel.InsecureChannelProcessor" />
</list>
</property>
</bean>
注意:channelProcessingFilter配置时,其filterInvocationDefinitionSource属性,要使用
正则表达式风格的URL路径匹配,此时,需要添加一个库,则ORO库。