【问题标题】:Shiro annotation not working on JavaEE6 projectShiro 注释不适用于 JavaEE6 项目
【发布时间】:2012-09-30 10:21:43
【问题描述】:

问题:JavaEE6 的实现在哪里?

我目前正在处理一个 JavaEE6 项目,我发现即使我已经根据文档配置了 web.xml 和 shiro.ini,Shiro 的注释也不能开箱即用。

这就是我所拥有的:

1.) 一个页面:

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

2.) 支持 bean:

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

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

3.) web.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.) shiro.ini

[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"]

但是当我单击按钮时,它仍然执行操作。它应该抛出未经授权的异常吗?其他 shiro 注释也是如此。

请注意,如果我手动执行检查,它会起作用:

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");
    }
}

谢谢,
嫖娼

【问题讨论】:

    标签: security jsf-2 java-ee-6 shiro


    【解决方案1】:

    您基本上需要Java EE interceptor 来扫描调用的 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 实际上是代理且 Shiro 的注释没有设置 @Inherited 的情况下。

    为了让它在 CDI 托管 bean 上工作,首先在/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 中注册拦截器,如下所示(如果您在 EAR 中有单独的 EJB 项目,则在 /META-INF/ejb-jar.xml 中注册):

    <?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 托管 bean 上,您需要设置自定义 @ShiroSecured 注释才能让拦截器运行。

    @Named
    @RequestScoped
    @ShiroSecured
    public class SomeBean {
    
        @RequiresRoles("ADMIN")
        public void doSomethingWhichIsOnlyAllowedByADMIN() {
            // ...
        }
    
    }
    

    这在 EJB 上不是必需的,ejb-jar.xml 已经在所有 EJB 上注册了它。

    另见:

    【讨论】:

    • ShiroSecuredInterceptor 类中,在hasAnnotation 方法中,您只检查一个超类。这有什么原因吗?只是想知道我是否需要将其更改为递归检查。总之,+1!
    • @Jasper:这样我涵盖了代理类(EJB、CDI 等)
    • NetBeans 抱怨(红色下划线错误)@PostConstruct 方法 (An interceptor for lifecycle callbacks may only declare interceptor binding types that are defined as @Target(TYPE)),但是,我的项目仍在构建并且后构造方法仍在工作。据我在谷歌搜索后所知,由于interceptShiroSecurity 中的context.proceed() 语句,post 构造仍然有效,对吧?
    • @Jasper:我不做 Netbeans。
    • 通过将ShiroSecured 注释中的@Target({ ElementType.TYPE, ElementType.METHOD }) 更改为@Target(ElementType.TYPE) 解决了该问题。
    【解决方案2】:

    基本上,我缺少的是 Shiro 的 Requires* 接口的实现,所以我根据自己的需要来实现。有兴趣的可以在这里找到:http://czetsuya-tech.blogspot.com/2012/10/how-to-integrate-apache-shiro-with.html

    【讨论】:

    • 博客上的sn-ps代码质量低,没有覆盖所有注解,漏掉了CDI代理上的类级别注解。
    猜你喜欢
    • 2016-09-13
    • 1970-01-01
    • 2015-02-17
    • 2016-03-30
    • 2016-08-27
    • 2021-11-06
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多