Аннотация Широ не работает над проектом JavaEE6

Вопрос: Где реализация для JavaEE6?

В настоящее время я работаю над проектом JavaEE6, и я обнаружил, что аннотация Широ не работает из коробки, хотя я уже настроил базу web.xml и shiro.ini на основе документации.

Вот что у меня есть:

1.) Страница:

<h:form>
  <h:commandLink action="#{userBean.action1()}" value="Action 1"></h:commandLink>
</h:form>

2.) Поддерживающая фасоль:

@Stateless
@Named
public class UserBean {
    @Inject
    private Logger log;

    @RequiresAuthentication
    public void action1() {
        log.debug("action.1");
    }
}

3.) веб.xml

<listener>
    <listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>
</listener>

<filter>
    <filter-name>ShiroFilter</filter-name>
    <filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>ShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

4.) широ.ини

[main]
# listener = org.apache.shiro.config.event.LoggingBeanListener

shiro.loginUrl = /login.xhtml

[users]
# format: username = password, role1, role2, ..., roleN
root = secret,admin
guest = guest,guest
presidentskroob = 12345,president
darkhelmet = ludicrousspeed,darklord,schwartz
lonestarr = vespa,goodguy,schwartz

[roles]
# format: roleName = permission1, permission2, ..., permissionN
admin = *
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

