Configuração do Framework Acegi no aplicativo Web JAVA J2EE

Dezembro 2016


A configuração passa por cinco etapas:

Etapa n° 1: Colocar o acegi-security-1.0.0-RC2.jar no diretório lib do seu aplicativo:


Etapa n° 2: Configuração dos listeners e filtros:

  • Para os filtros, existem três a serem integrados:
    • LocaleFilter: este filtro abrange o filtro OncePerRequestFilter, que é um filtro de base. Ele garante a sua execução apenas uma vez por pedido, em qualquer contêiner servlet. Ele fornece um método doFilterInternal com como argumentos HttpServletRequest e HttpServletResponse.
    • LocaleRequestWrapper: este filtro abrange o HttpServletRequestWrapper que configura a localização do usuário.
    • UserSecurityAdvice: este filtro é um advice utilizado pelo Acegi. Ele fortalece a segurança: só ajuda o administrador para que ele altere os usuários ou ajuda o usuário para que ele altere as suas informações.
  • Sobre os listeners, encontramos :
    • StartupListener: este ouvinte inicializa as configurações de segurança (como a criptografia de senha) e carrega a lista de funções possíveis configuradas no banco de dados, para acessar o aplicativo.
    • UserCounterListener: este ouvinte controla o número de conexões ativas para cada usuário, referindo-se à configuração existente no arquivo applicationContext-security.xml no bean declarado concurrentSessionController.

Etape 3: Configuração dos filtros e listeners no arquivo web.xml:


Em primeiro lugar, nós devemos implementar a lógica de filtros do ACEGI. Para isso,é preciso atualizar o arquivo web.xml da nossa aplicação Web definindo um filtro no sentido servlet. Este filtro vai carregar a sua própria configuração a partir dos arquivos de configuração do Spring. </code>
<filter> 
  <filter-name>localeFilter</filter-name> 
  <filter-class>com.cleyris.ebee.filter.LocaleFilter</filter-class> 
 </filter> 
..... 
  <listener> 
        <listener-class>com.cleyris.ebee.listener.StartupListener</listener-class> 
    </listener> 
    <listener> 
        <listener-class>com.cleyris.ebee.listener.UserCounterListener</listener-class> 
    </listener>

Etapa n° 4: Configuração dos filtros a nível do arquivo de configuração do Spring:


Este arquivo deve se encontrar no /WEB-INF/
Em nossa aplicação, este arquivo é chamado applicationContext-security.xml

