Mostrando postagens com marcador Spring Security. Mostrar todas as postagens
Mostrando postagens com marcador Spring Security. Mostrar todas as postagens

9 de julho de 2013

Login customizado com Spring Security 3.1.4

Olá Pessoal!

Depois de muito tempo sem postar (diríamos meses...), resolvi escrever sobre um tema que a algum tempo muitas pessoas vinham me pedindo sobre isso, e como eu consegui fazer uma solução para este "problema" resolvi escrever aqui, (não sei se é a forma certa, mas é funcional)!
O tema de hoje é sobre como ter um campo a mais no form de login utilizando o Spring Security, imagino eu que tenha uma forma mais simples de fazer... mas enquanto não descubro, vamos àquela que funciona.

Antes de começar o nosso projeto, é de total importância que você já conheça ou já tenha utilizado o Spring Security, caso não conheça, leia esta postagem.

* Começando *

Inicialmente a minha estrutura do banco de dados (básico):

 
Neste caso, eu tenho duas tabelas: uma de empresa onde a coluna regra será para dizer se é ROLE_ADM, ROLE_USER... entre outras, e tenho o usuário que está ligado diretamente a empresa, fiz o mais básico possível apenas para mostrar como que pode estar colocando um campo a mais no login, neste caso: o usuário terá que colocar seu login, senha e escolher a qual empresa pertence.
Para entender direito, esta será a nossa tela de login:


O projeto completo terá esta estrutura:


Não dê tanta atenção quanto ao pacotes que se encontram as classes, pois esta apenas foi uma organização minha, acredito que cada um tenha uma forma melhor de organizar.
A primeira coisa a se fazer é baixar a biblioteca do Spring Security na versão 3.1.4 (pode baixar clicando aqui), juntamente com o Spring Framework 3.1.4. As bibliotecas que eu uso são:


*Códigos*

Inicialmente é necessário fazer as classes de modelo, que estão no pacote javasemcafe.model, observo que fiz este projeto utilizando JPA, mas pra quem quiser usar apenas JDBC é só fazer as adaptações, utilizei o JPA por ser mais rápido, então de acordo com a estrutura de banco de dados teremos duas classes, uma é Empresa e a outra é Usuário:
Classe Empresa.java:
@Entity
@Table(name = "empresa")
public class Empresa implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "codigo")
    private Integer codigo;
    @Size(max = 45)
    @Column(name = "descricao")
    private String descricao;
    @Size(max = 45)
    @Column(name = "regra")
    private String regra;

    // Getters e Setters
}

Classe Usuario.java:
@Entity
@Table(name = "usuario")
public class Usuario implements Serializable {
    @Id
    @Basic(optional = false)
    @NotNull
    @Column(name = "codigo")
    private Integer codigo;
    @Size(max = 45)
    @Column(name = "login")
    private String login;
    @Size(max = 45)
    @Column(name = "senha")
    private String senha;
    @JoinColumn(name = "empresa_codigo", referencedColumnName = "codigo")
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    private Empresa empresa;

    // Getters e Setters
}

No pacote javasemcafe.dao, temos a classe EmpresaDAO.java:
@Stateless
public class EmpresaDAO {

    @PersistenceContext(unitName = "LoginCustomizadoPU")
    private EntityManager em;

    public List<Empresa> listar() {
        List<Empresa> empresas = null;

        try {
            Query query = em.createQuery("Select e from Empresa e order by e.descricao");

            empresas = query.getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return empresas;
    }
}

Nesta classe, apenas temos um método que retorna uma lista de empresas que será utilizada com o EmpresaBean.java para popular o combo de empresas na página de login.
Classe EmpresaBean.java:
@ManagedBean
@ViewScoped
public class EmpresaBean implements Serializable {

    @EJB
    private EmpresaDAO empresaDAO;
    private List<Empresa> empresas;

    public List<Empresa> getEmpresas() {
        empresas = empresaDAO.listar();

        return empresas;
    }