[urls]
# The /login.jsp is not restricted to authenticated users (otherwise no one could log in!), but
# the 'authc' filter must still be specified for it so it can process that url's
# login submissions. It is 'smart' enough to allow those requests through as specified by the
# shiro.loginUrl above.
/login.xhtml = authc
/logout = logout
/account/** = authc
/remoting/** = authc, roles[b2bClient], perms["remote:invoke:lan,wan"]

Но когда я нажимаю кнопку, он все равно выполняет действие. Это должно вызывать неавторизованное исключение, верно? То же самое верно и для других аннотаций широ.

Обратите внимание, что если я вручную выполняю проверку, она работает:

public void action1() {
    Subject currentUser = SecurityUtils.getSubject();
    AuthenticationToken token = new UsernamePasswordToken("guest", "guest");
    currentUser.login(token);

    log.debug("user." + currentUser);
    if (currentUser.isAuthenticated()) {
        log.debug("action.1");
    } else {
        log.debug("not authenticated");
    }
}

Спасибо,
czetsuya


person czetsuya    schedule 10.10.2012    source источник


Ответы (2)


В основном вам нужен перехватчик Java EE в для сканирования аннотаций к вызываемым методам CDI и EJB.

Сначала создайте аннотацию, которую перехватчик должен перехватить:

@Inherited
@InterceptorBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ShiroSecured {
    //
}

Затем создайте сам перехватчик:

@Interceptor
@ShiroSecured
public class ShiroSecuredInterceptor implements Serializable {

    private static final long serialVersionUID = 1L;

    @AroundInvoke
    public Object interceptShiroSecurity(InvocationContext context) throws Exception {
        Class<?> c = context.getTarget().getClass();
        Method m = context.getMethod();
        Subject subject = SecurityUtils.getSubject();

        if (!subject.isAuthenticated() && hasAnnotation(c, m, RequiresAuthentication.class)) {
            throw new UnauthenticatedException("Authentication required");
        }

        if (subject.getPrincipal() != null && hasAnnotation(c, m, RequiresGuest.class)) {
            throw new UnauthenticatedException("Guest required");
        }

        if (subject.getPrincipal() == null && hasAnnotation(c, m, RequiresUser.class)) {
            throw new UnauthenticatedException("User required");
        }

        RequiresRoles roles = getAnnotation(c, m, RequiresRoles.class);

        if (roles != null) {
            subject.checkRoles(Arrays.asList(roles.value()));
        }

        RequiresPermissions permissions = getAnnotation(c, m, RequiresPermissions.class);

        if (permissions != null) {
             subject.checkPermissions(permissions.value());
        }

        return context.proceed();
    }

    private static boolean hasAnnotation(Class<?> c, Method m, Class<? extends Annotation> a) {
        return m.isAnnotationPresent(a)
            || c.isAnnotationPresent(a)
            || c.getSuperclass().isAnnotationPresent(a);
    }

    private static <A extends Annotation> A getAnnotation(Class<?> c, Method m, Class<A> a) {
        return m.isAnnotationPresent(a) ? m.getAnnotation(a)
            : c.isAnnotationPresent(a) ? c.getAnnotation(a)
            : c.getSuperclass().getAnnotation(a);
    }

}

Обратите внимание, что аннотации проверяются в суперклассе целевого класса, а целевой класс может быть в случае, если CDI фактически является прокси-сервером, а аннотации Широ не имеют установленного @Inherited.

Чтобы заставить его работать с управляемыми компонентами CDI, сначала зарегистрируйте перехватчик в /WEB-INF/beans.xml следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd"
>
    <interceptors>
        <class>com.example.interceptor.ShiroSecuredInterceptor</class>
    </interceptors>
</beans>

Точно так же, чтобы заставить его работать с EJB, сначала зарегистрируйте перехватчик в /WEB-INF/ejb-jar.xml следующим образом (или в /META-INF/ejb-jar.xml, если у вас есть отдельный проект EJB в EAR):

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
    version="3.1"
>
    <interceptors>
        <interceptor>
            <interceptor-class>com.example.interceptor.ShiroSecuredInterceptor</interceptor-class>
        </interceptor>
    </interceptors>
    <assembly-descriptor>
        <interceptor-binding>
            <ejb-name>*</ejb-name>
            <interceptor-class>com.example.interceptor.ShiroSecuredInterceptor</interceptor-class>
        </interceptor-binding>
    </assembly-descriptor>
</ejb-jar>

В управляемом компоненте CDI вам необходимо установить пользовательскую аннотацию @ShiroSecured, чтобы запустить перехватчик.

@Named
@RequestScoped
@ShiroSecured
public class SomeBean {

    @RequiresRoles("ADMIN")
    public void doSomethingWhichIsOnlyAllowedByADMIN() {
        // ...
    }

}

В этом нет необходимости для EJB, ejb-jar.xml уже зарегистрировал его во всех EJB.

Смотрите также:

person BalusC    schedule 23.01.2013
comment
В классе ShiroSecuredInterceptor в методе hasAnnotation вы проверяете только один суперкласс. Есть ли причина для этого? Просто интересно, нужно ли мне изменить его на рекурсивную проверку. В любом случае +1! - person Jasper de Vries; 19.10.2015
comment
@Jasper: таким образом я охватываю классы прокси (EJB, CDI и т. д.) - person BalusC; 19.10.2015
comment
NetBeans жалуется (ошибка, подчеркнутая красным) на метод @PostConstruct (An interceptor for lifecycle callbacks may only declare interceptor binding types that are defined as @Target(TYPE)), однако мой проект все еще строится, и метод пост-конструкции все еще работает. Насколько я могу судить после некоторого поиска в Google, конструкция post все еще работает из-за оператора context.proceed() в interceptShiroSecurity, верно? - person Jasper de Vries; 19.10.2015
comment
@Jasper: я не пользуюсь Netbeans. - person BalusC; 19.10.2015
comment
Проблема решена путем изменения @Target({ ElementType.TYPE, ElementType.METHOD }) на @Target(ElementType.TYPE) в аннотации ShiroSecured. - person Jasper de Vries; 19.10.2015

По сути, чего мне не хватает, так это реализации интерфейсов Shiro Requires*, поэтому я реализовал ее в зависимости от своих потребностей. Те из вас, кто заинтересован, могут найти его здесь: http://czetsuya-tech.blogspot.com/2012/10/how-to-integrate-apache-shiro-with.html

person czetsuya    schedule 11.10.2012
comment
Фрагменты кода в блоге имеют низкое качество, не охватывают все аннотации и пропускают аннотации на уровне класса на прокси-серверах CDI. - person BalusC; 23.01.2013