aceig构建基于内存的用户信息认证
目前acegi(Spring Security)主要是针对简化Java EE企业级应用中安全的开发和部署而来的!acegi采纳了Spring Framework来架构自身,并在后来成为了spring官方的子项目,即Spring Security。
因此我们建议acegi作用的目标应用最好是基于spring Framework开发的
实例介绍:
本实例是基于acegi开发的,在index.jsp页面中,提供了securedpage.jsp页面的html超链接,一旦用户点击它,就会弹出登陆对话框(HTTP BASIC认证)。然后,开发者提供相应的用户凭证,才能够进入受保护的页面。
项目的目录结构以及所需要的jar如下:
两个web页面:
index.jsp
<%@ page session="false" pageEncoding="GBK" contentType="text/html; charset=GBK" %>
<title>acegifirstdemo应用首页</title>
<html>
<body>
<a href="securedpage.jsp">受保护的页面(securedpage.jsp)</a>
</body>
</html>
securedpage.jsp
<%@ page session="false" pageEncoding="GBK" contentType="text/html; charset=GBK" %>
<title>acegifirstdemo应用securedpage页面</title>
<html>
<body>
欢迎您的到来到securedpage.jsp,回 <a href="index.jsp">首页</a>
</body>
</html>
在web.xml中配置spring和acegi过滤器(由于acegi本身就是基于过滤器驱动的)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>acegi_security_100</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/classes/applicationContext.xml</param-value>
</context-param>
<!-- 过滤器定义 -->
<filter>
<filter-name>Acegi Filter Chain Proxy</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>
<!-- 拦截所有的web访问 -->
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
最后要配置受管filterChainProxy对象.在ApplicationContext.xml中定义!它引用了四个过滤器:
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"> <property name="filterInvocationDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
第一个过滤器:httpSessionContextIntegrationFilter尝试从HttpSession中获取SecurityContext,如果HttpSession不存在或无法获取到,则重新创建SecurityContext,并将SecurityContext存放到SecurityContextHolder中。然后再依次调用其他过滤器basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
<bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
<property name="allowSessionCreation" value="false" />
</bean>
第二个过滤器:basicProcessingFilter负责完成用户的认证。
①用户没有录入凭证信息
用户根本就没有提供凭证信息,该用户访问的资源又受到acegi的保护,则过滤器ExceptionTranslationFilter会调用AuthenticationEntryPoint构造http响应头信息,浏览器在收到响应信息后会弹出对话框要求用户输入用户名和密码
②用户录入了凭证信息
当用户访问的web资源受到保护时,这个过滤器便会介入进来,当访问受到保护的web资源时,如果用户已经在http请求头信息中提供了相应的用户凭证信息,则basicProcessingFilter会调用相应的认真管理器(ProviderManager)。如果认证失败,则过滤器basicProcessingFilter会调用AuthenticationEntryPoint构造http响应头信息,浏览器在收到响应信息后会弹出对话框要求用户输入用户名和密码
在认证过程中,会将请求委托给相应的dao认证提供者!这里使用简单的基于内存的InMemoryDaoImpl类,InMemoryDaoImpl对象会采用‘用户名=密码,角色集合,启用标志位’形式来存储用户和角色的信息。
<!-- ①authenticationManager: 认证器,认证用户名及密码是否有效
②authenticationEntryPoint:认证入口,向浏览器发送响应信息,浏览器弹出提示窗口,提示用户输入用户名及密码
-->
<bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint" />
</bean>
<!-- 对应了Http Basic认证,认证入口,向浏览器发送响应信息,浏览器弹出名为"Acegi First Demo Realm"的提示窗口 -->
<bean id="basicProcessingFilterEntryPoint"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="Acegi First Demo Realm" />
</bean>
<!--
认证管理器,包含多个认证器,认证器可以存在多个,但只要有一个认证器认证通过则认证通过
-->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider" />
</list>
</property>
</bean>
<!-- Dao认证提供者 -->
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl" />
</bean>
<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>admin=password,ROLE_ADMIN
GAO=WEIGANG,ROLE_USER
</value>
</property>
</bean>
第三个过滤器:exceptionTranslationFilter负责处理认证和授权过程中出现的异常
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint" />
<property name="createSessionAllowed" value="false" />
</bean>
第四个过滤器:FilterSecurityInterceptor负责完成用户的授权。
<!-- 访问页面控制器:控制用户到底有没有权限访问该页面
-->
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" />
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/securedpage.jsp=ROLE_ADMIN,ROLE_USER
/index.jsp=ROLE_USER
</value>
</property>
</bean>
<!-- 访问决策管理器,统计投票结果 -->
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
最后,配置logger监听器,可以查看acegi认证和授权信息
<!-- 监听各种用户认证事件 --> <bean id="authenticationLoggerListener" class="org.acegisecurity.event.authentication.LoggerListener" />
<!-- 监听各种用户授权事件 -->
<bean id="authorizationLoggerListener" class="org.acegisecurity.event.authorization.LoggerListener" />
完整的ApplicationContext.xml配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="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.xsd">
<bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/**=httpSessionContextIntegrationFilter,basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
</value>
</property>
</bean>
<!--
尝试从HttpSession中获取SecurityContext,如果HttpSession不存在或无法获取到,则重新创建SecurityContext,并将SecurityContext存放在
SecurityContextHolder中。然后再一次调用其他过滤器basicProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor
-->
<bean id="httpSessionContextIntegrationFilter"
class="org.acegisecurity.context.HttpSessionContextIntegrationFilter">
<property name="allowSessionCreation" value="false" />
</bean>
<!--
①authenticationManager: 认证器,认证用户名及密码是否有效
②authenticationEntryPoint:认证入口,向浏览器发送响应信息,浏览器弹出提示窗口,提示用户输入用户名及密码
-->
<bean id="basicProcessingFilter" class="org.acegisecurity.ui.basicauth.BasicProcessingFilter">
<property name="authenticationManager" ref="authenticationManager" />
<property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint" />
</bean>
<!-- 对应了Http Basic认证,认证入口,向浏览器发送响应信息,浏览器弹出名为"Acegi First Demo Realm"的提示窗口 -->
<bean id="basicProcessingFilterEntryPoint"
class="org.acegisecurity.ui.basicauth.BasicProcessingFilterEntryPoint">
<property name="realmName" value="Acegi First Demo Realm" />
</bean>
<!--
认证管理器,包含多个认证器,认证器可以存在多个,但只要有一个认证器认证通过则认证通过
-->
<bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
<property name="providers">
<list>
<ref local="daoAuthenticationProvider" />
</list>
</property>
</bean>
<!-- Dao认证提供者 -->
<bean id="daoAuthenticationProvider"
class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="inMemoryDaoImpl" />
</bean>
<bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl">
<property name="userMap">
<value>admin=password,ROLE_ADMIN
GAO=WEIGANG,ROLE_USER
</value>
</property>
</bean>
<bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
<property name="authenticationEntryPoint" ref="basicProcessingFilterEntryPoint" />
<property name="createSessionAllowed" value="false" />
</bean>
<!--
访问页面控制器:控制用户到底有没有权限访问该页面
-->
<bean id="filterInvocationInterceptor"
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
<property name="authenticationManager" ref="authenticationManager" />
<property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" />
<property name="objectDefinitionSource">
<value>
PATTERN_TYPE_APACHE_ANT
/securedpage.jsp=ROLE_ADMIN,ROLE_USER
/index.jsp=ROLE_USER
</value>
</property>
</bean>
<!-- 访问决策管理器,统计投票结果 -->
<bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<ref bean="roleVoter" />
</list>
</property>
</bean>
<bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" />
<!-- 监听各种用户认证事件 -->
<bean id="authenticationLoggerListener" class="org.acegisecurity.event.authentication.LoggerListener" />
<!-- 监听各种用户授权事件 -->
<bean id="authorizationLoggerListener" class="org.acegisecurity.event.authorization.LoggerListener" />
</beans>
说明:
①账户存在形式
②数据存在形式
商业性 Web 站点大都提供站点认证功能以保护某些受限资源,HTTP 协议和 J2EE 规范对 Web 站点的认证过程都已有了详尽的定义,常见浏览器都能根据相应协议提供对应的界面形式帮助用户完成站点的认证过程。
出于安全性的需要和用户授权管理的考虑,常见的 J2EE 站点对特定资源都会加入认证/授权机制。例如一个公网上的论坛,一个只对特定用户开放的 RSS 或 Atom Feed,这些资源都必须在确信访问者为被授权用户时才能向访问者开放。为了实现这样的功能,J2EE 站点通常会采用某种站点认证机制,其中常见的有 HTTP Basic 认证和 J2EE Form-Based 认证。
HTTP Basic 认证是 HTTP 认证协议(rfc2617)所定义的标准认证方式。要求 HTTP Basic 认证的服务器会在客户端访问受保护资源时向客户端发出请求,要求客户端上传用户名和密码对。服务器在收到用户名/密码并验证通过后,才将保护资源的内容返回给客户端。
HTTP Basic 认证方式使用 base64 编码方式传送用户名和密码,而 base64 仅仅是一种公开的编码格式而非加密措施,因而如果信道本身不使用 SSL 等安全协议,用户密码较容易被截获。
J2EE Form-Based 认证
Form-Based 认证不同于 HTTP Basic 认证,它是 J2EE 对于认证方式的一种扩展。它使用自定义的 HTML 表单(通常为 login.jsp)作为输入用户名和密码的用户界面,最终将用户在表单上填入的用户名/密码提交至服务器。
Form-Based 认证方式在 J2EE 站点中更为常见。这一方面是由于它提供了自定义的用户名密码输入界面;另一方面它的传输也更为安全,通常情况下 login.jsp 会被配置为需要使用 SSL 信道访问,这样用户名和密码的传送就被安全信道所保护,而较难被非法截取。
参考:
Web站点认证有哪些方式: