acegi+ssh动态实现基于角色的权限管理(二)

 现在设置认证管理器
<!-- * set authenticationManager , an important manager-->
 <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">
  <property name="providers">
   <list>
    <ref bean="daoAuthenticationProvider"/>
    <bean class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider">
     <property name="key" value="anonymousUser"/>
    </bean>
    <bean class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider">
     <property name="key" value="rememberMeUser"/>
    </bean>
   </list>
  </property>
  <!-- here you can set sessionController for user can't repeat (dulplicate)-->
  <property name="sessionController" ref="concurrentSessionController"/>
 </bean>

   三种认证提供者

daoAuthenticationProvider通过数据库方式来进行认证,也可以通过配置文件实现

AnonymousAuthenticationProvider,匿名用户认证,key值必须与     anonymouseProcessingFilter的key值一样,否则无法获取匿名用户的信息.

RememberMeAuthenticationProvider通过cookie认证,key值和rememberMeServices

的key值相同,否则无法取得相应的cookie信息.

sessionController,限制同一用户的登录次数,也就是同一用户在不同httpSession

中登录的次数

 

 <!-- set concurrentSessionControllter -->
 <bean id="concurrentSessionController" class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl">
  <property name="maximumSessions" value="1"/>
  <property name="exceptionIfMaximumExceeded" value="true"/>
  <property name="sessionRegistry" ref="sessionRegistryImpl"/>
 </bean>
 <!-- set sessionRegistryImpl -->
 <bean id="sessionRegistryImpl" class="org.acegisecurity.concurrent.SessionRegistryImpl"/>

 这样可以限制同一用户在不同session中的次数,sessionRegistry对用户与sessionId以map的结构进行记录,但出现的一个问题就是,在用户logout时,或者session已经invalidate或expired时,他不会移除在sessionRegistry中保存的信息,

虽然sessionRegistry也提供了removeSessionInformation(String sessionID)这个方法.

  结果就是,在下次登录的时候,认证管理器先在sessionRegistry判断有此用户没,

如果有,并超过maximumSessions,就抛出异常,并无法认证.而sessionRegistry以map

结构,并用static final形式存放信息,在不重启服务器的情况下,用户session注销后是没办法removeSessionInformation的.

 

我的解决办法是,通过acegi提供的listenter,在用户session destroy,执行removeSessionInformation.

public class HttpSessionEventListener extends HttpSessionEventPublisher {
 private static final Log logger = LogFactory.getLog(HttpSessionEventListener.class);
 
 @Override
 public void sessionDestroyed(HttpSessionEvent event) {
  // TODO 自动生成方法存根
  removeSessionRegistry(event);
  super.sessionDestroyed(event);
 }

 /**
  * romove session in sessionRegistry
  * @param event
  */
 private void removeSessionRegistry(HttpSessionEvent event) {
  // TODO 自动生成方法存根
  HttpSession session =event.getSession();
  ServletContext sc = session.getServletContext();
  
  WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(sc);
  if(wac == null) {
   logger.info("can't webApplication Context,maybe the key in servletContext is different");
   return ;
  }

  SessionRegistry sessionRegistry = null;
  try {
   sessionRegistry = (SessionRegistry) wac.getBean("sessionRegistryImpl");
  } catch (BeansException e) {
   // TODO 自动生成 catch 块
   logger.error("get SessionRegistry bean error,make sure bean name 'sessionRegistryImpl' exists",e);
  }
  
  sessionRegistry.removeSessionInformation(session.getId());
 }

 }

}

缺点是:wac.getBean("sessionRegistryImpl");bean name 写死了,缺乏灵活

web.xml添加如下:

<!-- httpSessionEvent ,listener session state -->
  <listener>
   <listener-class>com.runsa.components.acegi.support.HttpSessionEventListener</listener-class>
  </listener>

虽然解决了当前问题,但仍然避免不了有些用户,不通过安全退出,直接关闭浏览器,

在session没有destroy时,再次登录,也会出错.在javaeye也看到类似有人提出这样的问题,捕捉window.close()事件,利用ajax在关闭浏览器的时候,进行session.invalidate(),但是,window.close()不那么容易捕捉的吧,而且个浏览器差异也是有的。

 

通过db认证用户

<!-- set daoAuthenticationProvider -->
 <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider">
  <property name="userDetailsService" ref="hibernateDaoImpl"/>
  <property name="userCache" ref="userCache"/>
  <!-- here you can set passwordEncoder -->
 </bean>

userDetailsService,保存userContext的,也就是用户的信息和相应的权限.acegi默认提供jdbcDaoImpl的实现,这里我使用hibernate自己实现了