No nosso arquivo de configuração Spring, vamos declarar os filtros ACEGI responsáveis pela implementação da nossa estratégia de segurança. Para ser mais claro, seria preciso criar vários arquivos de configuração.
<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:aop="http://www.springframework.org/schema/aop" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd" 
       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 
                /**=httpSessionContextIntegrationFilter,authenticationProcessingFilter,securityContextHolderAwareRequestFilter,rememberMeProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor 
            </value> 
            <!-- Put channelProcessingFilter before securityContextHolderAwareRequestFilter to turn on SSL switching --> 
            <!-- It's off by default b/c Canoo WebTest doesn't support SSL out-of-the-box --> 
        </property> 
    </bean> 
    <!-- Para limitar o número de conectores à sessão, ao mesmo tempo --> 
 <bean id="concurrentSessionController" 
  class="org.acegisecurity.concurrent.ConcurrentSessionControllerImpl"> 
  <property name="maximumSessions"> 
   <value>1</value> 
  </property> 
  <property name="sessionRegistry"> 
   <ref local="sessionRegistry" /> 
  </property> 
 </bean> 
 <bean id="sessionRegistry" 
  class="org.acegisecurity.concurrent.SessionRegistryImpl" /> 
    <bean id="httpSessionContextIntegrationFilter" class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"/> 
 <!-- Fin --> 
    <!-- Changed to use logout.jsp since causes 404 on WebSphere: http://issues.appfuse.org/browse/APF-566 --> 
    <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"> 
        <constructor-arg value="/index.jsp"/> 
        <constructor-arg> 
            <list> 
                <ref bean="rememberMeServices"/> 
                <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/> 
            </list> 
        </constructor-arg> 
        <property name="filterProcessesUrl" value="/logout.jsp"/> 
    </bean> 

    <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="authenticationFailureUrl" value="/login.jsp?error=true"/> 
        <property name="defaultTargetUrl" value="/"/> 
        <property name="filterProcessesUrl" value="/j_security_check"/> 
        <property name="rememberMeServices" ref="rememberMeServices"/> 
    </bean> 

    <bean id="securityContextHolderAwareRequestFilter" class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"/> 

    <bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="rememberMeServices" ref="rememberMeServices"/> 
    </bean> 

    <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> 
        <property name="key" value="anonymous"/> 
        <property name="userAttribute" value="anonymous,ROLE_ANONYMOUS"/> 
    </bean> 

    <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"> 
        <property name="authenticationEntryPoint"> 
            <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> 
                <property name="loginFormUrl" value="/login.jsp"/> 
                <property name="forceHttps" value="false"/> 
            </bean> 
        </property> 
    </bean> 

    <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="accessDecisionManager" ref="accessDecisionManager"/> 
        <property name="objectDefinitionSource"> 
            <value> 
                PATTERN_TYPE_APACHE_ANT 
                /passwordHint.html*=ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER 
                /signup.html*=ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER 
                /a4j.res/*.html*=ROLE_ANONYMOUS,ROLE_ADMIN,ROLE_USER <!-- APF-737, OK to remove if not using JSF --> 
                /**/*.html*=ROLE_ADMIN,ROLE_USER 
            </value> 
        </property> 
    </bean> 

    <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"> 
        <property name="allowIfAllAbstainDecisions" value="false"/> 
        <property name="decisionVoters"> 
            <list> 
                <bean class="org.acegisecurity.vote.RoleVoter"/> 
            </list> 
        </property> 
    </bean> 

    <bean id="rememberMeServices" class="org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices"> 
        <property name="userDetailsService" ref="UserDao"/> 
        <property name="key" value="23_*!cdU='612./e;NrI"/> 
        <property name="parameter" value="rememberMe"/> 
    </bean> 

    <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"> 
        <property name="providers"> 
            <list> 
                <ref local="daoAuthenticationProvider"/> 
                <ref local="anonymousAuthenticationProvider"/> 
                <ref local="rememberMeAuthenticationProvider"/> 
            </list> 
        </property> 
    </bean> 

    <bean id="daoAuthenticationProvider" class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> 
         <property name="userDetailsService" ref="UserDao"/> 
         <property name="passwordEncoder" ref="passwordEncoder"/> 
    </bean> 

    <bean id="anonymousAuthenticationProvider" class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"> 
        <property name="key" value="anonymous"/> 
    </bean> 

    <bean id="rememberMeAuthenticationProvider" class="org.acegisecurity.providers.rememberme.RememberMeAuthenticationProvider"> 
        <property name="key" value="23_*!cdU='612./e;NrI"/> 
    </bean> 

    <!-- This bean definition must be available to ApplicationContext.getBean() so StartupListener 
         can look for it and detect if password encryption is turned on or not --> 
    <bean id="passwordEncoder" class="org.acegisecurity.providers.encoding.ShaPasswordEncoder"/> 

    <!-- This bean is optional; it isn't used by any other bean as it only listens and logs --> 
    <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener"/> 

    <!-- Apply method-level interceptor to userManager bean --> 
    <aop:config> 
        <aop:advisor id="managerSecurity" advice-ref="methodSecurityInterceptor" pointcut="execution(* com.cleyris.ebee.core.service.UserManager.*(..))"/> 
    </aop:config> 

    <bean id="methodSecurityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="accessDecisionManager" ref="accessDecisionManager"/> 
        <property name="objectDefinitionSource"> 
             <value> 
                 com.cleyris.ebee.core.service.UserManager.getUsers=ROLE_ADMIN 
                 com.cleyris.ebee.core.service.UserManager.removeUser=ROLE_ADMIN 
             </value> 
        </property> 
    </bean> 

    <!-- SSL Switching: to use this, configure it in the filterChainProxy bean --> 
    <bean id="channelProcessingFilter" class="org.acegisecurity.securechannel.ChannelProcessingFilter"> 
        <property name="channelDecisionManager" ref="channelDecisionManager"/> 
        <property name="filterInvocationDefinitionSource"> 
            <value> 
                PATTERN_TYPE_APACHE_ANT 
                /admin/**=REQUIRES_SECURE_CHANNEL 
                /login*=REQUIRES_SECURE_CHANNEL 
                /j_security_check*=REQUIRES_SECURE_CHANNEL 
                /signup.html*=REQUIRES_SECURE_CHANNEL 
                /saveUser.html*=REQUIRES_SECURE_CHANNEL 
                /**=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> 
</beans>

Neste arquivo, vamos definir as páginas de índice, login e logout, o número máximo de abertura de sessão, os diferentes papéis...

Etapa n° 5: Criação da interface UserManager, sua implementação, do userDao e da classe POJO User:

  • UserManager.java

public interface UserManager extends Manager { 
 ...... 
 /** 

  • @param user este método é utilizado pelo acegi para adicionar outro usuário
  • @return
  • @throws UserExistsException
  • /
public User saveUser(User user) throws UserExistsException; /**
  • este método é utilizado pelo acegi para interceptar um usuário
  • @param userId
  • @return
  • /
public User getUser(String userId); /**
  • este método é utilizado pelo acegi para excluir um usuário
  • @param userId
  • /
public void removeUser(String userId); /**
  • @param username este método é utilizado pelo acegi para exibir o usuário através do seu login
  • @return
  • @throws UsernameNotFoundException
  • /
public User getUserByUsername(String username) throws UsernameNotFoundException, CleyrisException ; ............... }
  • UserManagerImpl.java

public class UserManagerImpl extends BaseManager implements UserManager{ 

 //é a parte Acegi 
  
    @SuppressWarnings("unchecked") 
 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
        List<UserDetails> users = userDAO.setHqlQuery("from User where username=:username").setParameter("username",username).list(); 
        if (users == null || users.isEmpty()) { 
            throw new UsernameNotFoundException("user '" + username + "' not found..."); 
        } else { 
            return (UserDetails) users.get(0); 
        } 
    } 
     
    public User getUser(String userId) { 
        return userDao.get(new Long(userId)); 
    } 
     
    public User saveUser(User user) throws UserExistsException { 
        // if new user, lowercase userId 
        if (user.getUserId()== 0) { 
            user.setUsername(user.getUsername().toLowerCase()); 
        } 

        try { 
            return userDao.saveUser(user); 
        } catch (DataIntegrityViolationException e) { 
            
            if (log.isErrorEnabled()) 
    log 
      .error("Exception occurs class:com.cleyris.ebee.core.service.UserManagerImpl.java  method:saveUser: " 
        + e.getMessage() + "."); 
            throw new UserExistsException(Constants.UserExists); 
        } catch (EntityExistsException e) { // needed for JPA 
            
            if (log.isErrorEnabled()) 
    log 
      .error("Exception occurs class:com.cleyris.ebee.core.service.UserManagerImpl.java  method:saveUser: " 
        + e.getMessage() + "."); 
            throw new UserExistsException(Constants.UserExists); 
        } 
    } 

    public void removeUser(String userId) { 
        log.debug("removing user: " + userId); 
        userDao.remove(new Long(userId)); 
    } 
      public User getUserByUsername(String username) throws UsernameNotFoundException, CleyrisException { 
     User user= (User) userDao.loadUserByUsername(username); 
        return user; 
    } 
    public List<User> getUsers(User user) { 
        return userDao.getUsers(); 
    } 
}
  • UserDao.java

package com.cleyris.ebee.core.dao; 

import java.util.List; 

import org.acegisecurity.userdetails.UserDetails; 
import org.acegisecurity.userdetails.UsernameNotFoundException; 
import org.springframework.transaction.annotation.Transactional; 

import com.cleyris.ebee.core.modele.User; 

/** 

  • @author mlaouini
  • /
public interface UserDao extends BaseDao<User, Long> { /**
  • carregar as informações do usuário com base no login.
  • @param username the user's username
  • @return userDetails populated userDetails object
  • @throws org.acegisecurity.userdetails.UsernameNotFoundException thrown when user not found in database
  • /
@Transactional UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; /**
  • carregar uma lista de usuários ordenados por login usando a maiúscula no login==>username.
  • @return List populated list of users
  • /
List<User> getUsers(); /**
  • registrar um usuário.
  • @param user the object to be saved
  • @return the persisted User object
  • /
User saveUser(User user); }
  • UserDaoImpl.java

public class UserDaoImpl extends BaseDaoImpl<User, Long> implements UserDao, UserDetailsService { 
    public UserDaoImpl() { 
        super(User.class); 
    } 
    /** 

  • {@inheritDoc}
  • /
@SuppressWarnings("unchecked") public List<User> getUsers() { return getHibernateTemplate().find("from User u order by upper(u.username)"); } /**
  • {@inheritDoc}
  • /
public User saveUser(User user) { log.debug("user's id: " + user.getUserId()); getHibernateTemplate().saveOrUpdate(user); // necessary to throw a DataIntegrityViolation and catch it in UserManager getHibernateTemplate().flush(); return user; } public User save(User user) { return this.saveUser(user); } /**
  • {@inheritDoc}
  • /
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List users = getHibernateTemplate().find("from User where username=?", username); if (users == null || users.isEmpty()) { throw new UsernameNotFoundException("user '" + username + "' not found..."); } else { return (UserDetails) users.get(0); } } }
  • User.java

/** 

  • @author mlaouini
  • /
@Entity @javax.persistence.SequenceGenerator(name="SEQ_STORE",sequenceName="sequence_users") @Table(name = "users", schema = "ebee") public class User extends BaseObject implements UserDetails { ............ private String username; // required private String password; // required private String confirmPassword; private String passwordHint; private Set<Role> roles = new HashSet<Role>(); private boolean enabled; private boolean accountExpired; private boolean accountLocked; private boolean credentialsExpired; ..................... @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "user_role", schema = "ebee", joinColumns = { @JoinColumn(name = "userid", nullable = false, updatable = false) }, inverseJoinColumns = { @JoinColumn(name = "roleid", nullable = false, updatable = false) }) @NotNull public Set<Role> getRoles() { return this.roles; } public void setRoles(Set<Role> roles) { this.roles = roles; } /**
  • @see org.acegisecurity.userdetails.UserDetails#getAuthorities()
  • @return GrantedAuthority[] an array of roles.
  • /
@Transient public GrantedAuthority[] getAuthorities() { return roles.toArray(new GrantedAuthority[0]); } @Column(name = "account_enabled") public boolean isEnabled() { return enabled; } @Column(name = "account_expired", nullable = false) public boolean isAccountExpired() { return accountExpired; } /**
  • @see org.acegisecurity.userdetails.UserDetails#isAccountNonExpired()
  • /
@Transient public boolean isAccountNonExpired() { return !isAccountExpired(); } @Column(name = "account_locked", nullable = false) public boolean isAccountLocked() { return accountLocked; } /**
  • @see org.acegisecurity.userdetails.UserDetails#isAccountNonLocked()
  • /
@Transient public boolean isAccountNonLocked() { return !isAccountLocked(); } @Column(name = "credentials_expired", nullable = false) public boolean isCredentialsExpired() { return credentialsExpired; } /**
  • @see org.acegisecurity.userdetails.UserDetails#isCredentialsNonExpired()
  • /
@Transient public boolean isCredentialsNonExpired() { return !credentialsExpired; } @Column(nullable = false, length = 50, unique = true) public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Column(nullable = false) public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Transient public String getConfirmPassword() { return confirmPassword; } public void setConfirmPassword(String confirmPassword) { this.confirmPassword = confirmPassword; } @Column(name = "password_hint") public String getPasswordHint() { return passwordHint; } public void setPasswordHint(String passwordHint) { this.passwordHint = passwordHint; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public void setAccountExpired(boolean accountExpired) { this.accountExpired = accountExpired; } public void setAccountLocked(boolean accountLocked) { this.accountLocked = accountLocked; } public void setCredentialsExpired(boolean credentialsExpired) { this.credentialsExpired = credentialsExpired; } }

A classe User.java implementa o UserDetails. Essa interface fornece informações básicas sobre o usuário. As implementações não são usados diretamente pelo Acegi por razões de segurança. Elas simplesmente armazenam informações sobre o usuário que, em seguida, são encapsulados nos objetos de autenticação.


Tradução feita por Lucia Maurity y Nouira

Veja também

Artigo original publicado por . Tradução feita por pintuda. Última modificação: 15 de dezembro de 2011 às 10:26 por pintuda.
Este documento, intitulado 'Configuração do Framework Acegi no aplicativo Web JAVA J2EE', está disponível sob a licença Creative Commons. Você pode copiar e/ou modificar o conteúdo desta página com base nas condições estipuladas pela licença. Não se esqueça de creditar o CCM (br.ccm.net) ao utilizar este artigo.