Implementando UserDetailsService Spring Security

 

Olá Pessoal,

No post de hoje veremos como implementar a interface UserDetailsService do Spring. Mas para que serve ela, Camilo? É o que vamos ver logo a seguir…

Lets go…

Starting…

Este post será bem pontual e vou considerar que você já usa e conhece o Spring Security. Uma das opções mais comum de validar autenticação do usuário seria algo mais ou menos assim:

<jdbc-user-service data-source-ref="dataSource"

users-by-username-query="SELECT email, password, 'true' as enable FROM user WHERE email=?;"

authorities-by-username-query="select distinct u.email,ro.role_description from user u, user_role r, role ro where u.id = r.role_id and ro.id=r.role_id and u.email=?;" />

 

Independente de como está seu relacionamento no banco, a questão é que você usaria o jdbc-user-service />

Mas você pode estar perguntando “e se quiser usar minha classe de serviço (service) ou meu DAO, se nessas classes eu já tenha a implementação de busca de um usuário, como fazer?”.

Implementando UserDetailsService

Por default, o Spring a implementa. O que vamos fazer é  um override dessa implementação e dizer que é pra chamar um serviço de busca customizado.

Vou mostrar a seguir apenas o código principal e o que você precisa fazer.  Será bem pontual.

Step 1

Crie uma classe para esse tratamento (chamei de AuthenticationService). Veja :

@Service

public class AuthenticationService implements UserDetailsService {

       @Autowired

       @Qualifier("userServiceImpl")

       private UserService userServiceImpl;      

       @Override

       public UserDetails loadUserByUsername(String email)   throws UsernameNotFoundException, DataAccessException {

List<GrantedAuthority> listGrantAuthority = new ArrayList<GrantedAuthority>();

                    User user = userServiceImpl.getByEmail(email);

                    checkGrantAuthorities(user, listGrantAuthority);

                    UserDetails userDetails = validateUser(email, listGrantAuthority,user);

             return userDetails;

       }

 

O método que vamos trabalhar não é difícil de saber, já que tem anotação @Override. É nele que vamos realizar a busca usando as classes de serviço que chama o DAO. No caso acima o username é o email do usuário, por isso realizo a busca por email.  O método checkGrantAuthorities(…) verifica a role do usuário e adiciona em uma lista de Grant:

private void checkGrantAuthorities(User user, List<GrantedAuthority> listGrantAuthority) {

if(user!=null && user.getRoles()!=null && user.getRoles().isEmpty()==false)

             for(Role roleUser : user.getRoles()){

                    final String PREFIX = "ROLE_";

                    String role = PREFIX + roleUser.getRoleDescription();

                    listGrantAuthority.add(new GrantedAuthorityImpl(role));    

             }

       }

 

Precisamos fazer isso para que o Spring Security possa validar se o usuário passado possui Role de permissão para a página que deseja. O PREFIX que criamos é que no BD você não salva ROLE_ADMIN, daí precisamos fazer isso. Caso já salve como prefixo, você deve remover essa concatenação do código. Uma vantagem de não salvar o prefixo é que se amanhã você mudar de framework que faça essa parte de segurança, terá que fazer update em todo banco. Então é melhor deixar a aplicação tratar isso para seu banco ficar transparente.

Em seguida criei um método que valida se o usuário retornado é válido:

private UserDetails validateUser(String email,List<GrantedAuthority> listGrantAuthority, User user) {

             UserDetails userDetails= null;

             if(user!=null){

                    boolean accountNonLocked=true;

                    boolean enabledUser=true;

                    boolean accountNonExpired=true;

                    boolean credentialsNonExpired=true;

userDetails = new  org.springframework.security.core.userdetails.User(email, user.getPassword(), enabledUser, accountNonExpired, credentialsNonExpired, accountNonLocked, listGrantAuthority);

              }     

             return userDetails;

       }

 

 

Eu tive que colocar o caminho completo do User referente ao Spring  devido já ter um import para minha classe User.

Altere o arquivo de contexto do SpringSecurity. No meu caso, tenho um arquivo XML chamado de springsecurity.xml e no authenticationManager preciso dizer como vamos validar o usuário. Para usar a implementação acima precisamos dizer isso:

 

<authentication-manager alias="authenticationManager">