<!-- set hibernateDaoImpl,and you can implements daoImpl with Hibernate for yourself  -->
 <bean id="hibernateDaoImpl" class="com.runsa.components.acegi.support.HibernateDaoImpl">
  <property name="userService" ref="usersService"/>
  <property name="usernameBasedPrimaryKey" value="false"/>
 </bean>

下边是实现的code:

public static final String DEF_USERS_BY_USERNAME_QUERY = "SELECT new Users(u.userName,u.userPass,u.enabled) FROM Users u WHERE userName = ?";
    public static final String DEF_Role_BY_USERNAME_QUERY = "SELECT r.roleName FROM Role r inner join r.userRoles ur inner join ur.users u WHERE  u.userName = ?";


    private UsersService userService;
    private String roleByUsernameQuery;
    private String rolePrefix = "";
    private String usersByUsernameQuery;
    private boolean usernameBasedPrimaryKey = true;

 

    public HibernateDaoImpl() {
        usersByUsernameQuery = DEF_USERS_BY_USERNAME_QUERY;
        roleByUsernameQuery = DEF_Role_BY_USERNAME_QUERY;
    }

  

 protected void addCustomRole(String username, List roles) {}
    public boolean isUsernameBasedPrimaryKey() {
        return usernameBasedPrimaryKey;
    }

    @SuppressWarnings("unchecked")
 public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException, DataAccessException {
        List <UserDetails>users = usersByUsername(username);

        if (users.size() == 0) {
            throw new UsernameNotFoundException("User not found");
        }

        UserDetails user =  users.get(0); // contains no GrantedAuthority[]

        List dbRoles = roleByUsername(user.getUsername());

        addCustomRole(user.getUsername(), dbRoles);

        if (dbRoles.size() == 0) {
            throw new UsernameNotFoundException("User has no GrantedAuthority");
        }

        GrantedAuthority[] arrayAuths = (GrantedAuthority[]) dbRoles.toArray(new GrantedAuthority[dbRoles.size()]);

        String returnUsername = user.getUsername();

        if (!usernameBasedPrimaryKey) {
            returnUsername = username;
        }

        return new User(returnUsername, user.getPassword(), user.isEnabled(), true, true, true, arrayAuths);
    }

    /**
     * find roles by username
     * @param username
     * @return
     */
    @SuppressWarnings("unchecked")
 private List roleByUsername(String username) {
  // TODO 自动生成方法存根
     roleByUsernameQuery=DEF_Role_BY_USERNAME_QUERY;
     List roleList = userService.findByParam(roleByUsernameQuery,username);
     List <GrantedAuthorityImpl>roles = new Vector();
     
   for (Iterator iter = roleList.iterator(); iter.hasNext();) {
    String role = (String) iter.next();
    
    String roleName = rolePrefix + role;
    GrantedAuthorityImpl grantedAuth =new GrantedAuthorityImpl(roleName);
    roles.add(grantedAuth);
   }
  return roles;
 }

    /**
     * find users by username
     * @param username
     * @return
     */
 @SuppressWarnings("unchecked")
 private List usersByUsername(String username) {
  // TODO 自动生成方法存根
     List userList = userService.findByParam(usersByUsernameQuery,username);
     List <UserDetails>users =new Vector();
     for (Iterator iter = userList.iterator(); iter.hasNext();) {
   Users user = (Users) iter.next();
   UserDetails userDetails = new User(user.getUserName(), user.getUserPass(), user.isEnabled(),
     true, true, true, new GrantedAuthority[] {new GrantedAuthorityImpl("HOLDER")});
   users.add(userDetails);
     }
  return users;
 }

主要实现的方法public UserDetails loadUserByUsername(String username)通过用户名查询用户资料,然后再通过用户用查询用户的具有角色(权限).

 

也可以设置passwordEncoder对密码加密,acegi提供md5,sha,等好几种加密方式.

显然,在这里认证设置了加密方式,在获取用户信息的时候,对用户的密码也需要相同的加密方式.

 acegi的资源文件默认提供E文的,要自己实现本地化

 <!-- set load message resource -->
 <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
  <property name="basename" value="/WEB-INF/classes/messages_zh"/>
 </bean>

 

下边设置资源从数据库获取,并实现动态更新

 <!-- * set filterDefinitionSource for myself  -->
 <bean id="filterDefinitionSource" class="com.runsa.components.acegi.inteceptor.web.DBFilterInvocationDefinationSource">
  <property name="convertUrlToLowercaseBeforeComparison" value="true"/>
  <property name="useAntPath" value="true"/>
  <property name="acegiCacheManager" ref="acegiCacheManager"/>
 </bean>

 

....next page

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值