    public void setEmpresas(List<Empresa> empresas) {
        this.empresas = empresas;
    }
}
Neste caso apenas preenche uma Collection que aqui é o List.

Voltando ao pacote javasemcafe.dao, temos praticamente a classe mais importante:
AutenticacaoFilter.java, é nela que fazemos a verificação de login, senha e se o usuário pertence ou não a empresa selecionada no login, abaixo explico os detalhes dela:
public class AutenticacaoFilter extends UsernamePasswordAuthenticationFilter {

    private EntityManagerFactory factory = Persistence.createEntityManagerFactory("LoginCustomizadoPU");
    private EntityManager em;
    private String mensagem;

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, BadCredentialsException {
        String login = request.getParameter("j_login");
        String senha = request.getParameter("j_senha");
        Integer codigoEmpresa = Integer.parseInt(request.getParameter("j_empresa"));

        try {
            Usuario usuario = buscarUsuario(login, senha);
            if (usuario != null) {

                if (usuario.getEmpresa().getCodigo().equals(codigoEmpresa)) {

                    Collection<GrantedAuthority> regras = new ArrayList<GrantedAuthority>();
                    regras.add(new SimpleGrantedAuthority(usuario.getEmpresa().getRegra()));

                    request.getSession().setAttribute("usuarioLogado", usuario);
                    mensagem = "Bem vindo: " + usuario.getLogin();
                    return new UsernamePasswordAuthenticationToken(usuario.getLogin(), usuario.getSenha(), regras);

                } else {
                    mensagem = "Acesso negado a empresa selecionada!";
                    throw new BadCredentialsException(mensagem);
                }

            } else {
                mensagem = "Dados Incorretos";
                throw new BadCredentialsException(mensagem);
            }

        } catch (Exception e) {
            throw new BadCredentialsException(e.getMessage());
        }
    }

    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException {
        SecurityContextHolder.getContext().setAuthentication(authResult);
        request.getSession().setAttribute("msg", mensagem);
        response.sendRedirect("index.jsf");
    }

    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        request.getSession().setAttribute("msg", mensagem);
        response.sendRedirect("login.jsf");
    }

    /**
     * Metodos de acesso ao BD
     *
     */
    public Usuario buscarUsuario(String login, String senha) {
        em = factory.createEntityManager();
        Usuario usuario = null;
        try {
            Query query = em.createQuery("SELECT u FROM Usuario u WHERE u.login = :login AND u.senha = :senha");
            query.setParameter("login", login);
            query.setParameter("senha", senha);

            usuario = (Usuario) query.getSingleResult();

        } catch (NoResultException e) {
            System.out.println("DAO: Não foi encontrado resultado!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            em.close();
        }

        return usuario;
    }
}

Vamos entender o que acontece nessa classe: os métodos attemptAuthentication. successfulAuthentication e unsuccessfulAuthentication são métodos criados automaticamente quando se utiliza o extends UsernamePasswordAuthenticationFilter. O primeiro método é onde ocorre a verificação do usuário, é o comando executado quando o usuário clica no botão de "Acessar" (conforme tela no início da postagem), nesse método tem um detalhe importante, que é a linha 23 onde ele dá o return com os dados do usuário, no caso das regras são aqueles ROLE_ADM, ROLE_USER.... isso é necessário para o applicationContext.xml saber se deve liberar a pagina ou não, veremos mais pra frente isso, o que você precisa saber é que, dependendo a lógica que você utilizar para montar a estrutura do BD, será possível que um usuário pertença a diferentes filiais com diferentes regras para cada uma, mas aí teria que fazer uma tabela N-N, o que prologaria ainda mais essa postagem que já vai ficar enorme. Só mais um detalhe antes de continuar, na linha 21, nós jogamos o usuário para a sessão da aplicação, assim podemos recuperar ele a qualquer momento. Continuando, se no primeiro método ocorrer o return ele vai para o método de sucesso (successfulAuthentication), onde eu digo para o Spring Security que há uma autenticação e redireciono o usuário para a página inicial. No caso de não ocorrer um return, por dados incorretos, pelo usuário não ter acesso a empresa, automaticamente cairá no método unsuccessfulAuthentication, onde eu redireciono ele para a página de login novamente.
Vamos a uma observação nessa classe: percebam que eu tenho uma String mensagem, pois bem, ela não seria necessária se caso não quisesse mostrar ao usuário os avisos de dados incorretos e outros... então eu coloco essa String na sessão da aplicação, e recupero por uma outra classe que é a AutenticacaoPhaseListener.java, que se encontra no pacote javasemcafe.controller. 

public class AutenticacaoPhaseListener implements PhaseListener {

    @Override
    public void afterPhase(PhaseEvent event) {
        //não implementado
    }

    @Override
    public void beforePhase(PhaseEvent event) {
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        if (session != null) {
            String mensagem = (String) session.getAttribute("msg");

            if (mensagem != null) {
                context.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, mensagem, null));
                session.setAttribute("msg", null);
            }
        }
    }

    @Override
    public PhaseId getPhaseId() {
        return PhaseId.RESTORE_VIEW;
    }
}

Esta classe implementa um PhaseListener que vai pegar a mensagem na sessão da aplicação e vai mandá-la por por um FacesMessage para o JSF.

Utilizando a classe UsuarioBean.java, do pacote javasemcafe.controller(esta classe não está aparecendo na imagem da estrutura do projeto, mas deve estar no projeto), vamos pegar o usuário na sessão:

@ManagedBean
@ViewScoped
public class UsuarioBean {

    private Usuario usuarioLogado;

    public Usuario getUsuario() {
        FacesContext context = FacesContext.getCurrentInstance();
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        usuarioLogado = (Usuario) session.getAttribute("usuarioLogado");

        return usuarioLogado;
    }

    public void setUsuario(Usuario usuario) {
        this.usuarioLogado = usuario;
    }
}

Veja que é utilizado o atributo "usuarioLogado" que foi setado lá na classe AutenticacaoFilter.java, é através dela que conseguimos pegar esse usuário. Ou você pode estar pegando o usuário diretamente do Spring Security, porém neste caso ele não te trás muitas informações de usuário, mas sei que o login é uma delas, veja um exemplo (isto não está no projeto, é apenas uma outra forma de um outro projeto meu):
public ConfiguracaoBean() {
    Authentication authentication = (Authentication) SecurityContextHolder.getContext().getAuthentication();
    if (authentication != null) {
       usuario = authentication.getName();
    }
}

Agora vamos para as páginas do JSF, começando pela index.xhtml (também conhecida: index.jsf) que será a página para onde o usuário será redirecionado se logar o sistema:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:outputText value="Olá #{usuarioBean.usuario.login}!"/><br/>
        <h:outputText value="Você está na empresa: #{usuarioBean.usuario.empresa.descricao}!"/><br/>
        
        <a href="./logout">Sair</a>
    </h:body>
</html>

Não tem nada de mais, só estou usando a classe UsuarioBean.java para mostrar o usuário que está logado na sessão da aplicação. Ficando desta forma:


Página de login, login.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Login Customizado</title>
    </h:head>
    <h:body>
        <form action="j_spring_security_check" method="post" style="text-align: right;">
            <fieldset>
                <legend>Login</legend>
                <h:panelGrid columns="2">
                    <h:outputText value="Login:" />
                    <h:inputText id="j_login" />
                    <h:outputText value="Senha:" />
                    <h:inputSecret id="j_senha" />
                    <h:outputText value="Empresa:" />
                    <h:selectOneMenu id="j_empresa" style="width: 155px" >
                        <f:selectItems value="#{empresaBean.empresas}" var="e" itemLabel="#{e.descricao}" itemValue="#{e.codigo}" />
                    </h:selectOneMenu>
                    <h:outputText />
                    <h:commandButton value="Acessar" id="btnAcessar" />
                </h:panelGrid>
                <h:message for="btnAcessar" />
            </fieldset>    
        </form>

    </h:body>
</html>

A única coisa diferente do normal das páginas de login do Spring Security, é o caso de ter adicionado o h:selectOneMenu para fazer o combo, fora isso, o restante já foi explicado na postagem que eu indico para leitura no começo do post. No caso da h:message serve para mostrar as mensagens enviadas pelo FacesMessage, aparecendo dessa forma:


