最近两星期在学习acegi,过程中感谢JavaEye,SpringSide和在网上提供acegi学习心得的网友们。
为了加深自己的认识,准备写下一些DEMO,希望可以给准备学习acegi的同学一些帮助。 作为安全服务离不开认证和授权这两个主要组成部分。而这篇文章就是针对acegi的认证服务。 学习Acegi-认证(authentication) 代码环境基于: JDK1.5 acegi1.0.3 spring2.0 IDE基于: Eclipse3.2+MyEclipse5.0.1 面向人员: 熟悉Eclipse+MyEclipse开发但刚开始了解acegi的人员。如果你是高手请指出文章不足之处。 1.建立一个MyEclipse的WebProject,把下列jar文件拷贝到项目的WEB-INF/lib目录: acegi-security-1.0.3.jar spring2.0.jar 费话说一句(占些字数):这是因为代码运行需要这两个包的支持。 2.修改WEB-INF下的web.xml文件,内容如下: <? xml version = " 1.0 " encoding = " UTF-8 " ?>
< web - app version = " 2.4 " xmlns = " http://java./xml/ns/j2ee " xmlns:xsi = " http://www./2001/XMLSchema-instance " xsi:schemaLocation = " http://java./xml/ns/j2ee http: // java./xml/ns/j2ee/web-app_2_4.xsd"> < display - name > acegi Example of liuyxit </ display - name > <!-- 定义应用的上下文参数,用于ContextLoaderListener --> < context - param > < param - name > contextConfigLocation </ param - name > < param - value > classpath:spring / applicationContext.xml </ param - value > </ context - param > <!-- acegi 的filter链代理 --> < 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 > < filter - mapping > < filter - name > Acegi Filter Chain Proxy </ filter - name > < url - pattern > /* </url-pattern> </filter-mapping> <!-- 装载应用软件的Spring上下文 要由WebapplicationContextUtils.getWebApplicationnContext(servletContext)得到. --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> </web-app> 其中FilterChainProxy实现了filter接口,它主要是实例化FilterChainProxy,并把所有动作交由FilterChainProxy处理。这样简化了web.xml的配置,并且充分利用了Spring IOC管理Bean的优势。 <? xml version = " 1.0 " encoding = " UTF-8 " ?>
< beans xmlns = " http://www./schema/beans " xmlns:xsi = " http://www./2001/XMLSchema-instance " xmlns:aop = " http://www./schema/aop " xmlns:tx = " http://www./schema/tx " xsi:schemaLocation = " http://www./schema/beans http://www./schema/beans/spring-beans-2.0.xsd http: // www./schema/aop http://www./schema/aop/spring-aop-2.0.xsd http: // www./schema/tx http://www./schema/tx/spring-tx-2.0.xsd " default - autowire = " byName " default - lazy - init = " true " > <!-- ======================== FILTER CHAIN ======================= --> < bean id = " filterChainProxy " class = " org.acegisecurity.util.FilterChainProxy " > < property name = " filterInvocationDefinitionSource " > < value > CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /** =authenticationProcessingFilter,exceptionTranslationFilter </value> </property> </bean> <!-- ======================== 认证filter ======================= --> <!-- 表单认证处理filter --> <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/> <property name="defaultTargetUrl" value="/userinfo.jsp"/> <property name="filterProcessesUrl" value="/j_acegi_security_check"/> </bean> <!-- 认证管理器 --> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"><!-- 可有多个认证提供器,其中一个证通过就可以了 --> <list> <ref local="daoAuthenticationProvider"/> <ref local="rememberMeAuthenticationProvider"/> </list> </property> </bean> <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> liuyxit=123,ROLE_SUPERVISOR user1=user1,ROLE_USER user2=user2,disabled,ROLE_USER </value> </property> </bean> <!-- 异常处理filter --> <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint"> <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl" value="/acegilogin.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> </beans>
4.在WebRoot下创建index.jsp(其实不要也没关系,主要是为了方便),直接转向用户资料显示页。内容如下 <% @ page language = " java " pageEncoding = " UTF-8 " %>
<! DOCTYPE HTML PUBLIC " -//W3C//DTD HTML 4.0 Transitional//EN " > < html > < head > <!-- < META HTTP - EQUIV = " Refresh " CONTENT = " 0;URL=user!list.rgb " > --> < META HTTP - EQUIV = " Refresh " CONTENT = " 0;URL=userinfo.jsp " > </ head > < body > < p > Loading </ p > </ body > </ html > 5.在WebRoot下创建userinfo.jsp,用于显示当前登陆的用户信息。内容如下 <% @ page language = " java " pageEncoding = " UTF-8 " %>
<% @ page import = " org.acegisecurity.context.SecurityContextHolder " %> <% @ page import = " org.acegisecurity.userdetails.* " %> <% String path = request.getContextPath(); String basePath = request.getScheme() + " :// " + request.getServerName() + " : " + request.getServerPort() + path + " / " ; %> <! DOCTYPE HTML PUBLIC " -//W3C//DTD HTML 4.01 Transitional//EN " > < html > < head > < base href = " <%=basePath%> " > < title > My JSP ‘ pass.jsp ‘ starting page </ title > < meta http - equiv = " pragma " content = " no-cache " > < meta http - equiv = " cache-control " content = " no-cache " > < meta http - equiv = " expires " content = " 0 " > < meta http - equiv = " keywords " content = " keyword1,keyword2,keyword3 " > < meta http - equiv = " description " content = " This is my page " > <!-- < link rel = " stylesheet " type = " text/css " href = " styles.css " > --> </ head > < body > 当前用户: <% Object obj = SecurityContextHolder.getContext().getAuthentication(); if ( null != obj){ Object userDetail = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username = "" ; if (userDetail instanceof UserDetails) { username = ((UserDetails) userDetail).getUsername(); } else { username = userDetail.toString(); } out.print(username); out.print( " <br><a href=\ " j_acegi_logout\ " >注销</a> " ); } else { out.print( " 当前没有有效的用户 " ); out.print( " <br><a href=\ " acegilogin.jsp\ " >登陆</a> " ); } %> </ body > </ html >
6.在WebRoot下创建acegilogin.jsp <% @ page language = " java " pageEncoding = " UTF-8 " %>
<% @ page import = " org.acegisecurity.ui.AbstractProcessingFilter " %> <% @ page import = " org.acegisecurity.ui.webapp.AuthenticationProcessingFilter " %> <% @ page import = " org.acegisecurity.AuthenticationException " %> < html > < head > < title > Login </ title > </ head > < body > < h1 > Login </ h1 > < P > Valid users: < P > < P > username < b > liuyxit </ b > , password < b > 123 </ b > (supervisor) < P > username < b > user1 </ b > , password < b > user1 </ b > (normal user) < p > username < b > user2 </ b > , password < b > user2 </ b > (user disabled) < p > <% String strError = request.getParameter( " login_error " ); if ( null != strError){ %> < font color = " red " > 你的登陆失败,请重试。 < BR >< BR > 原因: <%= ((AuthenticationException) session.getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage() %> </ font > <% } // end if %> < form action = " j_acegi_security_check " method = " POST " > < table > < tr >< td > User: </ td >< td >< input type = ‘ text ‘ name = ‘ j_username ‘ value = ‘ <%= session.getAttribute(AuthenticationProcessingFilter.ACEGI_SECURITY_LAST_USERNAME_KEY) %> ‘ ></ td ></ tr > < tr >< td > Password: </ td >< td >< input type = ‘ password ‘ name = ‘ j_password ‘ ></ td ></ tr > < tr >< td >< input type = " checkbox " name = " _acegi_security_remember_me " ></ td >< td > 2周内自动登录 </ 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 >
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< beans xmlns = " http://www./schema/beans " xmlns:xsi = " http://www./2001/XMLSchema-instance " xmlns:aop = " http://www./schema/aop " xmlns:tx = " http://www./schema/tx " xsi:schemaLocation = " http://www./schema/beans http://www./schema/beans/spring-beans-2.0.xsd http: // www./schema/aop http://www./schema/aop/spring-aop-2.0.xsd http: // www./schema/tx http://www./schema/tx/spring-tx-2.0.xsd " default - autowire = " byName " default - lazy - init = " true " > <!-- ======================== FILTER CHAIN ======================= --> < bean id = " filterChainProxy " class = " org.acegisecurity.util.FilterChainProxy " > < property name = " filterInvocationDefinitionSource " > < value > CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /** =authenticationProcessingFilter,logoutFilter,rememberMeProcessingFilter,exceptionTranslationFilter </value> </property> </bean> <!-- ======================== 认证filter ======================= --> <!-- 表单认证处理filter --> <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="authenticationFailureUrl" value="/acegilogin.jsp?login_error=1"/> <property name="defaultTargetUrl" value="/userinfo.jsp"/> <property name="filterProcessesUrl" value="/j_acegi_security_check"/> </bean> <!-- 利用cookie自动登陆filter --> <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> <property name="authenticationManager" ref="authenticationManager"/> <property name="rememberMeServices" ref="rememberMeServices"/> </bean> <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> <property name="userDetailsService" ref="inMemoryDaoImpl"/> <property name="key" value="javargb"/> </bean> <bean id="rememberMeAuthenticationProvider" class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> <property name="key" value="javargb"/> </bean> <!-- 注销处理filter --> <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> <constructor-arg value="/acegilogin.jsp"/> <!-- URL redirected to after logout --> <constructor-arg> <list> <ref bean="rememberMeServices"/> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> </list> </constructor-arg> </bean> <!-- 认证管理器 --> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> <property name="providers"><!-- 可有多个认证提供器,其中一个证通过就可以了 --> <list> <ref local="daoAuthenticationProvider"/> <ref local="rememberMeAuthenticationProvider"/> </list> </property> </bean> <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="userProperties"> <bean class="org.springframework.beans.factory.config.PropertiesFactoryBean"> <property name="location" value="classpath:acegi/users.properties"/> </bean> </property> </bean> --> <bean id="inMemoryDaoImpl" class="org.acegisecurity.userdetails.memory.InMemoryDaoImpl"> <property name="userMap"> <value> liuyxit=123,ROLE_SUPERVISOR user1=user1,ROLE_USER user2=user2,disabled,ROLE_USER </value> </property> </bean> <!-- 异常处理filter --> <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> <property name="authenticationEntryPoint"> <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> <property name="loginFormUrl" value="/acegilogin.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> </beans> 另要注意:Acegi默认的自动登陆设定参数名为_acegi_security_remember_me,注销链接为/j_acegi_logout。 CREATE TABLE USERS(
USERNAME VARCHAR( 50 ) NOT NULL PRIMARY KEY, PASSWORD VARCHAR( 50 ) NOT NULL, ENABLED BIT NOT NULL) INSERT INTO USERS(username,password,enabled) values( ‘ liuyxit ‘ , ‘ 123 ‘ , ‘ 1 ‘ ) INSERT INTO USERS(username,password,enabled) values( ‘ user1 ‘ , ‘ user1 ‘ , ‘ 1 ‘ ) INSERT INTO USERS(username,password,enabled) values( ‘ user2 ‘ , ‘ user2 ‘ , ‘ 0 ‘ ) CREATE TABLE AUTHORITIES( USERNAME VARCHAR( 50 ) NOT NULL, AUTHORITY VARCHAR( 50 ) NOT NULL, CONSTRAINT FK_AUTHORITIES_USERS FOREIGN KEY(USERNAME) REFERENCES USERS(USERNAME) ); INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( ‘ liuyxit ‘ , ‘ ROLE_SUPERVISOR ‘ ) INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( ‘ user1 ‘ , ‘ ROLE_USER ‘ ) INSERT INTO AUTHORITIES(USERNAME,AUTHORITY) values( ‘ user2 ‘ , ‘ ROLE_USER ‘ ) 这里我用的是acegi默认的数据结构,可以改只要你指定JdbcDaoImpl的authoritiesByUsernameQuery和usersByUsernameQuery属性就可以了。另AUTHORITIES表也要一同加入,原因acegi获得userDetail时,也会读取这个表的内容,否则会抛“nested exception is java.sql.SQLException: 对象名 ‘authorities‘ 无效”这个异常。 <? xml version="1.0" encoding="UTF-8" ?>
< beans xmlns ="http://www./schema/beans" xmlns:xsi ="http://www./2001/XMLSchema-instance" xmlns:aop ="http://www./schema/aop" xmlns:tx ="http://www./schema/tx" xsi:schemaLocation ="http://www./schema/beans http://www./schema/beans/spring-beans-2.0.xsd http://www./schema/aop http://www./schema/aop/spring-aop-2.0.xsd http://www./schema/tx http://www./schema/tx/spring-tx-2.0.xsd" default-autowire ="byName" default-lazy-init ="true" > < bean id ="dataSource" class ="org.springframework.jdbc.datasource.DriverManagerDataSource" > < property name ="driverClassName" > <!-- 请自行修改为对应你的数据库的驱动类 --> < value > net.sourceforge.jtds.jdbc.Driver </ value > </ property > < property name ="url" > <!-- 请自行修改为对应你的数据库URL --> < value > jdbc:jtds:sqlserver://localhost:1433/javauser </ value > </ property > < property name ="username" > < value > sa </ value > </ property > < property name ="password" > < value > javauser </ value > </ property > </ bean > <!-- ======================== FILTER CHAIN ======================= --> < bean id ="filterChainProxy" class ="org.acegisecurity.util.FilterChainProxy" > < property name ="filterInvocationDefinitionSource" > < value > CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**=authenticationProcessingFilter,logoutFilter,rememberMeProcessingFilter,exceptionTranslationFilter </ value > </ property > </ bean > <!-- ======================== 认证filter ======================= --> <!-- 表单认证处理filter --> < bean id ="authenticationProcessingFilter" class ="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter" > < property name ="authenticationManager" ref ="authenticationManager" /> < property name ="authenticationFailureUrl" value ="/acegilogin.jsp?login_error=1" /> < property name ="defaultTargetUrl" value ="/userinfo.jsp" /> < property name ="filterProcessesUrl" value ="/j_acegi_security_check" /> </ bean > <!-- 利用cookie自动登陆filter --> < bean id ="rememberMeProcessingFilter" class ="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter" > < property name ="authenticationManager" ref ="authenticationManager" /> < property name ="rememberMeServices" ref ="rememberMeServices" /> </ bean > < bean id ="rememberMeServices" class ="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices" > < property name ="userDetailsService" ref ="jdbcDaoImpl" /> < property name ="key" value ="javargb" /> </ bean > < bean id ="rememberMeAuthenticationProvider" class ="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider" > < property name ="key" value ="javargb" /> </ bean > <!-- 注销处理filter --> < bean id ="logoutFilter" class ="org.acegisecurity.ui.logout.LogoutFilter" > < constructor-arg value ="/acegilogin.jsp" /> <!-- URL redirected to after logout --> < constructor-arg > < list > < ref bean ="rememberMeServices" /> < bean class ="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" /> </ list > </ constructor-arg > </ bean > <!-- 认证管理器 --> < bean id ="authenticationManager" class ="org.acegisecurity.providers.ProviderManager" > < property name ="providers" > <!-- 可有多个认证提供器,其中一个证通过就可以了 --> < list > < ref local ="daoAuthenticationProvider" /> < ref local ="rememberMeAuthenticationProvider" /> </ list > </ property > </ bean > < bean id ="daoAuthenticationProvider" class ="org.acegisecurity.providers.dao.DaoAuthenticationProvider" > < property name ="userDetailsService" ref ="jdbcDaoImpl" /> </ bean > < bean id ="jdbcDaoImpl" class ="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl" > < property name ="dataSource" >< ref bean ="dataSource" /></ property > </ bean > <!-- 异常处理filter --> < bean id ="exceptionTranslationFilter" class ="org.acegisecurity.ui.ExceptionTranslationFilter" > < property name ="authenticationEntryPoint" > < bean class ="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint" > < property name ="loginFormUrl" value ="/acegilogin.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 > </ beans > 这时userDetailsService是通过实现类jdbcDaoImpl从数据库获得用户资料来作认证比较。 |
|