Back-End

18 jun, 2014

Implementando UserDetailsService Spring Security

Publicidade

Olá, pessoal! No artigo de hoje veremos como implementar a interface UserDetailsService do Spring. Mas para que ela serve, Camilo? É o que veremos logo a seguir…

Este artigo será bem pontual e vou considerar que você já usa e conhece o Spring Security. Uma das opções mais comuns 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 eu quiser usar minha classe de serviço (service) ou meu DAO, caso nessas classes eu já tenha a implementação de busca de um usuário?”.

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.

Passo 01:

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 e-mail do usuário, por isso realizo a busca por e-mail.  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, logo 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 por 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. Confira aqui!

Vou ficando por aqui e espero que tenham gostado.

Abraços!