Vamos ver agora os arquivos de configuração que ficam dentro do WEB-INF.
Começando com a configuração básica no web.xml para o Spring Security ser identificado (já explicado na postagem indicada):
<!-- Spring security -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

<!-- Fim spring security --> 

Bom, como devem saber na versão JEE 6, o faces-config.xml é opcional, ou seja é utilizado em alguns casos, como o caso agora, onde eu preciso "registrar" o PhaseListener (a classe AutenticacaoPhaseListener.java): 

<faces-config version="2.1"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd">
    <lifecycle>
        <phase-listener>javasemcafe.controller.AutenticacaoPhaseListener</phase-listener>
    </lifecycle>
</faces-config>

Agora vamos para toda a "mágica" da customização do login, o applicationContext.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
   http://www.springframework.org/schema/security
   http://www.springframework.org/schema/security/spring-security-3.1.xsd">

    <security:http auto-config="false" use-expressions="true" access-denied-page="/index.jsf" entry-point-ref="authenticationEntryPoint" >
        <security:intercept-url pattern="/index.jsf" access="hasRole('ROLE_ADM')"/>
     
        <security:logout invalidate-session="true" logout-success-url="/login.jsf" logout-url="/logout"/>

        <security:custom-filter ref="authenticationFilter" position="FORM_LOGIN_FILTER"/>
    </security:http>

    <bean id="authenticationFilter" class="javasemcafe.dao.AutenticacaoFilter" p:authenticationManager-ref="authenticationManager"/>

    <bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint" p:loginFormUrl="/login.jsf"/>

    <security:authentication-manager alias="authenticationManager" />
</beans>

O que vemos de diferente na configuração do applicationContext, é a partir da 15º linha, onde de uma forma geral, estou dizendo que a autenticação deverá ser feita pela classe: javasemcafe.dao.AutenticacaoFilter, como pode ver na linha 18.

Bom pessoal, acabei deixando de explicar muitas classes (do Spring Security) porque senão essa postagem iria ficar bem mais longa!

Até a próxima :)
andii.brunetta

20 de abril de 2011

Java EE 6: Segurança com Spring Security 3.0.5

Quando se fala em sistemas online, logo deve-se pensar em como tratar a parte da segurança dos dados, de acesso... nessa postagens vamos trabalhar com a segurança de acesso ao sistema, ou seja, o login.
Para fazer esse controle de login vou utilizar o framework Spring Security na versão 3.0.5, estarei disponibilizando os jar's usados, pois não aconselho baixar do próprio site deles, andei fazendo uns testes e o que está disponível agora nessa mesma versão não bate com o que eu baixei a um tempo atrás, então para fazer este projeto o mesmo pode ser baixado por aqui.
O Spring Security trata a autenticação de usuários da seguinte forma: o bloqueio de acesso pode ser feito a diretórios ou a arquivos, ou seja o usuário deve ter a permissão de acesso para o determinado diretório ou arquivo, simplificando... suponhamos que eu tenha um diretório admin, com o Spring Security eu posso definir que apenas usuários que tenham permissão de administrador possa ter acesso ao conteúdo desse diretório. Existem duas formas de fazer a autenticação de acesso, com usuários pré-definidos no xml de configuração ou fazendo a validação de usuário em um banco de dados. Nessa postagem vamos tratar essas duas formas.
Inicialmente vamos criar o nosso projeto de base que vai funcionar para os exemplos com ou sem banco de dados.
Estrutura


Obs: veja que temos um diretório admin e dentro dele um xhtml com o nome de index, então nesse projeto temos 2 index, o da raiz poderá ser acessado sem fazer login, já o que se encontra dentro da do diretório precisará de permissão para ser acessado. Vamos começar com a codificação:
/index.xhtml
<h:body>
        Usuario não precisa estar logado...
</h:body>

/negado.xhtml
<h:body>
        Você não tem permissão de acessar este diretorio.
        <a href="index.jsf">Voltar </a>
</h:body>

/falha.xhtml
<h:body>
        Login ou senha incorretos...
        <a href="index.jsf">Voltar</a>
