posts - 0,  comments - 17,  trackbacks - 0
          來源:http://www.aygfsteel.com/baputista
          還以我傳統(tǒng)的例子為例:
          畢業(yè)設(shè)計選題系統(tǒng),三種角色:教師,學(xué)生,管理員,我想讓他們的登陸都在一個界面下自動識別,而無需進(jìn)行身份選擇,登陸后,他們將分別到各自的admin.jsp,stu.jsp,teacher.jsp
          在數(shù)據(jù)庫中的表結(jié)構(gòu)如下(很多屬性略):
          id--- user---password--type---about

          type是用來存儲用戶的類別,分別有a,t,s分別對應(yīng)三種角色
          about對應(yīng)的是acegi里所需要的enable,用戶是否可用

          在model里,我們采用了繼承關(guān)系:

          父類user:
          package subject.model;

          public abstract class User extends BaseObject
          {
           private Integer id;
           private String user;
           private String password;
           private String name;
           private String telphone;

          //set and get method 
           
           public abstract String getType(); //這個是用來反映用戶角色的關(guān)鍵函數(shù),在子類實現(xiàn),從而實現(xiàn)多態(tài)
          }

          子類的實現(xiàn):
          ======================
          package subject.model;

          import subject.Constants;

          public class Teacher extends User
          {
           private String level;         //教師的職稱

          //set and get method

           public String getType()
           {
            return Constants.TEACHER;
           }
          }
          ================
          package subject.model;

          import subject.Constants;

          public class Student extends User
          {
           private static final long serialVersionUID = 1L;

           private SchoolClass schoolClass;         //學(xué)生的班級
           private String sn;             //學(xué)生的學(xué)號

          //set and get method
           
           public String getType()
           {
            return Constants.STUDENT;
           }
          }
          =================
          package subject.model;

          import subject.Constants;

          public class Admin extends User
          {
           private String grade;           //管理員的級別
          //set and get method

           public String getType()
           {
            return Constants.ADMIN;
           }
          }

          對于三者所共有的屬性在數(shù)據(jù)庫里,都存在一個字段,而依據(jù)不同的角色擁有不同的含義,學(xué)生的班級則存放在了about里,只要學(xué)生有班級,他就able,否則就enable了!而管理員和教師則默認(rèn)為1!

          這種是屬于一個繼承樹存放在一個表的情況,Hibernate的配置如下:
          <hibernate-mapping>

           <class name="subject.model.User" discriminator-value="not null">

            <id name="id">
             <generator class="increment" />
            </id>
            
            <discriminator column="type" type="character" />
            
            <property name="user" />
            <property name="password" />
            <property name="name" />
            <property name="telphone" />

            <subclass name="subject.model.Admin" discriminator-value="a">
             <property name="grade" column="sn" />
            </subclass>
            
            <subclass name="subject.model.Teacher" discriminator-value="t">
             <property name="level" column="sn" />
            </subclass>
            
            <subclass name="subject.model.Student" discriminator-value="s">
             
             <property name="sn" />
             
             <many-to-one name="schoolClass" class="subject.model.SchoolClass"
              column="about" update="false" insert="false" />
              
            </subclass>

           </class>

          </hibernate-mapping>

          =============================================
          上面的這些都是模型的基礎(chǔ),下面再講怎么樣配合Spring和Acegi實現(xiàn)系統(tǒng)的安全與登陸
          在Spring中Hibernate的配置只介紹不說明:
          <!-- 定義DBCP數(shù)據(jù)源 -->
           <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://localhost/subject?useUnicode=true&amp;characterEncoding=gbk" />
            <property name="username" value="root" />
            <property name="password" value="" />
            <property name="maxActive" value="100" />
            <property name="maxIdle" value="30" />
            <property name="maxWait" value="1000" />
            <property name="defaultAutoCommit" value="true" />
            <property name="removeAbandoned" value="true" />
            <property name="removeAbandonedTimeout" value="60" />
           </bean>

           <!-- Hibernate -->
           <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="mappingResources">
             <list>
              <value>subject/model/User.hbm.xml</value>
             </list>
            </property>
            <property name="hibernateProperties">
             <props>
              <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
             </props>
            </property>
           </bean>

           <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory" />
           </bean>

          <!-- Dao對象 -->
          <bean id="userDao" class="subject.dao.hibernate.UserDaoImpl">
            <property name="sessionFactory" ref="sessionFactory" />
           </bean>

          <!-- 業(yè)務(wù)邏輯 -->
           <bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
            <property name="transactionManager" ref="transactionManager" />
            <property name="transactionAttributes">
             <props>
              <prop key="save*">PROPAGATION_REQUIRED</prop>
              <prop key="remove*">PROPAGATION_REQUIRED</prop>
              <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
             </props>
            </property>
           </bean>

          <bean id="userManager" parent="txProxyTemplate">
            <property name="target">
             <bean class="subject.service.impl.UserManagerImpl">
              <property name="userDao" ref="userDao" />
             </bean>
            </property>
           </bean>

          <!-- Struts -->
           <bean name="/user" class="subject.web.action.UserAction" singleton="false">
            <property name="userManager">
             <ref bean="userManager" />
            </property>
           </bean>
          ==================
          上面具體的不用了解,無非就是調(diào)用和數(shù)據(jù)庫的操作,
          下面就要對Acegi進(jìn)行聲明了:
          我不用Ctrl+c和Ctrl+V的方式對Acegi進(jìn)行介紹,沒有意義,隨便google就一大堆
          我們想主要在這樣的系統(tǒng)中需要的安全策略都有哪些?
          1.用戶的登陸
          2.防止多個用戶登陸一個帳號
          3.用戶的注銷
          4.防止非法用戶的訪問

          我這個程序所涉及到的只有這些,下面就進(jìn)行說明:

          在web.xml的聲明:
          <!-- Acegi安全控制 Filter 配置 -->
              <filter>
                  <filter-name>securityFilter</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>securityFilter</filter-name>
                  <url-pattern>/*</url-pattern>
              </filter-mapping>

          Acegi通過實現(xiàn)了Filter接口的FilterToBeanProxy提供一種特殊的使用Servlet Filter的方式,它委托Spring中的Bean -- FilterChainProxy來完成過濾功能,這樣就簡化了web.xml的配置,并且利用Spring IOC的優(yōu)勢。FilterChainProxy包含了處理認(rèn)證過程的filter列表,每個filter都有各自的功能。

          <!-- ======================== FILTER CHAIN ======================= -->
           <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy">
            <property name="filterInvocationDefinitionSource">
             <value>
              CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
              PATTERN_TYPE_APACHE_ANT
              
              /**=httpSessionContextIntegrationFilter,logoutFilter,authenticationProcessingFilter,
                   securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor
             </value>
            </property>
           </bean>

          大體上先介紹一下:
          httpSessionContextIntegrationFilter:每次request前 HttpSessionContextIntegrationFilter從Session中獲取Authentication對象,在request完后, 又把Authentication對象保存到Session中供下次request使用,此filter必須其他Acegi filter前使用,使之能跨越多個請求。
          logoutFilter:用戶的注銷
          authenticationProcessingFilter:處理登陸請求
          exceptionTranslationFilter:異常轉(zhuǎn)換過濾器
          filterInvocationInterceptor:在訪問前進(jìn)行權(quán)限檢查

          這些就猶如在web.xml聲明一系列的過濾器,不過當(dāng)把他們都聲明在spring中就可以享受Spring給我們帶來的方便了。

          下面就是對這些過濾器的具體聲明:
          只對有用的地方進(jìn)行聲明,別的地方幾乎都是默許的
          <!-- ======================== FILTER ======================= -->
           <bean id="httpSessionContextIntegrationFilter"
            class="org.acegisecurity.context.HttpSessionContextIntegrationFilter" />

           <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
            <constructor-arg value="/index.htm" />             離開后所轉(zhuǎn)向的位置
            <constructor-arg>
                      <list>
                          <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
                      </list>
                  </constructor-arg>
            <property name="filterProcessesUrl" value="/logout.htm" />        定義用戶注銷的地址,
           </bean>

          下面的這個過濾器,我們根據(jù)自己的需求有了自己的實現(xiàn):

           <bean id="authenticationProcessingFilter" class="subject.web.filter.UserAuthenticationProcessingFilter">
            <property name="authenticationManager" ref="authenticationManager"/>  下面會介紹的用來起到認(rèn)證管理的作用
            <property name="authenticationFailureUrl" value="/login.htm?error=wrong"/>  登陸失敗的地址
            <property name="defaultTargetUrl" value="/login.htm"/>       登陸成功的地址
            <property name="filterProcessesUrl" value="/j_security_check"/>      登陸請求的地址
            <property name="userManager" ref="userManager"/>        自己添加的屬性,這樣就可以訪問到我們的業(yè)務(wù)邏輯
            <property name="exceptionMappings">   出現(xiàn)異常所對應(yīng)的地址
                      <value>
                          org.acegisecurity.AuthenticationException=/login.htm?error=fail     登陸失敗                org.acegisecurity.concurrent.ConcurrentLoginException=/login.htm?error=too        已登陸了
                      </value>
                  </property>
           </bean>
           
           <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/>

           <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter">
            <property name="authenticationEntryPoint">
             <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
              <property name="loginFormUrl" value="/login.htm?error=please"/>//如果用戶沒登陸就想訪問,先到這里登陸吧
              <property name="forceHttps" value="false"/>
             </bean>
            </property>
           </bean>
           
           <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
            <property name="authenticationManager" ref="authenticationManager"/>       認(rèn)證服務(wù)
            <property name="accessDecisionManager">
             <bean class="org.acegisecurity.vote.AffirmativeBased">
              <property name="allowIfAllAbstainDecisions" value="false"/>
              <property name="decisionVoters">
               <list>
                <bean class="org.acegisecurity.vote.RoleVoter">
                              <property name="rolePrefix" value=""/>         //這里定義數(shù)據(jù)庫中存放的角色和我們在這里聲明的角色間是否需要加個前綴?我沒加
                          </bean>
               </list>
              </property>
             </bean>
            </property>
            <property name="objectDefinitionSource">
                      <value>
                          PATTERN_TYPE_APACHE_ANT
                         
                          /admin.htm*=a         這里就是數(shù)據(jù)庫中對應(yīng)的tyep a
                          /student*=s           由于沒有前綴和數(shù)據(jù)庫里一樣
                          /teacher*=t
                      </value>
                  </property>
           </bean>
           
           <bean id="loggerListener"
                    class="org.acegisecurity.event.authentication.LoggerListener"/>       記錄事件

          下面就要說明我們的認(rèn)證服務(wù)了,其起到的關(guān)鍵作用就是用來保證用戶登陸身份的驗證:

          它將驗證的功能委托給多個Provider,并通過遍歷Providers, 以保證獲取不同來源的身份認(rèn)證,若某個Provider能成功確認(rèn)當(dāng)前用戶的身份,authenticate()方法會返回一個完整的包含用戶授權(quán)信息的Authentication對象,否則會拋出一個AuthenticationException。

          先聲明一個管理器吧,在上面的過濾器中都已經(jīng)用到過了
          <!-- ======================== AUTHENTICATION ======================= -->
           <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
            <property name="providers">
             <list>
              <ref local="daoAuthenticationProvider" />   我僅僅用到 從數(shù)據(jù)庫中讀取用戶信息驗證身份
             </list>
            </property>
            <property name="sessionController">
             <bean id="concurrentSessionController"
              class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl">
              <property name="maximumSessions">
               <value>1</value>每個用戶同時登陸一位
              </property>
              <property name="sessionRegistry">
               <bean id="sessionRegistry" class="org.acegisecurity.concurrent.SessionRegistryImpl" />
              </property>
              <property name="exceptionIfMaximumExceeded" value="true" />
             </bean>
            </property>
           </bean>
           來實現(xiàn)唯一的一個Provider,從數(shù)據(jù)庫驗證身份
           <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
            <property name="userDetailsService">
             <bean id="jdbcDaoImpl"
                      class="org.acegisecurity.userdetails.jdbc.JdbcDaoImpl">
                    <property name="dataSource" ref="dataSource"/>
                    <property name="usersByUsernameQuery">
                        <value>
                            select user,password,about from user where user = ?        查找用戶的查詢語句,只需要把你數(shù)據(jù)庫中的用戶和密碼以及enable相對應(yīng)上就行
                        </value>
                    </property>
                    <property name="authoritiesByUsernameQuery">
                        <value>
                            select user,type from user where user = ?           這里就是把用戶和權(quán)限對應(yīng)上,在appfuse中用的兩個表,我都放一個表里了,所以就用這一個就行問題的關(guān)鍵是要讓它能找到兩個字段,構(gòu)成一個對象
                        </value>
                    </property>
                </bean>
            </property>
            <property name="userCache"> 緩存都這么寫:
             <bean class="org.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
              <property name="cache">
               <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
                <property name="cacheManager">
                 <bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
                </property>
                <property name="cacheName" value="userCache"/>
               </bean>
              </property>
             </bean>
            </property>
           </bean>

          ==============
          對于上面登陸請求的處理器我借鑒了springSide,實現(xiàn)的方法如下:
          package subject.web.filter;

          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.http.HttpSession;

          import org.acegisecurity.Authentication;
          import org.acegisecurity.context.SecurityContext;
          import org.acegisecurity.context.SecurityContextHolder;
          import org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;
          import org.acegisecurity.userdetails.UserDetails;

          import subject.Constants;
          import subject.model.User;
          import subject.service.UserManager;

          public class UserAuthenticationProcessingFilter extends
            AuthenticationProcessingFilter
          {
           private UserManager userManager;

           public void setUserManager( UserManager userManager )
           {
            this.userManager = userManager;
           }

           protected boolean requiresAuthentication( HttpServletRequest request ,
             HttpServletResponse response )
           {
            boolean requiresAuth = super.requiresAuthentication( request, response );
            HttpSession httpSession = null;
            try
            {
             httpSession = request.getSession( false );
            }
            catch ( IllegalStateException ignored )
            {
            }
            if ( httpSession != null )
            {
             if ( httpSession.getAttribute( Constants.USER ) == null )
             {
              if ( !requiresAuth )
              {
               SecurityContext sc = SecurityContextHolder.getContext();
               Authentication auth = sc.getAuthentication();
               if ( auth != null
                 && auth.getPrincipal() instanceof UserDetails )
               {
                UserDetails ud = (UserDetails) auth.getPrincipal();//上面聲明的sql無非就是要包裝成這個對象
                User user = userManager.getUser( ud.getUsername() );從業(yè)務(wù)邏輯里找到用戶,放到session里
                httpSession.setAttribute( Constants.USER, user );
               }
              }
             }
            }
            return requiresAuth;
           }
          }

          在看看我的login.htm在登陸成功時是怎么工作的吧?
          public class UserAction extends BaseAction
          {
           private UserManager mgr;

           public void setUserManager( UserManager mgr )
           {
            this.mgr = mgr;
           }

           public ActionForward login( ActionMapping mapping , ActionForm form ,
             HttpServletRequest request , HttpServletResponse response )
             throws Exception
           {
            User user = (User) getSessionObject( request, Constants.USER );
            ActionMessages msg = new ActionMessages();
            if ( user != null )
            {
             return new ActionForward(  user.getType() + ".htm", true );成功就去type.htm
            }
            else
            {
             String error = getParameter( request, Constants.ERROR );
             if ( error != null )對于不同的錯誤,都加以提示
             {
              if ( error.equalsIgnoreCase( "wrong" ) )
               msg.add( "msg", new ActionMessage( "fail.login.wrong" ) );
              else if ( error.equalsIgnoreCase( "too" ) )
               msg.add( "msg", new ActionMessage( "fail.login.too" ) );
              else if ( error.equalsIgnoreCase( "fail" ) )
               msg.add( "msg", new ActionMessage( "fail.login.fail" ) );
              else
               msg.add( "msg", new ActionMessage( "fail.login.please" ) );
             }
             else
              msg.add( "msg", new ActionMessage( "fail.login.please" ) );
            }
            saveErrors( request, msg );
            return mapping.findForward( "fail" );
           }

          }

          當(dāng)然,Acegi需要介紹的東西太多了,我只把我這次認(rèn)為有必要解釋的東西寫在了上面讓大家來參考,作為能google到的東西,比如對于認(rèn)證的方式還有很多,我就沒有詳細(xì)的介紹,在學(xué)習(xí)Acegi過程中,把它自帶的例子弄清楚很關(guān)鍵,希望大家一起學(xué)習(xí)一起共勉!

          posted on 2008-04-21 17:57 xyz 閱讀(370) 評論(0)  編輯  收藏 所屬分類: 網(wǎng)絡(luò)文摘

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          留言簿

          隨筆檔案(1)

          文章分類(44)

          文章檔案(46)

          收藏夾(1)

          Adobe

          AOP

          API

          appServer

          BI

          c

          • c-free
          • codeblocks
          • codelite
          • CodeLite IDE 是一個強(qiáng)大的開源,跨平臺的 C/C++整合開發(fā)環(huán)境. 支持包括 Windows、Linux 和 Mac 系統(tǒng)下運行
          • codelite官網(wǎng)
          • dev-c++
          • Dev-C++是一個C&C++開發(fā)工具,它是一款自由軟件,遵守GPL協(xié)議。
          • GCC
          • GCC 原名為 GNU C 語言編譯器(GNU C Compiler),因為它原本只能處理 C語言。GCC 很快地擴(kuò)展,變得可處理 C++。之后也變得可處理 Fortran、Pascal、Objective-C、Java, 以及 Ada 與其他語言。

          Cache

          CMS

          DB

          eclipse

          FreeMarker

          hibernate

          html5

          ibatis

          java

          jquery

          js

          json

          Linux

          Log

          mail server

          mobile

          mysql

          oauth

          openID

          other

          PHP

          portal

          report

          Scheduler

          schema

          Security

          SOA

          spring

          struts

          UI原型設(shè)計

          w3c

          Wap

          webservice

          xml

          供應(yīng)鏈管理

          博客鏈接

          好網(wǎng)站

          工作流

          開源網(wǎng)

          招聘

          插件下載

          操作系統(tǒng)

          構(gòu)建可伸縮的系統(tǒng)

          構(gòu)建工具

          測試

          游戲

          源碼托管

          經(jīng)營

          資源

          金融/財務(wù)

          搜索

          •  

          最新評論

          主站蜘蛛池模板: 新化县| 平乐县| 汾阳市| 南昌县| 饶河县| 宜春市| 府谷县| 灯塔市| 华安县| 吉木萨尔县| 农安县| 洛阳市| 平乐县| 茂名市| 益阳市| 虹口区| 岢岚县| 沁源县| 敖汉旗| 娱乐| 蛟河市| 尼勒克县| 安宁市| 莱阳市| 巍山| 镇雄县| 霍邱县| 肥乡县| 凭祥市| 湛江市| 泾源县| 石城县| 英超| 清徐县| 汨罗市| 永宁县| 沁源县| 七台河市| 北海市| 玉林市| 海晏县|