Adds support for javax.annotation.security.RolesAllowed, PermitAll, and DenyAll
diff --git a/core/pom.xml b/core/pom.xml
index 39ea570..17b695f 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -94,6 +94,12 @@
             <artifactId>shiro-event</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+
         <!-- Test dependencies -->
         <dependency>
             <groupId>org.slf4j</groupId>
diff --git a/core/src/main/java/org/apache/shiro/authz/aop/JavaxSecurityRolesAnnotationHandler.java b/core/src/main/java/org/apache/shiro/authz/aop/JavaxSecurityRolesAnnotationHandler.java
new file mode 100644
index 0000000..6198deb
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/authz/aop/JavaxSecurityRolesAnnotationHandler.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.authz.aop;
+
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.annotation.Logical;
+import org.apache.shiro.authz.annotation.RequiresRoles;
+
+import javax.annotation.security.RolesAllowed;
+import java.lang.annotation.Annotation;
+import java.util.Arrays;
+
+/**
+ * Checks to see if a @{@link RolesAllowed}, annotation is declared, and if so, performs
+ * a role check to see if the calling <code>Subject</code> is allowed to proceed.
+ *
+ * @since 1.4.0
+ */
+public class JavaxSecurityRolesAnnotationHandler extends AuthorizingAnnotationHandler {
+
+    /**
+     * Default no-argument constructor that ensures this handler looks for
+     * {@link RequiresRoles RequiresRoles} annotations.
+     */
+    public JavaxSecurityRolesAnnotationHandler() {
+        super(RolesAllowed.class);
+    }
+
+    /**
+     * Ensures that the calling <code>Subject</code> has the Annotation's specified roles, and if not, throws an
+     * <code>AuthorizingException</code> indicating that access is denied.
+     *
+     * @param a the RolesAllowed annotation to use to check for one or more roles
+     * @throws AuthorizationException
+     *          if the calling <code>Subject</code> does not have the role(s) necessary to
+     *          proceed.
+     */
+    public void assertAuthorized(Annotation a) throws AuthorizationException {
+
+        if (!(a instanceof RolesAllowed)) {
+            return;
+        }
+
+        RolesAllowed rannotation = (RolesAllowed) a;
+        String[] roles = rannotation.value();
+
+        if (roles.length == 1) {
+            getSubject().checkRole(roles[0]);
+            return;
+        }
+
+        // Avoid processing exceptions unnecessarily - "delay" throwing the exception by calling hasRole first
+        boolean hasAtLeastOneRole = false;
+
+        for (String role : roles) {
+            if (getSubject().hasRole(role)) {
+                hasAtLeastOneRole = true;
+            }
+        }
+
+        // Cause the exception if none of the role match, note that the exception message will be a bit misleading
+        if (!hasAtLeastOneRole)
+        {
+            getSubject().checkRole(roles[0]);
+        }
+    }
+
+}
diff --git a/core/src/main/java/org/apache/shiro/authz/aop/JavaxSecurityRolesMethodInterceptor.java b/core/src/main/java/org/apache/shiro/authz/aop/JavaxSecurityRolesMethodInterceptor.java
new file mode 100644
index 0000000..8530fab
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/authz/aop/JavaxSecurityRolesMethodInterceptor.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.authz.aop;
+
+import org.apache.shiro.aop.AnnotationResolver;
+import org.apache.shiro.aop.MethodInvocation;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.UnauthorizedException;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+
+/**
+ * Checks to see if a @{@link javax.annotation.security.RolesAllowed RolesAllowed} annotation is declared, and if so, performs
+ * a role check to see if the calling <code>Subject</code> is allowed to invoke the method.
+ *
+ * @since 1.4.0
+ */
+public class JavaxSecurityRolesMethodInterceptor extends AuthorizingAnnotationMethodInterceptor {
+
+    /**
+     * Default no-argument constructor that ensures this interceptor looks for
+     * {@link javax.annotation.security.RolesAllowed RolesAllowed} annotations in a method declaration.
+     */
+    public JavaxSecurityRolesMethodInterceptor() {
+        super( new JavaxSecurityRolesAnnotationHandler() );
+    }
+
+    /**
+     * @param resolver
+     * @since 1.4.0
+     */
+    public JavaxSecurityRolesMethodInterceptor(AnnotationResolver resolver) {
+        super(new JavaxSecurityRolesAnnotationHandler(), resolver);
+    }
+
+    @Override
+    public void assertAuthorized(MethodInvocation mi) throws AuthorizationException {
+
+        DenyAll denyAllOnMethod = mi.getMethod().getAnnotation(DenyAll.class); // this can only be at the method level
+        PermitAll permitAllOnMethod = mi.getMethod().getAnnotation(PermitAll.class); // a PermitAll on a class, is the default condition.
+        RolesAllowed rolesAllowedOnMethod = mi.getMethod().getAnnotation(RolesAllowed.class);
+
+        // DenyAll on the method take precedence over RolesAllowed and PermitAll
+        if (denyAllOnMethod != null ) {
+            throw new UnauthorizedException("Subject does not have access to method due to DenyAll annotation.");
+        }
+
+        // RolesAllowed on the method takes precedence over PermitAll
+        if(rolesAllowedOnMethod != null) {
+            super.assertAuthorized(mi);
+            return;
+        }
+
+        // PermitAll on method takes precedence over RolesAllowed on the class
+        if (permitAllOnMethod != null) {
+            // just return
+            return;
+        }
+
+        // DenyAll can't be attached to classes
+
+        // RolesAllowed on the class takes precedence over PermitAll
+        super.assertAuthorized(mi);
+    }
+}
diff --git a/core/src/test/groovy/org/apache/shiro/aop/JavaxSecurityRolesMethodIntercepterTest.groovy b/core/src/test/groovy/org/apache/shiro/aop/JavaxSecurityRolesMethodIntercepterTest.groovy
new file mode 100644
index 0000000..9828ee6
--- /dev/null
+++ b/core/src/test/groovy/org/apache/shiro/aop/JavaxSecurityRolesMethodIntercepterTest.groovy
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.aop
+
+import org.apache.shiro.authz.UnauthorizedException
+import org.apache.shiro.authz.aop.JavaxSecurityRolesMethodInterceptor
+import org.apache.shiro.subject.Subject
+import org.apache.shiro.util.ThreadContext
+import org.junit.Assert;
+import org.junit.Test;
+
+import static org.apache.shiro.aop.JavaxSecurityRoleStubs.*
+
+import java.lang.reflect.Method
+
+import static org.easymock.EasyMock.*
+import static org.junit.Assert.*
+
+
+/**
+ * Tests for {@link JavaxSecurityRolesMethodInterceptor}.
+ */
+public class JavaxSecurityRolesMethodIntercepterTest {
+
+    @Test
+    public void rolesAllowedOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        expect(subject.checkRole("RoleOne"))
+        assertTrue executeStubMethod(new RolesAllowedOnMethod(), subject);
+    }
+
+    @Test
+    public void rolesAllowedOnClass() throws Throwable {
+
+        Subject subject = mock(Subject)
+        expect(subject.checkRole("RoleOne"))
+        assertTrue executeStubMethod(new RolesAllowedOnClass(), subject);
+    }
+
+
+    @Test
+    public void permitAllOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        assertTrue executeStubMethod(new PermitAllOnMethod(), subject);
+    }
+
+    @Test
+    public void permitAllOnClass() throws Throwable {
+
+        Subject subject = mock(Subject)
+        assertTrue executeStubMethod(new PermitAllOnClass(), subject);
+    }
+
+    @Test(expected = UnauthorizedException.class)
+    public void denyAllOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        executeStubMethod(new DenyAllOnMethod(), subject);
+    }
+
+    @Test(expected = UnauthorizedException.class)
+    public void rolesAllowedOnClassDenyAllOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        executeStubMethod(new RolesAllowedOnClassDenyAllOnMethod(), subject);
+    }
+
+    @Test(expected = UnauthorizedException.class)
+    public void rolesAllowedOnMethodDenyAllOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        executeStubMethod(new RolesAllowedOnMethodDenyAllOnMethod(), subject);
+    }
+
+    @Test
+    public void rolesAllowedOnClassPermitAllOnClass() throws Throwable {
+
+        Subject subject = mock(Subject)
+        expect(subject.checkRole("RoleOne"))
+        assertTrue executeStubMethod(new RolesAllowedOnClassPermitAllOnClass(), subject);
+    }
+
+    @Test
+    public void rolesAllowedOnMethodPermitAllOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        expect(subject.checkRole("RoleOne"))
+        assertTrue executeStubMethod(new RolesAllowedOnMethodPermitAllOnMethod(), subject);
+    }
+
+    @Test
+    public void rolesAllowedOnClassPermitAllOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        assertTrue executeStubMethod(new RolesAllowedOnClassPermitAllOnMethod(), subject);
+    }
+
+    @Test
+    public void permitAllOnClassRolesAllowedOnMethod() throws Throwable {
+
+        Subject subject = mock(Subject)
+        expect(subject.checkRole("RoleOne"))
+        assertTrue executeStubMethod(new PermitAllOnClassRolesAllowedOnMethod(), subject);
+    }
+
+
+
+
+
+
+    static boolean executeStubMethod(final SimpleStub stub, Subject subject) {
+
+        try {
+            replay subject
+            ThreadContext.bind(subject)
+
+            JavaxSecurityRolesMethodInterceptor interceptor = new JavaxSecurityRolesMethodInterceptor();
+
+            Object result = interceptor.invoke(new MethodInvocation() {
+                @Override
+                public Object proceed() throws Throwable {
+                    return stub.callMe();
+                }
+
+                @Override
+                public Method getMethod() {
+                    try {
+                        return stub.getClass().getMethod("callMe");
+                    } catch (NoSuchMethodException e) {
+                        Assert.fail("Failed to return method 'callMe()' on stub");
+                        return null;
+                    }
+                }
+
+                @Override
+                public Object[] getArguments() {
+                    return new Object[0];
+                }
+
+                @Override
+                public Object getThis() {
+                    return stub;
+                }
+            });
+
+            verify subject
+
+            return result;
+        }
+        finally {
+            ThreadContext.unbindSubject()
+        }
+
+
+
+    }
+}
diff --git a/core/src/test/java/org/apache/shiro/aop/JavaxSecurityRoleStubs.java b/core/src/test/java/org/apache/shiro/aop/JavaxSecurityRoleStubs.java
new file mode 100644
index 0000000..2a07a2d
--- /dev/null
+++ b/core/src/test/java/org/apache/shiro/aop/JavaxSecurityRoleStubs.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.aop;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+public final class JavaxSecurityRoleStubs {
+
+    public interface SimpleStub {
+        boolean callMe();
+    }
+
+
+    @RolesAllowed("RoleOne")
+    public static class RolesAllowedOnClass implements SimpleStub {
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    public static class RolesAllowedOnMethod implements SimpleStub {
+        @RolesAllowed("RoleOne")
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    public static class PermitAllOnMethod implements SimpleStub {
+        @PermitAll
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    @PermitAll
+    public static class PermitAllOnClass implements SimpleStub {
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    public static class DenyAllOnMethod implements SimpleStub {
+        @DenyAll
+        public boolean callMe() {
+            return false;
+        }
+    }
+
+    @RolesAllowed("RoleOne")
+    public static class  RolesAllowedOnClassDenyAllOnMethod implements SimpleStub {
+        @DenyAll
+        public boolean callMe() {
+            return false;
+        }
+    }
+
+    public static class  RolesAllowedOnMethodDenyAllOnMethod implements SimpleStub {
+        @RolesAllowed("RoleOne")
+        @DenyAll
+        public boolean callMe() {
+            return false;
+        }
+    }
+
+    @RolesAllowed("RoleOne")
+    @PermitAll
+    public static class RolesAllowedOnClassPermitAllOnClass implements SimpleStub {
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    public static class RolesAllowedOnMethodPermitAllOnMethod implements SimpleStub {
+        @RolesAllowed("RoleOne")
+        @PermitAll
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    @RolesAllowed("RoleOne")
+    public static class RolesAllowedOnClassPermitAllOnMethod implements SimpleStub {
+        @PermitAll
+        public boolean callMe() {
+            return true;
+        }
+    }
+
+    @PermitAll
+    public static class PermitAllOnClassRolesAllowedOnMethod implements SimpleStub {
+        @RolesAllowed("RoleOne")
+        public boolean callMe() {
+            return true;
+        }
+    }
+}
diff --git a/pom.xml b/pom.xml
index 1162d41..3959dc8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -813,6 +813,11 @@
                 <scope>provided</scope>
             </dependency>
             <dependency>
+                <groupId>javax.annotation</groupId>
+                <artifactId>javax.annotation-api</artifactId>
+                <version>1.2</version>
+            </dependency>
+            <dependency>
                 <groupId>log4j</groupId>
                 <artifactId>log4j</artifactId>
                 <version>1.2.17</version>
diff --git a/samples/jaxrs/pom.xml b/samples/jaxrs/pom.xml
index da02fed..3c57d32 100644
--- a/samples/jaxrs/pom.xml
+++ b/samples/jaxrs/pom.xml
@@ -80,6 +80,12 @@
             <artifactId>shiro-jaxrs</artifactId>
         </dependency>
 
+        <!-- enable optional support for javax.annotation.security annotations -->
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+        </dependency>
+
         <dependency>
             <!-- Required for any libraries that expect to call the commons logging APIs -->
             <groupId>org.slf4j</groupId>
diff --git a/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/SampleApplication.java b/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/SampleApplication.java
index b7ae949..00b8649 100644
--- a/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/SampleApplication.java
+++ b/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/SampleApplication.java
@@ -20,6 +20,7 @@
 
 import org.apache.shiro.samples.jaxrs.resources.HelloResource;
 import org.apache.shiro.samples.jaxrs.resources.SecureResource;
+import org.apache.shiro.web.jaxrs.JavaxSecurityAnnotationFilterFeature;
 import org.apache.shiro.web.jaxrs.ShiroFeature;
 
 import javax.ws.rs.ApplicationPath;
@@ -41,6 +42,9 @@
         // register Shiro
         classes.add(ShiroFeature.class);
 
+        // optional javax.annotations.security support
+        classes.add(JavaxSecurityAnnotationFilterFeature.class);
+
         // register resources
         classes.add(HelloResource.class);
         classes.add(SecureResource.class);
diff --git a/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/resources/SecureResource.java b/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/resources/SecureResource.java
index c590987..47e347e 100644
--- a/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/resources/SecureResource.java
+++ b/samples/jaxrs/src/main/java/org/apache/shiro/samples/jaxrs/resources/SecureResource.java
@@ -25,12 +25,15 @@
 import org.apache.shiro.authz.annotation.RequiresRoles;
 import org.apache.shiro.authz.annotation.RequiresUser;
 
+import javax.annotation.security.RolesAllowed;
 import javax.ws.rs.DefaultValue;
 import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
 import javax.ws.rs.QueryParam;
 
+import static org.glassfish.grizzly.http.util.Header.Allow;
+
 @Path("secure")
 @Produces({"application/json","plain/text"})
 public class SecureResource {
@@ -71,5 +74,11 @@
         return "protected";
     }
 
+    @RolesAllowed("admin")
+    @Path("RolesAllowed")
+    @GET
+    public String protectedByRolesAllowed() {
+        return "protected";
+    }
 
 }
diff --git a/samples/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/ContainerIntegrationIT.groovy b/samples/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/ContainerIntegrationIT.groovy
index 84899d7..40495ce 100644
--- a/samples/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/ContainerIntegrationIT.groovy
+++ b/samples/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/ContainerIntegrationIT.groovy
@@ -96,6 +96,31 @@
     }
 
     @Test
+    void testSecuredRolesAllowed() {
+
+        get(getBaseUri() + "secure/RolesAllowed")
+                .then()
+                .assertThat().statusCode(is(401))
+
+        given()
+                .header("Authorization", getBasicAuthorizationHeaderValue("guest", "guest"))
+                .when()
+                .get(getBaseUri() + "secure/RolesAllowed")
+                .then()
+                .assertThat()
+                .statusCode(is(403)).and()
+
+        given()
+                .header("Authorization", getBasicAuthorizationHeaderValue("root", "secret"))
+                .when()
+                .get(getBaseUri() + "secure/RolesAllowed")
+                .then()
+                .assertThat()
+                .statusCode(is(200)).and()
+                .body(equalTo("protected"))
+    }
+
+    @Test
     void testSecuredRequiresPermissions() {
 
         get(getBaseUri() + "secure/RequiresPermissions")
diff --git a/support/jaxrs/pom.xml b/support/jaxrs/pom.xml
index 05b3c5b..bcf922b 100644
--- a/support/jaxrs/pom.xml
+++ b/support/jaxrs/pom.xml
@@ -49,6 +49,12 @@
             <version>2.0.1</version>
         </dependency>
 
+        <dependency>
+            <groupId>javax.annotation</groupId>
+            <artifactId>javax.annotation-api</artifactId>
+            <optional>true</optional>
+        </dependency>
+
         <!-- Test Deps -->
         <dependency>
             <groupId>org.slf4j</groupId>
diff --git a/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/JavaxSecurityAnnotationFilterFeature.java b/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/JavaxSecurityAnnotationFilterFeature.java
new file mode 100644
index 0000000..49d4374
--- /dev/null
+++ b/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/JavaxSecurityAnnotationFilterFeature.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.web.jaxrs;
+
+import org.apache.shiro.web.filter.authz.AuthorizationFilter;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.DynamicFeature;
+import javax.ws.rs.container.ResourceInfo;
+import javax.ws.rs.core.FeatureContext;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Wraps {@link AuthorizationFilter filters} around JAX-RS resources that are annotated with Shiro annotations.
+ * @since 1.4
+ */
+public class JavaxSecurityAnnotationFilterFeature implements DynamicFeature {
+
+    private static List<Class<? extends Annotation>> javaxSecurityAnnotations = Collections.unmodifiableList(Arrays.asList(
+            DenyAll.class,
+            PermitAll.class,
+            RolesAllowed.class));
+
+    @Override
+    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
+
+        List<Annotation> authzSpecs = new ArrayList<Annotation>();
+
+        for (Class<? extends Annotation> annotationClass : javaxSecurityAnnotations) {
+            // XXX What is the performance of getAnnotation vs getAnnotations?
+            Annotation classAuthzSpec = resourceInfo.getResourceClass().getAnnotation(annotationClass);
+            Annotation methodAuthzSpec = resourceInfo.getResourceMethod().getAnnotation(annotationClass);
+
+            if (classAuthzSpec != null) {
+                authzSpecs.add(classAuthzSpec);
+            }
+            if (methodAuthzSpec != null) {
+                authzSpecs.add(methodAuthzSpec);
+            }
+        }
+
+        if (!authzSpecs.isEmpty()) {
+            context.register(new JavaxSecurityAuthorizationFilter(authzSpecs), Priorities.AUTHORIZATION);
+        }
+    }
+
+}
diff --git a/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/JavaxSecurityAuthorizationFilter.java b/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/JavaxSecurityAuthorizationFilter.java
new file mode 100644
index 0000000..1519ab5
--- /dev/null
+++ b/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs/JavaxSecurityAuthorizationFilter.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.web.jaxrs;
+
+import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
+import org.apache.shiro.authz.aop.JavaxSecurityRolesAnnotationHandler;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A filter that grants or denies access to a JAX-RS resource based on the <code>javax.annotation.security</code> annotations on it.
+ *
+ * @see org.apache.shiro.authz.annotation
+ * @since 1.4
+ */
+public class JavaxSecurityAuthorizationFilter implements ContainerRequestFilter {
+
+    private final Map<AuthorizingAnnotationHandler, Annotation> authzChecks;
+
+    public JavaxSecurityAuthorizationFilter(Collection<Annotation> authzSpecs) {
+        Map<AuthorizingAnnotationHandler, Annotation> authChecks = new HashMap<AuthorizingAnnotationHandler, Annotation>(authzSpecs.size());
+        for (Annotation authSpec : authzSpecs) {
+            authChecks.put(createHandler(authSpec), authSpec);
+        }
+        this.authzChecks = Collections.unmodifiableMap(authChecks);
+    }
+
+    private static AuthorizingAnnotationHandler createHandler(Annotation annotation) {
+        Class<?> t = annotation.annotationType();
+
+        if (RolesAllowed.class.equals(t) || DenyAll.class.equals(t) || PermitAll.class.equals(t)) {
+            return new JavaxSecurityRolesAnnotationHandler();
+        }
+
+        else throw new IllegalArgumentException("Cannot create a handler for the unknown for annotation " + t);
+    }
+
+    @Override
+    public void filter(ContainerRequestContext requestContext) throws IOException {
+
+        for (Map.Entry<AuthorizingAnnotationHandler, Annotation> authzCheck : authzChecks.entrySet()) {
+            AuthorizingAnnotationHandler handler = authzCheck.getKey();
+            Annotation authzSpec = authzCheck.getValue();
+            handler.assertAuthorized(authzSpec);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/JavaxSecurityAnnotationFilterFeatureTest.groovy b/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/JavaxSecurityAnnotationFilterFeatureTest.groovy
new file mode 100644
index 0000000..d43d25e
--- /dev/null
+++ b/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/JavaxSecurityAnnotationFilterFeatureTest.groovy
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.web.jaxrs
+
+import org.easymock.Capture
+import org.junit.Test
+
+import javax.annotation.security.DenyAll
+import javax.annotation.security.PermitAll
+import javax.annotation.security.RolesAllowed
+import javax.ws.rs.Priorities
+import javax.ws.rs.container.ResourceInfo
+import javax.ws.rs.core.FeatureContext
+
+import static org.easymock.EasyMock.*
+
+/**
+ * Tests for {@link JavaxSecurityAnnotationFilterFeature}.
+ * @since 1.4.0
+ */
+class JavaxSecurityAnnotationFilterFeatureTest {
+
+    @Test
+    void simpleTest() {
+
+        def resourceInfo = mock(ResourceInfo)
+        def featureContext = mock(FeatureContext)
+        def filterCapture = new Capture<JavaxSecurityAuthorizationFilter>()
+        expect(resourceInfo.getResourceClass()).andReturn(JavaxAnnotatedStub).anyTimes()
+        expect(resourceInfo.getResourceMethod()).andReturn(JavaxAnnotatedStub.getMethod("denyMe")).anyTimes()
+        expect(featureContext.register(capture(filterCapture), eq(Priorities.AUTHORIZATION))).andReturn(featureContext)
+
+        replay resourceInfo, featureContext
+
+        new JavaxSecurityAnnotationFilterFeature().configure(resourceInfo, featureContext)
+
+        verify resourceInfo, featureContext
+    }
+}
diff --git a/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/JavaxSecurityAuthorizationFilterTest.groovy b/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/JavaxSecurityAuthorizationFilterTest.groovy
new file mode 100644
index 0000000..0d1e711
--- /dev/null
+++ b/support/jaxrs/src/test/groovy/org/apache/shiro/web/jaxrs/JavaxSecurityAuthorizationFilterTest.groovy
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.web.jaxrs
+
+import org.apache.shiro.authz.annotation.RequiresGuest
+import org.apache.shiro.subject.Subject
+import org.apache.shiro.util.ThreadContext
+import org.junit.Assert
+import org.junit.Test
+
+import javax.annotation.security.PermitAll
+import javax.annotation.security.RolesAllowed
+import javax.ws.rs.container.ContainerRequestContext
+import java.lang.annotation.Annotation
+
+import static org.easymock.EasyMock.*
+import static org.hamcrest.Matchers.*
+import static org.hamcrest.MatcherAssert.*
+
+/**
+ * Tests for {@link JavaxSecurityAuthorizationFilter}.
+ * @since 1.4.0
+ */
+class JavaxSecurityAuthorizationFilterTest {
+
+    @Test
+    void simpleTest() {
+
+        List<Annotation> annotationSpecs = new ArrayList<Annotation>();
+        annotationSpecs.add(JavaxAnnotatedStub.getAnnotation(PermitAll.class))
+        annotationSpecs.add(JavaxAnnotatedStub.getMethod("rolesAllowed").getAnnotation(RolesAllowed.class))
+
+        def requestContext = mock(ContainerRequestContext)
+        def subject = mock(Subject)
+        subject.checkRole("Role1")
+
+        try {
+            ThreadContext.bind(subject)
+
+            replay requestContext, subject
+
+            new JavaxSecurityAuthorizationFilter(annotationSpecs).filter(requestContext)
+
+            verify requestContext, subject
+        }
+        finally {
+            ThreadContext.unbindSubject()
+        }
+    }
+
+    @Test
+    void unhandledAnnotationTest() {
+        List<Annotation> annotationSpecs = new ArrayList<Annotation>();
+        annotationSpecs.add(JavaxAnnotatedStub.getMethod("requiresGuest").getAnnotation(RequiresGuest.class))
+
+        def requestContext = mock(ContainerRequestContext)
+        def subject = mock(Subject)
+
+        try {
+            ThreadContext.bind(subject)
+
+            replay requestContext, subject
+
+            new JavaxSecurityAuthorizationFilter(annotationSpecs).filter(requestContext)
+            Assert.fail("expected IllegalArgumentException")
+        }
+        catch(IllegalArgumentException e){
+            assertThat e.getMessage(), stringContainsInOrder(RequiresGuest.getName())
+        }
+        finally {
+
+            verify requestContext, subject
+            ThreadContext.unbindSubject()
+        }
+    }
+}
diff --git a/support/jaxrs/src/test/java/org/apache.shiro.web.jaxrs/JavaxAnnotatedStub.java b/support/jaxrs/src/test/java/org/apache.shiro.web.jaxrs/JavaxAnnotatedStub.java
new file mode 100644
index 0000000..eec850b
--- /dev/null
+++ b/support/jaxrs/src/test/java/org/apache.shiro.web.jaxrs/JavaxAnnotatedStub.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.web.jaxrs;
+
+import org.apache.shiro.authz.annotation.RequiresGuest;
+
+import javax.annotation.security.DenyAll;
+import javax.annotation.security.PermitAll;
+import javax.annotation.security.RolesAllowed;
+
+@PermitAll
+public class JavaxAnnotatedStub {
+
+    @DenyAll
+    public void denyMe() {}
+
+    @PermitAll
+    public void allowMe() {}
+
+    @RolesAllowed("Role1")
+    public void rolesAllowed() {}
+
+    @RequiresGuest
+    public void requiresGuest() {}
+}