       <authentication-provider user-service-ref="authenticationService">      

             </authentication-provider>

       </authentication-manager>

 

Pronto. Feito isso, agora basta testar sua aplicação e ver que vai funcionar da mesma forma que o jdbc default, porém agora os dados vem de um serviço de pesquisa que você implementou, que pode ser em HQL, Criteria etc.

A documentação do Spring é muito bacana:

http://docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/reference/ns-config.html

Abraços. Vou ficando por aqui e espero que tenham gostado

See ya!!

Criptografando senha com Spring Security

Olá Pessoal,

No post de hoje vamos ver como criptografar senha usando o Spring Security. O objetivo é que a senha do usuário não seja salva no seu BD, e já sabemos o motivo de fazer isso. Não vou entrar nos detalhes.

Lets go

Requisitos

  • Baixe a versão mais recente do SpringSecurity. (Para o post estou usando a versão 3.2.x)

Starting

Há várias soluções na internet, desde usando o Java puro ou usando APIs, Frameworks para criptografar a senha, e para quem está usando o SpringSecurity no projeto tem uma vantagem: já tem uma classe implementada pelo pessoal do Spring que faz isso em 1 linha. Vejamos a classe que criei a seguir para fazer isso. Vejam como é simples:

public abstract class GenerateHashPasswordUtil {       

        private static Object salt; 

        public static String generateHash(String password) {

                MessageDigestPasswordEncoder digestPasswordEncoder = getInstanceMessageDisterPassword();

                String encodePassword = digestPasswordEncoder.encodePassword(password, salt);

                return encodePassword;

        } 

        private static MessageDigestPasswordEncoder getInstanceMessageDisterPassword() {

//informo tipo de enconding que desejo

MessageDigestPasswordEncoder   digestPasswordEncoder = new MessageDigestPasswordEncoder("MD5");

                return digestPasswordEncoder;

        }

        //método que faz a validação  como não usamos salt deixei em null

        public static boolean isPasswordValid(String password, String hashPassword) {

        MessageDigestPasswordEncoder digestPasswordEncoder = getInstanceMessageDisterPassword();

                return digestPasswordEncoder.isPasswordValid(hashPassword, password, salt);

        }

}

 

E aqui a classe de teste que valida alguns cenários:

public class GenerateHashPasswordUtilTest { 

       @Test

       public void testHashWasGenerateWithSuccess() {

             String password="1234";

             assertNotNull(GenerateHashPasswordUtil.generateHash(password));

       }

       @Test

       public void testValidIfPasswordIsValidAfterHashed(){

             String password="brazil";

              String hashPassword =GenerateHashPasswordUtil.generateHash(password);

 boolean expectedValidPassword = GenerateHashPasswordUtil.isPasswordValid(password, hashPassword);

             assertTrue(expectedValidPassword);

       }

       @Test

       public void testPassWordIsNotEqualToHashCodeGenerated(){

             String password = "XPto";

             String passwordhash = GenerateHashPasswordUtil.generateHash(password);

             String passwordTyped = "xPto";

             boolean expectedInvalidPassword = GenerateHashPasswordUtil.isPasswordValid(passwordTyped, passwordhash);

             assertFalse(expectedInvalidPassword);

       } 

}

 