</h:body>

/admin/index.xhtml
<h:body>
        Usuario logado com sucesso!
        <a href="../logout">Sair</a>
</h:body>
O /logout eu vou explicar mais pra frente... é coisa da config do Spring Security.
Depois de criar as páginas para fazer nosso teste, vamos colocar as bibliotecas no projeto, você pode criar uma biblioteca no Netbeans (Ferramentas/Bibliotecas/Nova biblioteca...), ou adicionar diretamente os jar's ao projeto (no projeto, botão direito em Bibliotecas e em add jar).
Os jar's são:



Outra coisa em comum nos exemplos que vamos fazer é a configuração do Spring Security no WEB-INF/web.xml, dentro dele adicione a seguinte configuração (pode ser logo abaixo da tag web-app):

<!-- Spring security -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

<!-- Fim spring security -->

Se você executar ele neste ponto, dará erro, pois com essa configuração ele exige que você tenha um /WEB-INF/applicationContext.xml, então para começar crie um arquivo xml com o nome de applicationContext dentro do WEB-INF...

Segurança de acesso com usuários pré-definidos (sem BD)
Toda a configuração a partir de agora é feita dentro do arquivo applicationContext.xml citado acima.
Abaixo vou postar como ele fica e depois explico as tags:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <http auto-config="true" access-denied-page="/negado.jsf">
        <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
        <logout invalidate-session="true" logout-success-url="/index.jsf" logout-url="/logout"/>
    </http>

    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="adm" password="123" authorities="ROLE_ADMIN" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>
beans: é a raiz do nosso xml, nela é definido os namespace do Spring Security.

http: caso não tenha percebido, nesse projeto nós não fizemos uma página de login, isso porque ao setar a propriedade auto-config como true, o próprio Spring Security cria uma página de login (ao final da postagem ensino como personalizar sua página de login), e a propriedade access-denied-page se refere à pagina que deve ser mostrada quando um usuário for logado mas não tem acesso a um diretório ou arquivo (esse arquivo negado.jsf se refere à nossa página criada lá na estrutura do projeto).
intercept-url: esta tag é a responsável por definir quais as permissões que o usuário deve ter para acessar determinados diretórios ou arquivos, no exemplo veja que estamos definindo que o usuário deve ter a permissão ROLE_ADMIN para ter acesso aos arquivos do diretório admin.
logout: esta tag é a responsável por chamar a servlet que desloga o usuário, a propriedade logout-sucess-url se refere a página pra onde o usuário deve ser enviado ao ser deslogado, e o logout-url seria o nome da servlet... lembra que na página /admin/index.jsf nós temos "../logout", pois bem, é a esta configuração que ele está se referindo.

authentication-manager: gerencia a autenticação.
authentication-provider: provedor de autenticação.
user-service: é onde deve-se definir os usuários.
user: é o usuário propriamente dito, a propriedade name é o login do usuário, password é a senha e authorities são as permissões do usuário. Um usuário pode ter mais de uma permissão, estas devem ser separadas por vírgula na propriedade authorities, ficando por exemplo assim:
authorities="ROLE_ADMIN,ROLE_USER".

Bom, assim nosso login com usuário pré-definido já está funcionando, basta testar...

Segurança de acesso autenticando o usuário no banco de dados (com BD)
Para este exemplo vamos utilizar o MySQL, precisamos ter uma tabela com a seguinte estrutura:


Para fazer este projeto, aconselho apenas fazer uma cópia do projeto acima, pois só precisaremos alterar o applicationContext.xml, o mesmo deve ficar assim (logo abaixo do código vou explicar as novas tags):
<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <http auto-config="true" access-denied-page="/negado.jsf">
        <intercept-url pattern="/admin/**" access="ROLE_ADMIN" />
        <logout invalidate-session="true" logout-success-url="/index.jsf" logout-url="/logout"/>
    </http>

    <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" >
        <beans:property name="url" value="jdbc:mysql://localhost:3306/5tads" />
        <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <beans:property name="username" value="root" />
        <beans:property name="password" value="admin" />
    </beans:bean>
    
    <authentication-manager>
        <authentication-provider>
            <jdbc-user-service data-source-ref="dataSource"
                users-by-username-query="SELECT username, password, 'true' as enable FROM users WHERE username=?"
                authorities-by-username-query="SELECT username, authority FROM users WHERE username=?"
            />
        </authentication-provider>
    </authentication-manager>

