@Stateless
@RolesAllowed({"user", "employee", "admin"})
@RunAs("inventoryDpt")
public class ItemEJB {
··@PersistenceContext(unitName = "chapter08PU")
··private EntityManager em;
··@EJB
··private InventoryEJB inventory;
··public List
····TypedQuery
····return query.getResultList();
··}
··public Book createBook(Book book) {
····em.persist(book);
····inventory.addItem(book);
····returnbook;
··}
}
Как вы можете видеть, декларативная авторизация дает вам легкий доступ к политике аутентификации. Но что, если необходимо предоставить параметры безопасности индивида или применить какую-либо бизнес-логику на основе текущей роли принципала? Здесь вам поможет программная авторизация.
Декларативная авторизация охватывает большинство вопросов безопасности, которые необходимо решить приложению. Но иногда нужно настроить более тонкую авторизацию (позволив запустить блок кода, а не целый метод, разрешить или запретить доступ к индивиду вместо роли и т. д.). Вы можете использовать программную авторизацию, чтобы выборочно разрешать или блокировать доступ к роли или принципалу. Это становится возможно потому, что у вас есть прямой доступ к интерфейсу JAAS java.security.Principal, а также к контексту EJB, что позволяет проверить роль принципала в коде.
Интерфейс SessionContext определяет следующие методы, связанные с безопасностью:
• isCallerInRole() — возвращает переменную типа boolean и проверяет, имеет ли вызывающая сторона заданную роль безопасности;
• getCallerPrincipal() — возвращает java.security.Principal, который идентифицирует вызывающую сторону.
Чтобы понять, как применять эти методы, рассмотрим пример. Класс ItemEJB в листинге 8.9 не использует декларативные методы безопасности, но нуждается в выполнении какой-то проверки программно. Прежде всего, компоненту необходимо получить ссылку на его контекст (с помощью аннотации @Resource). В связи с этим метод deleteBook() может проверить, имеет ли вызывающая сторона роль admin. Если результат проверки отрицательный, генерируется исключение java.lang.SecurityException, уведомляющее пользователя о нарушении авторизации. Метод createBook() выполняет бизнес-логику с использованием ролей и принципалов. Обратите внимание, что метод getCallerPrincipal() возвращает объект типа Principal, который имеет имя. Метод проверяет, соответствует ли имя принципала значению "paul", а затем устанавливает значение "special user" в сущности книги.
@Stateless
public class ItemEJB {
··@PersistenceContext(unitName = "chapter08PU")
··private EntityManager em;
··@Resource
··private SessionContext ctx;
··public void deleteBook(Book book) {
····if (!ctx.isCallerInRole("admin"))
······throw new SecurityException("Разрешено только администраторам");
····em.remove(em.merge(book));
··}
··public Book createBook(Book book) {
····if (ctx.isCallerInRole("employee") &&!ctx.isCallerInRole("admin")) {
······book.setCreatedBy("employee only");
····} else if (ctx.getCallerPrincipal(). getName(). equals("paul")) {
······book.setCreatedBy("special user");
····}
····em.persist(book);
····returnbook;
··}
}
В разделе «Все вместе» главы 4 я продемонстрировал разработку сущности Book (показанную в листинге 4.3), которая отображается в базе данных Derby. Тогда я показал вам класс Main (см. листинг 4.4), который использует менеджер сущностей для сохранения книги и получения всех книг из базы данных (применяя явное разграничение транзакции: tx.begin() и tx.commit()).