JAAS: isUserInRole sempre retorna FALSE

Caros:

Estou com um problema no JAAS e estou compartilhando com vc.

O negócio é que estou fazendo um LoginModule que esta autenticando no servidor AD via EJB3 e gerá um Principal com o User e as sua Roles, até aqui esta tudo certo .
No entanto quando estou na minha aplicação Web (com JSF) e chamo um método que verifica se um usuário pertence a uma Role ( context.getExternalContext().isUserInRole(“logado”):wink: , SEMPRE recebo como FALSE. O mais estranho é que se executo “context.getExternalContext().getUserPrincipal();” eu recebo o usuário.

fiz um debug via console no JBoss e aparentemente ele esta criando o user e suas respectivas roles e adicionando no Principal:

11:13:27,586 INFO [STDOUT] .....inicializando 11:13:27,602 INFO [STDOUT] ..........fazendo o login 11:13:27,602 INFO [STDOUT] ..........buscando o user name e senha 11:13:27,602 INFO [STDOUT] ..............quem esta logado : teste 11:13:27,602 INFO [STDOUT] ..............senha codificada do usuario logado : [C@86f716 11:13:27,602 INFO [STDOUT] ..............senha descodificada do usuario logado : teste 11:13:27,602 INFO [STDOUT] ..........fim da busca 11:13:27,602 INFO [STDOUT] ..........validando o usuario 11:13:27,602 INFO [STDOUT] ..........validou o usuario e atribuiu as roles 11:13:27,602 INFO [STDOUT] .............dados do usuario logado 11:13:27,602 INFO [STDOUT] ..............>>> teste 11:13:27,602 INFO [STDOUT] ..............>>> [br.sanepar.security.principals.Role@1c39aa6, br.sanepar.security.principals.Role@c24ae7] 11:13:27,602 INFO [STDOUT] .............>>>Role : ADM 11:13:27,602 INFO [STDOUT] .............>>>Role : logado 11:13:27,602 INFO [STDOUT] ..........executando o commit() 11:13:27,602 INFO [STDOUT] ..............adiciona o usuario no principals br.sanepar.security.principals.User@1f7a434 11:13:27,617 INFO [STDOUT] ..............adiciona as roles no principals br.sanepar.security.principals.Role@1c39aa6 11:13:27,617 INFO [STDOUT] ..............adiciona as roles no principals br.sanepar.security.principals.Role@c24ae7 11:13:27,867 INFO [DefaultLifecycleProviderFactory] Using LifecycleProvider org.apache.myfaces.config.annotation.TomcatAnnotationLifecyclePro 11:13:27,883 INFO [TomcatAnnotationLifecycleProvider] Creating instance of demo.spring.action.ManagerBean 11:14:01,088 INFO [TomcatAnnotationLifecycleProvider] Creating instance of demo.spring.action.ManagerBean 11:14:01,104 INFO [STDOUT] nome : teste 11:14:01,104 INFO [STDOUT] faz parte da role 'logado'? : false

O meu LoginModuleSanepar.java

public class LoginModuleSanepar implements LoginModule {

	protected Subject subject;
	protected CallbackHandler callbackHandler;
	protected Map sharedState;

	private boolean commitSucceeded = false;
	private boolean succeeded = false;

	private Set roles = new HashSet();
	private User user;

	public boolean abort() throws LoginException {
		// TODO Stub de método gerado automaticamente

		
		if (!succeeded) {
			return false;
		} else if (succeeded && !commitSucceeded) {
			succeeded = false;
		} else {
			succeeded = false;
			logout();
		}

		this.subject = null;
		this.callbackHandler = null;
		this.sharedState = null;
		this.roles = new HashSet();
		
		System.out.println("..........executando o abort()");

		return succeeded;

	}

	public boolean commit() throws LoginException {
		// TODO Stub de método gerado automaticamente

		System.out.println("..........executando o commit()");
		
		// adiciona o usuario no principals
		if (user != null && !subject.getPrincipals().contains(user)) {
			System.out.println("..............adiciona o usuario no principals " + user);
			subject.getPrincipals().add(user);
		}
		// adiciona as roles no principals
		if (roles != null) {
			Iterator it = roles.iterator();
			while (it.hasNext()) {
				Role role = (Role) it.next();
				if (!subject.getPrincipals().contains(role)) {
					System.out.println("..............adiciona as roles no principals " + role);
					subject.getPrincipals().add(role);
				}
			}
		}

		commitSucceeded = true;
		return true;
	}

	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map sharedState, Map options) {
		// TODO Stub de método gerado automaticamente
		this.subject = subject;
		this.callbackHandler = callbackHandler;
		this.sharedState = sharedState;

		System.out.println(".....inicializando");

	}

	public boolean login() throws LoginException {
		// TODO Stub de método gerado automaticamente

		System.out.println("..........fazendo o login");

		getUsernamePassword();

		validaUsuario();

		sharedState.put("javax.security.auth.principal", user);
		sharedState.put("javax.security.auth.roles", roles);

		System.out.println(".............dados do usuario logado");
		System.out.println("..............>>> " + user.getName());
		System.out.println("..............>>> " + user.getRoles());

		Collection<Role> teste = new ArrayList();
		teste.addAll(user.getRoles());
		for (Role r : teste)
			System.out.println(".............>>>Role : "+r.getName());

		return true;
	}

	public boolean logout() throws LoginException {

		// remove o usuario e as roles do principals
		subject.getPrincipals().removeAll(roles);
		subject.getPrincipals().remove(user);
		return true;
	}

	/**
	 * Valida login e senha no banco
	 */
	private void validaUsuario() throws LoginException {
		
		System.out.println("..........validando o usuario");

		if (senhaInformado.equals("teste")) {
			user = new User("teste");
			roles.add(new Role("logado"));
			roles.add(new Role("ADM"));			
			user.setRoles(roles);
			System.out.println("..........validou o usuario e atribuiu as roles");
			return;
		} else {
			System.out.println("..........senha invalida");
			throw new LoginException("Senha Inválida.");
		}
	}

	/**
	 * Login do usuário.
	 */
	protected String loginInformado;

	/**
	 * Senha do usuário.
	 */
	protected String senhaInformado;

	/**
	 * Obtem o login e senha digitados
	 */
	protected void getUsernamePassword() throws LoginException {

		System.out.println("..........buscando o user name e senha");

		if (callbackHandler == null)
			throw new LoginException(
					"Error: no CallbackHandler available to garner authentication information from the user");

		Callback[] callbacks = new Callback[2];
		callbacks[0] = new NameCallback("Login");
		callbacks[1] = new PasswordCallback("Senha", false);

		try {
			callbackHandler.handle(callbacks);
			loginInformado = ((NameCallback) callbacks[0]).getName();
			System.out.println("..............quem esta logado : " + loginInformado);
			char[] tmpPassword = ((PasswordCallback) callbacks[1])
					.getPassword();
			

			senhaInformado = new String(tmpPassword);
			((PasswordCallback) callbacks[1]).clearPassword();
			
			System.out.println("..............senha codificada do usuario logado : " + tmpPassword.toString());
			System.out.println("..............senha descodificada do usuario logado : " + senhaInformado);

		} catch (java.io.IOException ioe) {
			throw new LoginException(ioe.toString());
		} catch (UnsupportedCallbackException uce) {
			throw new LoginException(
					"Error: "
							+ uce.getCallback().toString()
							+ " not available to garner authentication information from the user");
		}

		System.out.println("..........fim da busca");
		
	}

	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}

	public boolean isCommitSucceeded() {
		return commitSucceeded;
	}

	public void setCommitSucceeded(boolean commitSucceeded) {
		this.commitSucceeded = commitSucceeded;
	}

	public boolean isSucceeded() {
		return succeeded;
	}

	public void setSucceeded(boolean succeeded) {
		this.succeeded = succeeded;
	}

}

meu web.xml

<login-config>
		<auth-method>FORM</auth-method>
		<realm-name>LoginModuleSanepar</realm-name>		
		<form-login-config>
			<form-login-page>/security/login.faces</form-login-page>
			<form-error-page>/security/error_login.faces</form-error-page>
		</form-login-config>
	</login-config>
	
	<security-constraint>
		<display-name>aplicacao</display-name>
		<web-resource-collection>
			<web-resource-name>aplicacao</web-resource-name>
			<url-pattern>/pages/*</url-pattern>
		</web-resource-collection>
		<auth-constraint>
			<role-name>logado</role-name>
		</auth-constraint>
	</security-constraint>
	
	<!-- Lista de Roles -->
	<security-role>
		<role-name>logado</role-name>
	</security-role>

Aqui ocorre outro problema, por exemplo se eu seto a tag - auth-constraint - para a role-name = logado. Ele até autentica, mas apresenta um erro : HTTP Status 403 - Access to the requested resource has been denied para testa eu coloquei um ‘*’ no role-name do auth-constraint e autenticou. Mas como podemos ver, sem a Role.

e como estou utilizando o JBOSS, tenho um jboss-web.xml que esta assim…

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
  <security-domain>java:/jaas/LoginModuleSanepar</security-domain>
</jboss-web>

Eu não sei o que esta havendo, mas desconfio que o problema esteja diretamente dentro do LoginModule. Mas não entendo porque ele autentica e faz tudo certinho.

Se puderem me ajudar, ficarei grato.

Desde já obrigado

Srs.

O negócio é assim, como estou utilizando o JBOSS 4.2 é necessário implementar o LoginModule de outra forma. Na verdade deve-se extender a classe UsernamePasswordLoginModule;

Ao final sua classe login módule fica assim :


public class LoginModuleEmpresa extends UsernamePasswordLoginModule

{

	protected Subject subject;
	protected CallbackHandler callbackHandler;
	protected Map sharedState;
	
	private Map<String, String> atributos = new HashMap<String, String>();
	private String dominUser;
	private List listGrupos = new ArrayList();
	

	public void initialize(Subject subject, CallbackHandler callbackHandler,
			Map sharedState, Map options) {

		super.initialize(subject, callbackHandler, sharedState, options);
		this.subject = subject;
		this.callbackHandler = callbackHandler;
		this.sharedState = sharedState;

	}

	@Override
	protected boolean validatePassword(String inputPassword,
			String expectedPassword) {

		boolean isValid = false;
		if (inputPassword != null) {
			try {
				NamingEnumeration answer = null;
				Hashtable parameters = new Hashtable();
				parameters.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
				parameters.put(LdapContext.CONTROL_FACTORIES,"com.sun.jndi.ldap.ControlFactory");
				parameters.put(Context.PROVIDER_URL, "ldap://meuservidor:389");
				parameters.put("java.naming.ldap.version", "3");
				parameters.put(Context.SECURITY_AUTHENTICATION, "simple");
				parameters.put(Context.SECURITY_PRINCIPAL, "usuario_master");
				parameters.put(Context.SECURITY_CREDENTIALS, "senha");
				parameters.put("java.naming.referral", "ignore");

				DirContext ctx = new InitialDirContext(parameters);
				// Criando controle de Busca
				SearchControls searchCtls = new SearchControls();
				// Definindo Escopo da busca
				searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
				// Definindo o Filtro
				String searchFilter = "(&(objectCategory=Person) ((sAMAccountName="+getUsername()+")))";
				//Definindo atributos de retorno
				String returnedAtts[] = {"memberOf","cn","department","sAMAccountName","mail","distinguishedName"};
				searchCtls.setReturningAttributes(returnedAtts);
				// Efetuando a busca com base no filtro
				answer = ctx.search("dc=empresa,dc=com,dc=br", searchFilter,searchCtls);

				while (answer.hasMoreElements()) {
					SearchResult sr = (SearchResult) answer.next();
					Attributes attrs = sr.getAttributes();
					for (NamingEnumeration ae = attrs.getAll(); ae.hasMoreElements();) {
						Attribute attr = (Attribute) ae.next();
						String attrId = attr.getID();
						for (NamingEnumeration vals = attr.getAll(); vals.hasMore();) {
							String thing = vals.next().toString();
							listGrupos.add(thing);
							atributos.put(attrId, thing);
						}
					}

				}
				
				setDominUser(atributos.get("distinguishedName"));
				String msgPass = passwordValidade(inputPassword);
				if(msgPass.equals(""))
					isValid = true;	
				else
					System.out.println(msgPass);
					

			} catch (Throwable e) {
				e.printStackTrace();
				super.setValidateError(e);
			}
		}
		return isValid;
	}

	@Override
	protected String getUsersPassword() throws LoginException {
		return null;
	}

	@Override
	protected Group[] getRoleSets() throws LoginException {

		Group[] groups = { new SimpleGroup("Roles") };
		
		for(int i=0 ; i<listGrupos.size() ; i++)
		{
			String t = listGrupos.get(i).toString();
			String grupos[] = t.split(",");
			for(int j=0 ; j<grupos.length ; j++){
				String var = grupos[j];
				String prefixo = var.substring(0, 3);
				if(prefixo.equals("CN=")){
					SimplePrincipal role = new SimplePrincipal(var.substring(3));
					groups[0].addMember(role);
					System.out.println("grupos " + var.substring(3));
				}

			}
		}
			
		SimplePrincipal role = new SimplePrincipal("logado");
		groups[0].addMember(role);

		
		return groups;
	}

	
	protected String passwordValidade(String password) {

		String msg = "";
		try {
			// Configura as propriedades do usuario a ser autenticado
			Properties parameters = new Properties();

			parameters.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
			parameters.put(LdapContext.CONTROL_FACTORIES,"com.sun.jndi.ldap.ControlFactory");
			parameters.put(Context.PROVIDER_URL, "ldap://meuservidor_ldap:389");
			parameters.put("java.naming.ldap.version", "3");
			parameters.put(Context.SECURITY_AUTHENTICATION, "simple");
			parameters.put(Context.SECURITY_CREDENTIALS, password.toString());
			parameters.put(Context.SECURITY_PRINCIPAL, getDominUser());
			LdapContext ldapContext = (LdapContext) new InitialLdapContext(parameters, null);
			ldapContext.close();

			return msg;

		} catch (Exception e) {
			if (e.toString().indexOf("data 525") > 0)
				msg = "Usuário não encontrado";
			else if (e.toString().indexOf("data 52e") > 0)
				msg = "Senha invalida";
			else if (e.toString().indexOf("data 530") > 0)
				msg = "Logon não permitido neste horário";
			else if (e.toString().indexOf("data 532") > 0)
				msg = "Senha expirada";
			else if (e.toString().indexOf("data 533") > 0)
				msg = "Conta de usuário desativada";
			else if (e.toString().indexOf("data 701") > 0)
				msg = "Conta de usuário expirada";
			else if (e.toString().indexOf("data 775") > 0)
				msg = "Chave bloqueada temporariamente (tente novamente após 5 minutos)";
			else
				msg = e.toString();

			return msg;
		}
	}
	
	
	
	
	
	public String getDominUser() {
		return dominUser;
	}

	public void setDominUser(String dominUser) {
		this.dominUser = dominUser;
	}

	public Map<String, String> getAtributos() {
		return atributos;
	}

	public void setAtributos(Map<String, String> atributos) {
		this.atributos = atributos;
	}

	public List getListGrupos() {
		return listGrupos;
	}

	public void setListGrupos(List listGrupos) {
		this.listGrupos = listGrupos;
	}


}

Essa classe implementa um conexão via JNDI com o servidor LDAP. Depois de encontrar o usuário ele adiciona o “menberof” como Roles do usuário.

Verifiquei que tem como colocar as configurações direta no JBOSS para acesso ao LDAP. No entanto, preciso configurar uma determinada além daquelas que o usuário tem, por exemplo um role USUARIOLOGADO.

Basicamente é isso…

Implementei o JAAS e funciona muiiito bem.

Se tiver mais duvidas entre em contato;

[]´s

e como vc faz a chamada a esta classe LoginModuleEmpresa? tem que fazer mais alguma configuração?
se eu tiver um managedBean que controla o login, como faço pra executar estas classes passando o login e senha?