 Simples, não? Se quiser ver o código hash basta imprimir no console.

Abraços, vou ficando por aqui.

Novo curso: Desenvolvimento de Aplicações JEE Utilizando Frameworks

olá Pessoal, 
 
O post de hoje tem como objetivo apresentar para vocês um novo curso que lançei recentemente pelo IMasterPro. O objetivo é para aqueles iniciantes no mundo JEE  usando frameworks possam ter a oportunidade de colocar mão na massa usando aqueles frameworks que mais ouvimos no dia-dia tais como “Hibernate, JSF, SpringSecurity etc”. A seguir apresento um pouco sobre o curso.
 
Lets go.. 
 
 
Sobre Curso 
 
O curso tem como objetivo de ser prático com exercícios e explicações mão na massa, pois acredito que só aprende praticando, afinal de contas você não vai aprender a dirigir se ler um manual de como dirigir ou apenas como dar partida no carro, isso não é o suficiente para você falar que sabe dirigir, é preciso praticar dia-dia para se tornar um melhor motorista, foi com esse conceito que o criei o curso. 
 
 
O que veremos? 
 
Ahh coloquei um pouco de tudo, Hibernate, SpringSecurity, JSF, Mysql 5.x etc. Vamos misturando um pouco daqui e dali, criando pequenas aplicações e evoluindo a cada aula o aprendizado.
 
O que NÃO é o objetivo do curso 
 
Este não tem como objetivo torná-lo especialista em cada framework, até porque é impossível se tornar especialista em tantas tecnologias em algumas horas apenas. Não tenha essa expectativa. A idéia aqui é para quem quer sair do 0x0.
 
Publico Alvo
Eu digo que é iniciante no mundo JEE e não iniciantes em Java. Se você é iniciante no mundo Java, favor não faça o investimento agora.  Na página do curso eu falo dos requisitos esperados que tenha antes de fazer o investimento.
Mais detalhes do curso
 
Vou ficando por aqui… 
 
abracos, see ya!!!!

Solucionado ROLE_ Spring Security

Olá Pessoal,

O post de hoje vem com o objetivo de complementar dois posts do Edson Gonçalves referente Spring Security. Muitos desenvolvedores que pretende usar Spring Security (SS) em suas apps JEE para implementar a regra de segurança sofrem quando já temos dados cadastrado no BD e não queremos mudar as informações no BD para prefix o ROLE_ que é requerido pelo SS.

Neste post vou mostrar como resolver este problema, que você pode se deparar em sistema legado.

Lets go…

Recomendado:

Spring Security + BD

Spring Security Sem BD

Iniciando

Vou levar em conta que você ja tem implementado o SS conforme este post do Edson Gonçalves. Caso contrário leia o post do Gonçalves antes de continuar com o meu.

Mas, para refrescar sua mente lembre-se que o role deve ter ROLE_ prefixado para que as regras funcionem. Isso é default no SS. Há como alterar, porém pesquisei muitooo e não conseguir implementar, no forum SS há varias sugestoes, mas nenhuma delas foi mais eficiente que prefix o ROLE_ com os dados trazido do BD.

Desenvolvendo

A solução é mais simples que podemos imaginar, você vai precisar apenas prefix o ROLE_. Para quem usa MySQL temos a funcao concat(), no Oracle podemos usar | | etc. Em verifique na documentação do seu banco como concatenar.

A seguir mostramos com o MySQL. É simples demais, confira:

authorities-by-username-query="SELECT  username, concat('ROLE_',authority) FROM users where username = ? "

O código completo fica assim:

< 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, concat('ROLE_',authority) FROM users where username = ? "
/>

ou dessa forma:

 users-by-username-query="SELECT l.login as username, l.senha as password, 'true' as enable from funcionario l where l.login=?"	
authorities-by-username-query="SELECT lo.login as username, CONCAT('ROLE_',lo.cargo)as authority FROM funcionario lo where lo.login=?"	/>
 

Outra dica é se a coluna do usuario/password não for o que o Spring Security espera, basta você fazer conforme o código a seguir:

users-by-username-query="SELECT us.email as username, us.senha as password, 'true' as enable FROM	usuario as us WHERE email=?"
authorities-by-username-query="SELECT  us.email as username, us.tipo as authority FROM usuario us where email= ?"

Vou ficando por aqui, espero que tenham gostado do post.

Abracos e até o próximo post.