</beans:beans>
Algumas tags são padrão tanto para usar com ou sem banco, por exemplo a http. Agora vamos ver quais as tags que temos de diferente do exemplo anterior (sem BD).

bean: entre várias possibilidades de utilização desta, nesse exemplo estamos usando ela para definir o dataSource que fará a conexão com o banco de dados MySQL, a propriedade id é nome que identifica esse bean... já a propriedade class se refere a classe do Spring Security que é utilizada para fazer a conexão.
property: serve para setar uma propriedade, neste exemplo é usada para setar propriedades diferentes referentes a conexão ao banco de dados. Vamos ver as propriedades pelos name...
url: é a url de acesso ao banco de dados, 5tads é a database que eu estou usando
driverClassName: é o nome do driver do MySQL
username: é o usuário de acesso ao banco de dados
password: é a senha de acesso ao banco de dados.

Veja que o nosso authentication-provider mudou... vamos ver o que ele mudou:
Quando utilizamos o banco de dados para fazer a validação dos usuários, não temos o porque de definir os usuários manualmente no xml, por isso o nosso user-service foi substituído por jdbc-user-service.
jdbc-user-service: a propriedade data-source-ref utiliza como referencia o nosso bean de conexão acima com o id de dataSource, já a users-by-username-query é a query responsável por selecionar o usuário pelo username... quanto ao "'true' as enable" na query, seria por exemplo uma coluna booleana na nossa tabela users dizendo se o usuário está ativo ou não. A outra propriedade é a authorities-by-username-query que tem uma query que é responsável por pegar as permissões do usuário pelo username.

Feito isso... o nosso controle de login com BD está pronto, e é só testar! 


Página de login
Como já falei anteriormente, o Spring Security já tem sua página de login que é chamada pela servlet : /spring_security_login ... A página é a seguinte:


Vou criar a minha página de login... dentro de páginas web crie uma nova página JSF, com o nome de login.xhtml, ela será a responsável por substituir a página do Spring Security, o código dela é o seguinte:
<h:body>
        <a href="index.jsf">Retornar para a Página Inicial</a>
        <form action="j_spring_security_check" method="post">
            <h:panelGrid columns="2">
                <h:outputText value="Usuario" />
                <h:inputText id="j_username" />
                <h:outputText value="Senha" />
                <h:inputSecret id="j_password" />
            </h:panelGrid>
            <input type="submit" value="Efetuar Login" />
        </form>
</h:body>
Mas agora você deve estar se perguntando... porque não usar o form do próprio JSF e o commandButton no lugar do input? Então, pelos testes que eu fiz, não é possível usar o form e o commandButton do JSF, ele acaba não funcionando direito, e olha que não foi por falta de testes...
Mas vamos entender algumas coisinhas.. como já falei, precisamos usar o form e o input do próprio HTML, outras coisas são de extrema importância para o funcionamento, a action do form precisa ser a servlet j_spring_security_check, assim como os id's dos campos precisam ser j_username e j_passsword ... se eu não me engano quando usa-se o Spring Security de forma personalizada (daria uma longa postagem) tem como usar outros id's, mas não é o nosso foco hoje.
Agora temos que informar ao applicationContext.xml que temos uma página de login, para isso, dentro da tag http, basta informar a seguinte linha:
<form-login login-page="/login.jsf" authentication-failure-url="/falha.jsf"/>
A propriedade login-page é a responsável por dizer qual é a nossa página de login, e a authentication-failure-url é a página que informa que o login e a senha estão incorretos (fizemos essa página lá no inicio da postagem).

Pra quem precisar de algo mais complexo como um login personalizado, deixo a seguinte referencia: Spring Security 3 - MVC Integration: Using A Custom Authentication Manager me ajudou muito quando precisei implementar um assim!

Referências: