Я создал пользовательский LoginModule для аутентификации пользователей, присутствующих в коллекциях mongoDB. В моем случае мне нужна одна роль на странице... Я уже использовал аутентификацию JAAS с JSF, но в этом случае она работает не так, как ожидалось... Она всегда возвращает ошибку 403 (Forbidden). Отображение URL-адресов, по-видимому, в порядке.
Что моя иерархия страниц:
Следуйте моим настройкам:
JBoss-web.xml
<jboss-web>
<security-domain>nfceSecurityDomain</security-domain>
<disable-audit>true</disable-audit>
</jboss-web>
web.xml
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/app/login.html</form-login-page>
<form-error-page>/app/login_error.html</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>VISUALIZAR_NOTAS</role-name>
</security-role>
<security-role>
<role-name>GESTAO_CERTIFICADO</role-name>
</security-role>
<security-role>
<role-name>GESTAO_EMPRESA</role-name>
</security-role>
<security-role>
<role-name>DOWNLOAD_XML</role-name>
</security-role>
<security-role>
<role-name>INUTILIZACAO</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>index</web-resource-name>
<url-pattern>/app/index.html</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>VISUALIZAR_NOTAS</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>orderList</web-resource-name>
<url-pattern>/app/pages/orderlist.html</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>VISUALIZAR_NOTAS</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>certificateConfigurations</web-resource-name>
<url-pattern>/app/pages/certifiedlist.html</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>GESTAO_CERTIFICADO</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>enterpriseConfigurations</web-resource-name>
<url-pattern>/app/pages/enterpriselist.html</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>GESTAO_EMPRESA</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>xmlDownload</web-resource-name>
<url-pattern>/app/pages/orderdownload.html</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>DOWNLOAD_XML</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>invalidate</web-resource-name>
<url-pattern>/app/pages/orderInvalidate.html</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>INUTILIZACAO</role-name>
</auth-constraint>
</security-constraint>
standalone.xml
<subsystem xmlns="urn:jboss:domain:security:1.2">
<security-domains>
<security-domain name="nfceSecurityDomain" cache-type="default">
<authentication>
<login-module code="br.com.ciss.nfce.security.JAASLoginModule" flag="required"/>
</authentication>
</security-domain>
</security-domains>
</subsystem>
И вот моя реализация LoginModule, чтобы получить пользователя и роли от MongoDB:
public class JAASLoginModule implements LoginModule {
private static final Logger LOG = Logger.getLogger(JAASLoginModule.class);
private Subject subject;
private CallbackHandler callbackHandler;
private Map sharedState;
private Map options;
private boolean succeeded = false;
private boolean commitSucceeded = false;
private String username = null;
private String _idUser = "";
private char[] password = null;
private Principal userPrincipal = null;
private Principal passwordPrincipal = null;
private ConnectionMongoUtil connectionMongoUtil;
public JAASLoginModule() {
super();
}
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
}
@Override
public boolean login() throws LoginException {
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("username");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
password = ((PasswordCallback) callbacks[1]).getPassword();
if (username == null || password == null) {
LOG.error("Callback handler does not return login data properly");
throw new LoginException(
"Callback handler does not return login data properly");
}
if (isValidUser()) { // validate user.
succeeded = true;
return true;
}
} catch (IOException e) {
e.printStackTrace();
} catch (UnsupportedCallbackException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean commit() throws LoginException {
if (succeeded == false) {
return false;
} else {
userPrincipal = new JAASUserPrincipal(username);
if (!subject.getPrincipals().contains(userPrincipal)) {
subject.getPrincipals().add(userPrincipal);
LOG.debug("User principal added:" + userPrincipal);
}
passwordPrincipal = new JAASPasswordPrincipal(new String(password));
if (!subject.getPrincipals().contains(passwordPrincipal)) {
subject.getPrincipals().add(passwordPrincipal);
LOG.debug("Password principal added: " + passwordPrincipal);
}
List<String> roles = getRoles();
for (String role : roles) {
Principal rolePrincipal = new JAASRolePrincipal(role);
if (!subject.getPrincipals().contains(rolePrincipal)) {
subject.getPrincipals().add(rolePrincipal);
LOG.debug("Role principal added: " + rolePrincipal);
}
}
commitSucceeded = true;
LOG.info("Login subject were successfully populated with principals and roles");
return true;
}
}
@Override
public boolean abort() throws LoginException {
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
succeeded = false;
username = null;
if (password != null) {
password = null;
}
userPrincipal = null;
} else {
logout();
}
return true;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
if (password != null) {
for (int i = 0; i < password.length; i++) {
password[i] = ' ';
password = null;
}
}
userPrincipal = null;
return true;
}
private boolean isValidUser() throws LoginException {
try {
BasicDBObject search = new BasicDBObject();
search.put("email", username);
connectionMongoUtil = new ConnectionMongoUtil();
DBObject user = connectionMongoUtil.getCollection("User").findOne(search);
if (user == null){
LOG.debug("USUÁRIO NÃO LOCALIZADO!");
return false;
}else{
if ( new String(password).equals(user.get("password"))){
_idUser = user.get("_id").toString();
return true;
}else{
LOG.debug("SENHA INVÁLIDA PARA O USUÁRIO " + username);
}
}
return false;
} catch (Exception e) {
LOG.error("Error when loading user " + username + " from the database \n", e);
}
return false;
}
/**
* Returns list of roles assigned to authenticated user.
*
* @return
*/
private List<String> getRoles() {
BasicDBObject search = new BasicDBObject();
search.put("_idUser", _idUser);
DBObject userModules = connectionMongoUtil.getCollection("UserModules").findOne(search);
String jsonArrayModules = userModules.get("_idModules").toString();
String[] modules = null;
try {
modules = new ObjectMapper().readValue(jsonArrayModules, String[].class);
} catch (IOException e) {
e.printStackTrace();
}
List<String> modulesList = new ArrayList<String>();
for (String _idModule : modules) {
DBObject module = connectionMongoUtil.getCollection("Module").findOne(new BasicDBObject("_id", new ObjectId(_idModule)));
if (module != null){
modulesList.add(module.get("name").toString());
}
}
return modulesList;
}
}
Я добавил следующее свойство в standalone.xml, чтобы увидеть журналы, созданные JAAS, и не имеет журнала ошибок...
<logger category="org.jboss.security">
<level name="ALL"/>
</logger>
В моих коллекциях MongoDB я добавил все роли своему пользователю, и когда я пытаюсь войти в систему, я могу войти в систему, но все страницы заблокированы, возвращая ошибку 403.
Кто-нибудь может мне помочь? Может быть, это всего лишь небольшая деталь, вызывающая ошибку 403...
Спасибо за внимание!
Единственный способ разрешить эту ситуацию - это изменение JAAS по Spring Security :)
Я знаю, что это старая нить, но все же без хорошего решения.
У меня была такая же проблема...
В некотором контексте: у меня было "здоровое" Java-приложение, использующее JAAS (прямое выполнение javax.security.auth.spi.LoginModule), плавно выполняющееся на Tomcat7. Затем на сцену пришли требования заказчика: "Он должен работать на JBoss 7.1.1" WTF? Даже небольшая версия и патч!?!
Hands On: я сделал всю конфигурацию, имел некоторые недостатки с кодом Java 8 и JAX-RS, но это еще одна история, в конце концов мое приложение было успешно развернуто в JBoss 7.1.1.
Проблема: я мог видеть на консоли, что логин был успешным, но все запросы возвращали HTTP 403.
При отладке всех Принципов (CallerPrincipal и UserRoles) были в порядке.
Кроме того, у меня есть один абстрактный BaseServlet для оркестровки и который все запросы должны проходить, но HTTP 403 возникла до его достижения...
Решение. Поэтому я заметил, что вся документация JBoss, касающаяся JAAS, советует разработчикам расширять свои настраиваемые LoginModules, поэтому я начал копать.
Org.jboss.security.auth.spi.AbstractServerLoginModule использует то, что они называют "стандартным шаблоном использования JBossSX для хранения идентификаторов и ролей".
Я не стал так подробно объяснять шаблон, но я реализовал "JBossWebRealm", который завершает процесс аутентификации в стороне контейнера, ожидает реализации java.security.acl.Group с именем "Роли", содержащим всех Принципов (CallerPrincipal и UserRoles) в качестве членов (членство в JAASRealm).
Этот объект используется для проверки безопасности.
Конец истории, я добавил этот метод к моей реализации LoginModule:
public boolean commit() throws LoginException {
// some validation code here
Set<Principal> principals = subject.getPrincipals();
// ensure principals contains (CallerPrincipal and UserRoles)
createRolesGroup(principals);
return true;
}
private void createRolesGroup(Set<Principal> principals) {
// Thee java.security.acl.Group implementation
SecurityGroup rolesGroup = new SecurityGroup("Roles");
Iterator<Principal> iter = principals.iterator();
while(iter.hasNext()) {
Object principal = iter.next();
if(!(principal instanceof Group)){
rolesGroup.addMember((Principal)principal);
}
principals.add(rolesGroup);
}
И работает как шарм.
Надеюсь, поможет.
Вы пробовали развернуть свой модуль в wildfly http://www.mastertheboss.com/jboss-server/jboss-security/configuring-a-mongodb-login-module