воскресенье, 4 мая 2008 г.

JBoss Seam and Custom Security with privileges[RUS]

Для создания привилегий нам потребуются привилегии и собственно класс который их обрабатывает. И так по порядку.

import icust.entity.Privilegue;
import icust.entity.User;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.security.Identity;

import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;

@Name("org.jboss.seam.security.identity")
@Scope(value = ScopeType.SESSION)
@Install(precedence = Install.DEPLOYMENT)
@BypassInterceptors
@Startup
public class ICustIdentity extends Identity implements Serializable {
private User currentUser;
private Map permissions;


public void setPermissions(Map permissions) {
this.permissions = permissions;
}

public boolean hasPermission(String name, String action, Object...arg) {
if (permissions==null||permissions.size()==0) {
return false;
}

Iterator iter = permissions.keySet().iterator();
while (iter.hasNext()) {
Object key = iter.next();
Privilegue priv = (Privilegue) permissions.get(key);
if (priv.getName().equalsIgnoreCase(name)&&priv.getAction().equalsIgnoreCase(action)) {
return true;
}
}
return false;
}
public User getCurrentUser() {
return currentUser;
}
public void setCurrentUser(User currentUser) {
this.currentUser = currentUser;
}
}

как видите ничего сложного, на что неоюходимо обратить внимание:
1. Класс должен наследоваться от Identity
2. Аннотация. Разберемся подробнее:
@Name("org.jboss.seam.security.identity")
имя бина в конткетсе seam, оно должно быть таким и никаким другим.
@Scope(value = ScopeType.SESSION)
тут я думаю объяснять не стоит. Безопастность хранится в сессии ну и может еще в куках )
@Install(precedence = Install.DEPLOYMENT)
важный параметр, в свое время потратил много времени что бы понять почему не работает, хотя должно. он отвечает насколько данный компонент стоит выше в иерархии одноименных копнонетов. Т.е. компоненты могут иметь одинаковое имя, но вот который из них будет загружен указывает именно этот параметр. Более подробно ищите в документации к seam.
@BypassInterceptors
Точно не скажу, но могу предположить - игнорируем все перехватчики
@Startup
ну и напоследок, данный компонет должен загружатся в самом начале.
3 Метод
основной метод который делает всю работу это
public boolean hasPermission(String name, String action, Object...arg)
соответственно стандартный класс поставляемый в seam в данном методе реализует просто заглушку. Мы же делаем там некую работу по проверке привилегий.
Далее нам понадобится прописать в components.xml

Выражение "#{Login.login}" означает что проверку авторизации будет выболнять компонент Login в методе login. Параметр authenticate-every-request важен, если вы не хотите что и в всплывающих окнах проводилась проверка авторизации.

<event type="org.jboss.seam.notLoggedIn">
<action expression="#{redirect.captureCurrentView}">
</event>

<event type="org.jboss.seam.postAuthenticate">
<action expression="#{redirect.returnToCapturedView}">
</event>

Эти два обработчика событий описывают поведение seam в случае успешной и не успешной авторизации. Если авторизация прошла успешно, пользователь будет автоматически переадресован на страницу к которой он пытался получить доступ.

Далее pages.xml

<?xml version="1.0" encoding="UTF-8"?>
<pages xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.0.xsd"
login-view-id="/login.xhtml">
<page id="/user/*" required="true">
</page>
</pages>

тут и объяснять то особо нечего. Хочу отметить только что это не единственно возможный варинат закрытия доступа к страницам. Так же можно воспользоваться тэгом restrict или же попытатся реализовать свой PhaseListener =)

Теперь рассмотрим метод авторизации:

public boolean login() {
try {
Criteria crit = hibernateSession.createCriteria(User.class);
crit.add(Expression.eq("login",Identity.instance().getUsername()).ignoreCase());
crit.add(Expression.eq("password",Identity.instance().getPassword()));
crit.add(Expression.eq("status",Boolean.TRUE));
User user = (User) crit.uniqueResult();
if (user!=null) {
Identity.instance().setUsername(user.getName());
((ICustIdentity)Identity.instance()).setCurrentUser(user);
user.setLastaccess(new Date());
hibernateSession.update(user);
if (user.getRole() != null)
{
Identity.instance().addRole(user.getRole().getName());
((ICustIdentity)Identity.instance()).setPermissions(user.getRole().getPrivs());
}
if (redirect.getViewId()==null) {
redirect.setViewId("/user/index.xhtml");
}

return true;
} else {
FacesMessages.instance().add("Invalid username/password");
return false;
}
} catch (Exception e) {
log.error(e.getMessage(), e);
FacesMessages.instance().add(new FacesMessage("application.error"));
}
return false;
}

Как видите он возвращает true или false в зависимости от того авторизовался пользователь или нет. Я не буду подробно останавливатся на данном методе, расскажу только о двух моментах:
1. Установка роли и привилегий
Identity.instance().addRole(user.getRole().getName());
((ICustIdentity)Identity.instance()).setPermissions(user.getRole().getPrivs());
2. Переадресация
if (redirect.getViewId()==null) {
redirect.setViewId("/user/index.xhtml");
}
Seam имеет встроенный компонет redirect(см документацию). Так в чем тут особенность? Дело в том что если вы попытались обратится к странице, доступ к которой закрыт неавторизованному пользователю то после авторизации вас автоматически перебросит на эту страницу(redirect.getViewId() будет содержать адресс той страницы.). Но что делать если вы сразу обратились к странице авторизации, то Seam 
не будет знать куда вас перебросить.
Ну и наконец то ради чего вы все это читали :-)
rendered="#{s:hasPermission('role','add','')||s:hasPermission('role','edit','')}"
или же прямо на методе:
@Restrict("#{s:hasPermission('account','modify',selectedAccount)}")
public void modify() {}

более подробно можно посмотреть перейдя по ссылке:
http://docs.jboss.com/seam/1.1.5.GA/reference/en/html/security.html

Комментариев нет: