├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── org
│ └── secnod
│ └── shiro
│ ├── jaxrs
│ ├── Auth.java
│ └── ShiroExceptionMapper.java
│ └── jersey
│ ├── AuthorizationFilter.java
│ ├── AuthorizationFilterFeature.java
│ ├── SubjectFactory.java
│ └── TypeFactory.java
└── test
├── java
└── org
│ └── secnod
│ ├── example
│ └── webapp
│ │ ├── ExampleApplication.java
│ │ ├── HelloWorldResource.java
│ │ ├── User.java
│ │ └── UserFactory.java
│ └── shiro
│ └── test
│ └── integration
│ ├── AnnotationAuthTest.java
│ ├── IntegrationTestSuite.java
│ └── webapp
│ ├── FieldInjectionResource.java
│ ├── GuestOnlyResource.java
│ ├── InjectionResource.java
│ ├── IntegrationTestApplication.java
│ ├── JettyServer.java
│ ├── OptionalBasicHttpAuthenticationFilter.java
│ ├── PermissionProtectedResource.java
│ ├── PublicResource.java
│ ├── RequireAuthenticatedUserResource.java
│ ├── RequireUserResource.java
│ ├── RoleProtectedResource.java
│ ├── SessionResource.java
│ ├── SubjectAuthResource.java
│ └── UserAuthResource.java
└── resources
├── logging.properties
├── org
└── secnod
│ ├── example
│ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── shiro
│ └── test
│ └── integration
│ └── webapp
│ ├── 404.html
│ ├── WEB-INF
│ ├── shiro.ini
│ └── web.xml
│ └── index.html
└── shiro.ini
/.gitignore:
--------------------------------------------------------------------------------
1 | .project
2 | target
3 | .settings
4 | .classpath
5 | infinitest.filters
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Apache Shiro support for the Jersey JAX-RS implementation.
2 |
3 | # News
4 |
5 | [Shiro 1.4](http://shiro.apache.org/news.html#1.4.0-RC2-released) has been
6 | released and includes a new official JAX-RS module `shiro-jaxrs` based on `shiro-jersey`.
7 |
8 | The official `shiro-jaxrs` module offers feature parity with the generic JAX-RS
9 | functionality of `shiro-jersey`. The main difference is that `shiro-jaxrs` does
10 | not support the Jersey specific injections of `shiro-jersey`.
11 |
12 | See:
13 | * [Apache Shiro JAX-RS Support](https://shiro.apache.org/jaxrs.html)
14 | * [the source code](https://github.com/apache/shiro/tree/shiro-root-1.4.0/support/jaxrs/src/main/java/org/apache/shiro/web/jaxrs)
15 | * [SHIRO-392](https://issues.apache.org/jira/browse/SHIRO-392)
16 | * [Example usage of the Shiro JAX-RS module](https://stormpath.com/blog/protecting-jax-rs-resources-rbac-apache-shIro) ([Wayback Machine](http://web.archive.org/web/20230129054611/https://stormpath.com/blog/protecting-jax-rs-resources-rbac-apache-shIro))
17 |
18 | # Adding the shiro-jersey dependency
19 |
20 | Add the following dependencies to `pom.xml` in an existing project already using Jersey:
21 |
22 | ```xml
23 |
24 | org.secnod.shiro
25 | shiro-jersey
26 | 0.2.0
27 |
28 | ```
29 |
30 | Version compatibility:
31 |
32 | |Jersey |Shiro Jersey| Compatibility |
33 | |--------|------------|------|
34 | |3.x |0.4.0-SNAPSHOT| Jakarta EE |
35 | |2.26- |0.3.0 | Java EE |
36 | |2.0-2.25|0.2.0 | Java EE |
37 | |1.x |0.1.1 | Java EE |
38 |
39 | If you are upgrading from:
40 |
41 | * Jersey 2.0-2.25, see the [upgrade instructions](#mig-0.2.x).
42 | * Jersey 1.x, see the [upgrade instructions](#mig-0.1.x).
43 |
44 | # Configuring Shiro in a Jersey web application
45 |
46 | An example web application is provided complete with [source code](src/test/java/org/secnod/example/webapp)
47 | and [web content](src/test/resources/org/secnod/example/webapp).
48 |
49 | The rest of this section describes how Shiro has been added to the example application.
50 |
51 | Add the Shiro servlet filter in `web.xml`:
52 |
53 | ```xml
54 |
55 |
56 | shiroConfigLocations
57 | classpath:shiro.ini
58 |
59 |
60 |
61 | org.apache.shiro.web.env.EnvironmentLoaderListener
62 |
63 |
64 |
65 | ShiroFilter
66 | org.apache.shiro.web.servlet.ShiroFilter
67 |
68 |
69 |
70 | ShiroFilter
71 | /*
72 | REQUEST
73 | FORWARD
74 | INCLUDE
75 | ERROR
76 |
77 | ```
78 |
79 | Then register the following components in the JAX-RS application:
80 |
81 | ```java
82 | public class ApiApplication extends ResourceConfig {
83 | public ApiApplication() {
84 | register(org.apache.shiro.web.jaxrs.ShiroFeature.class);
85 | register(new SubjectFactory());
86 | }
87 | }
88 | ```
89 |
90 | ## Configuring Shiro
91 | Finally configure `shiro.ini` in the default package on the classpath:
92 |
93 | ```ini
94 | [main]
95 |
96 | [users]
97 | exampleuser = examplepassword, examplerole
98 |
99 | [roles]
100 | examplerole = something:readpermission
101 |
102 | [urls]
103 | /** = noSessionCreation, authcBasic
104 | ```
105 |
106 | Real applications should of course not store users and passwords in the INI-file. See the
107 | [Shiro configuration documentation](http://shiro.apache.org/configuration.html).
108 |
109 | # Using Shiro from JAX-RS
110 |
111 | This section describes the different alternatives for how Shiro can be used from JAX-RS.
112 |
113 | ## Declarative authorization with annotations
114 |
115 | JAX-RS resource classes and methods can be annotated with the
116 | [standard Shiro annotations](http://shiro.apache.org/static/1.4.2/apidocs/org/apache/shiro/authz/annotation/package-summary.html).
117 |
118 | The authorization requirements can for example be declared with `@RequiresPermissions` on JAX-RS resource classes /
119 | methods:
120 |
121 | ```java
122 | @Path("/auth")
123 | @Produces(MediaType.TEXT_PLAIN)
124 | @RequiresPermissions("protected:read")
125 | public class AuthResource {
126 |
127 | @GET
128 | public String get() {
129 | return "OK";
130 | }
131 |
132 | @PUT
133 | @RequiresPermissions("protected:write")
134 | public String set(String value) {
135 | return value;
136 | }
137 | }
138 | ```
139 |
140 | The example above can be summarized as:
141 |
142 | * HTTP GET access requires the user to have the permission `protected:read`
143 | * HTTP PUT access requires the user to have both permissions `protected:read` and `protected:write`
144 |
145 | ## Programmatic authorization
146 |
147 | Programmatic authorization is done by injecting the Shiro
148 | [Subject](http://shiro.apache.org/static/1.4.2/apidocs/org/apache/shiro/subject/Subject.html) as a method parameter:
149 |
150 | ```java
151 | @Path("/auth")
152 | @Produces(MediaType.TEXT_PLAIN)
153 | public class AuthResource {
154 |
155 | @GET
156 | public String get(@Auth Subject subject) {
157 | subject.checkPermission("protected:read");
158 | return "OK";
159 | }
160 | }
161 | ```
162 |
163 | Injecting the Subject is just a convenience over calling
164 | [SecurityUtils.getSubject()](http://shiro.apache.org/static/1.4.2/apidocs/org/apache/shiro/SecurityUtils.html#getSubject()).
165 |
166 | Declarative and programmatic authorization are often combined when some permissions are static and some are dynamic:
167 |
168 | ```java
169 | @Path("/auth")
170 | @Produces(MediaType.TEXT_PLAIN)
171 | public class AuthResource {
172 |
173 | @GET
174 | @RequiresPermissions("static-permission")
175 | public String get(@Auth Subject subject) {
176 | subject.checkPermission(dynamicPermission());
177 | return "OK";
178 | }
179 | }
180 | ```
181 |
182 | ## Optionally using an application specific user class
183 |
184 | Instead of using the Shiro `Subject` class directly one can use an application specific user class for programmatic
185 | authorization:
186 |
187 | ```java
188 | @Path("/auth")
189 | @Produces(MediaType.TEXT_PLAIN)
190 | public class AuthResource {
191 |
192 | @GET
193 | public String get(@Auth User user) {
194 | user.checkBusinessRulePermission();
195 | return "OK";
196 | }
197 | }
198 | ```
199 |
200 | A custom `User` class is a convenient way of implementing application
201 | specific authorization based on business rules on the user's data.
202 |
203 | More authorization as rules means less authorization as permissions and hence fewer permissions to maintain.
204 |
205 | See:
206 | * The example [User](src/test/java/org/secnod/example/webapp/User.java) class.
207 | * The example [UserFactory](src/test/java/org/secnod/example/webapp/UserFactory.java)
208 | which must be registered as a JAX-RS component.
209 | * The class [TypeFactory](src/main/java/org/secnod/shiro/jersey/TypeFactory.java)
210 | can be extended for injection of custom classes with the `@Auth` annotation.
211 |
212 | ## Migrating from 0.2.x
213 |
214 | `AuthInjectionBinder` has been deleted. Remove its registration in
215 | `ResourceConfig.register()`.
216 |
217 | ## Migrating from 0.1.x
218 |
219 | These instructions assume that the JAX-RS application is a subclass of
220 | `org.glassfish.jersey.server.ResourceConfig`.
221 |
222 | Note that JAX-RS component registration is done by `ResourceConfig.register()`
223 | instead of `javax.ws.rs.core.Application.getSingletons()`.
224 |
225 | * `AuthorizationFilterFeature` replaces `ShiroResourceFilterFactory`
226 |
227 | Remove the configuration of `ShiroResourceFilterFactory` from `web.xml` and
228 | register `AuthorizationFilterFeature` as a JAX-RS component.
229 |
230 | * `SubjectFactory` replaces `SubjectInjectableProvider`
231 | * `TypeFactory` replaces `AuthInjectableProvider`
232 |
233 | # Development
234 | ## Running the integration tests
235 |
236 | The integration tests for this project can be run as follows:
237 |
238 | mvn -Pintegration-tests test -Dshiro.jersey=false
239 |
240 | The default is to run the tests using the Apache Shiro [JAX-RS support](https://repo1.maven.org/maven2/org/apache/shiro/shiro-jaxrs/1.4.2/).
241 | Alternatively, the old `shiro-jersey` features can be enabled instead by setting `shiro.jersey` to `true`.
242 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.secnod.shiro
6 | shiro-jersey
7 | 0.4.0-SNAPSHOT
8 |
9 | ${project.groupId}:${project.artifactId}
10 | Apache Shiro support for the Jersey JAX-RS implementation.
11 | https://github.com/silb/shiro-jersey
12 |
13 |
14 |
15 | Apache License 2.0
16 | http://www.apache.org/licenses/LICENSE-2.0.html
17 | repo
18 |
19 |
20 |
21 |
22 |
23 | Stig Inge Lea Bjørnsen
24 | stiginge@pvv.org
25 | http://blog.silb.no
26 |
27 |
28 |
29 |
30 | scm:git:https://github.com/silb/shiro-jersey.git
31 | scm:git:git@github.com:silb/shiro-jersey.git
32 | HEAD
33 | https://github.com/silb/shiro-jersey
34 |
35 |
36 |
37 | https://github.com/silb/shiro-jersey/issues
38 |
39 |
40 |
41 |
42 | ossrh
43 | https://oss.sonatype.org/content/repositories/snapshots
44 |
45 |
46 | ossrh
47 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
48 |
49 |
50 |
51 |
52 |
53 | org.apache.shiro
54 | shiro-core
55 | ${shiro.version}
56 |
57 |
58 | org.apache.shiro
59 | shiro-jaxrs
60 | ${shiro.version}
61 |
62 |
63 | org.apache.shiro
64 | shiro-web
65 | ${shiro.version}
66 | runtime
67 |
68 |
69 | javax.ws.rs
70 | javax.ws.rs-api
71 | 2.1
72 |
73 |
74 | org.glassfish.jersey.core
75 | jersey-server
76 | ${jersey.version}
77 |
78 |
79 | org.glassfish.jersey.containers
80 | jersey-container-servlet
81 | ${jersey.version}
82 |
83 |
84 | org.glassfish.jersey.inject
85 | jersey-hk2
86 | ${jersey.version}
87 |
88 |
89 | junit
90 | junit
91 | 4.12
92 | test
93 |
94 |
95 | org.slf4j
96 | slf4j-api
97 | ${slf4j.version}
98 | test
99 |
100 |
101 | org.slf4j
102 | slf4j-jdk14
103 | ${slf4j.version}
104 | test
105 |
106 |
107 | org.glassfish.jersey.core
108 | jersey-client
109 | ${jersey.version}
110 | test
111 |
112 |
113 | org.glassfish.jersey.connectors
114 | jersey-apache-connector
115 | ${jersey.version}
116 | test
117 |
118 |
119 | org.eclipse.jetty
120 | jetty-webapp
121 | ${jetty.version}
122 | test
123 |
124 |
125 | com.fasterxml.jackson.jaxrs
126 | jackson-jaxrs-json-provider
127 | 2.6.3
128 | test
129 |
130 |
131 |
132 |
133 | UTF-8
134 | UTF-8
135 | 1.7.12
136 | 2.26
137 | 9.2.13.v20150730
138 | 1.4.2
139 | 1.8
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | org.apache.maven.plugins
148 | maven-javadoc-plugin
149 | 2.8
150 |
151 |
152 |
153 |
154 |
155 |
156 | org.apache.maven.plugins
157 | maven-compiler-plugin
158 | 2.3.2
159 |
160 | ${jdk.version}
161 | ${jdk.version}
162 | true
163 | -Xlint
164 | true
165 |
166 |
167 |
168 |
169 | org.apache.maven.plugins
170 | maven-enforcer-plugin
171 | 1.3.1
172 |
173 |
174 | enforce-versions
175 |
176 | enforce
177 |
178 |
179 |
180 |
181 | [${jdk.version},1.9]
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 | org.codehaus.mojo
191 | exec-maven-plugin
192 | 1.2.1
193 |
194 |
195 |
196 | exec
197 |
198 |
199 |
200 |
201 | java
202 | test
203 |
204 | -Djava.util.logging.config.file=src/test/resources/logging.properties
205 | -classpath
206 |
207 | org.secnod.shiro.test.integration.webapp.JettyServer
208 |
209 |
210 |
211 |
212 |
213 | org.apache.maven.plugins
214 | maven-surefire-plugin
215 | 2.16
216 |
217 |
218 | **/test/integration/**/*Test.java
219 |
220 |
221 |
222 |
223 |
224 | org.apache.maven.plugins
225 | maven-eclipse-plugin
226 | 2.8
227 |
228 | true
229 | true
230 |
231 | org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-${jdk.version}
232 |
233 |
234 |
235 |
236 |
237 | org.apache.maven.plugins
238 | maven-source-plugin
239 | 2.1.2
240 |
241 |
242 | attach-sources
243 |
244 | jar-no-fork
245 | test-jar-no-fork
246 |
247 |
248 |
249 |
250 |
251 | true
252 |
253 |
254 |
255 |
256 | org.apache.maven.plugins
257 | maven-jar-plugin
258 | 2.3.1
259 |
260 |
261 |
262 | test-jar
263 |
264 |
265 |
266 |
267 |
268 |
269 | org.apache.maven.plugins
270 | maven-release-plugin
271 | 2.4.2
272 |
273 |
274 | org.apache.maven.scm
275 | maven-scm-provider-gitexe
276 | 1.8.1
277 |
278 |
279 |
280 | v@{project.version}
281 | false
282 | release
283 |
284 |
285 |
286 |
287 | org.apache.maven.plugins
288 | maven-javadoc-plugin
289 |
290 |
291 | https://eclipse-ee4j.github.io/jersey.github.io/apidocs/${jersey.version}/jersey/
292 | http://shiro.apache.org/static/${shiro.version}/apidocs/
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 | integration-tests
304 |
305 |
306 |
307 | org.apache.maven.plugins
308 | maven-surefire-plugin
309 | 2.16
310 |
311 |
312 | **/test/integration/IntegrationTestSuite.java
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 | release
322 |
323 |
324 |
325 |
326 | org.apache.maven.plugins
327 | maven-javadoc-plugin
328 |
329 |
330 | attach-javadocs
331 |
332 | jar
333 |
334 |
335 |
336 |
337 |
338 |
339 | org.apache.maven.plugins
340 | maven-gpg-plugin
341 | 1.5
342 |
343 |
344 | sign-artifacts
345 | verify
346 |
347 | sign
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 | infinitest
360 |
361 |
362 |
363 | org.apache.maven.plugins
364 | maven-eclipse-plugin
365 | 2.8
366 |
367 |
368 |
369 | infinitest.filters
370 |
371 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
--------------------------------------------------------------------------------
/src/main/java/org/secnod/shiro/jaxrs/Auth.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.jaxrs;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface Auth {
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/org/secnod/shiro/jaxrs/ShiroExceptionMapper.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.jaxrs;
2 |
3 | import javax.ws.rs.core.Response;
4 | import javax.ws.rs.core.Response.Status;
5 | import javax.ws.rs.ext.ExceptionMapper;
6 |
7 | import org.apache.shiro.authz.AuthorizationException;
8 | import org.apache.shiro.authz.UnauthorizedException;
9 |
10 | /**
11 | * @deprecated replaced by the native Shiro exception mapper {@link org.apache.shiro.web.jaxrs.ExceptionMapper}
12 | *
13 | * @see org.apache.shiro.web.jaxrs.ShiroFeature
14 | */
15 | @Deprecated
16 | public class ShiroExceptionMapper implements ExceptionMapper {
17 |
18 | @Override
19 | public Response toResponse(AuthorizationException exception) {
20 |
21 | Status status;
22 |
23 | if (exception instanceof UnauthorizedException) {
24 | status = Status.FORBIDDEN;
25 | } else {
26 | status = Status.UNAUTHORIZED;
27 | }
28 |
29 | return Response.status(status).build();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/org/secnod/shiro/jersey/AuthorizationFilter.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.jersey;
2 |
3 | import java.io.IOException;
4 | import java.lang.annotation.Annotation;
5 | import java.util.Collection;
6 | import java.util.Collections;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import javax.ws.rs.container.ContainerRequestContext;
11 | import javax.ws.rs.container.ContainerRequestFilter;
12 |
13 | import org.apache.shiro.authz.annotation.RequiresAuthentication;
14 | import org.apache.shiro.authz.annotation.RequiresGuest;
15 | import org.apache.shiro.authz.annotation.RequiresPermissions;
16 | import org.apache.shiro.authz.annotation.RequiresRoles;
17 | import org.apache.shiro.authz.annotation.RequiresUser;
18 | import org.apache.shiro.authz.aop.AuthenticatedAnnotationHandler;
19 | import org.apache.shiro.authz.aop.AuthorizingAnnotationHandler;
20 | import org.apache.shiro.authz.aop.GuestAnnotationHandler;
21 | import org.apache.shiro.authz.aop.PermissionAnnotationHandler;
22 | import org.apache.shiro.authz.aop.RoleAnnotationHandler;
23 | import org.apache.shiro.authz.aop.UserAnnotationHandler;
24 |
25 | /**
26 | * A filter that grants or denies access to a JAX-RS resource based on the Shiro annotations on it.
27 | *
28 | * @see org.apache.shiro.authz.annotation
29 | *
30 | * @deprecated replaced by the native Shiro filter {@link org.apache.shiro.web.jaxrs.AnnotationAuthorizationFilter}
31 | */
32 | @Deprecated
33 | public class AuthorizationFilter implements ContainerRequestFilter {
34 |
35 | private final Map authzChecks;
36 |
37 | public AuthorizationFilter(Collection authzSpecs) {
38 | Map authChecks = new HashMap<>(authzSpecs.size());
39 | for (Annotation authSpec : authzSpecs) {
40 | authChecks.put(createHandler(authSpec), authSpec);
41 | }
42 | this.authzChecks = Collections.unmodifiableMap(authChecks);
43 | }
44 |
45 | private static AuthorizingAnnotationHandler createHandler(Annotation annotation) {
46 | Class> t = annotation.annotationType();
47 | if (RequiresPermissions.class.equals(t)) return new PermissionAnnotationHandler();
48 | else if (RequiresRoles.class.equals(t)) return new RoleAnnotationHandler();
49 | else if (RequiresUser.class.equals(t)) return new UserAnnotationHandler();
50 | else if (RequiresGuest.class.equals(t)) return new GuestAnnotationHandler();
51 | else if (RequiresAuthentication.class.equals(t)) return new AuthenticatedAnnotationHandler();
52 | else throw new IllegalArgumentException("Cannot create a handler for the unknown for annotation " + t);
53 | }
54 |
55 | @Override
56 | public void filter(ContainerRequestContext requestContext) throws IOException {
57 | for (Map.Entry authzCheck : authzChecks.entrySet()) {
58 | AuthorizingAnnotationHandler handler = authzCheck.getKey();
59 | Annotation authzSpec = authzCheck.getValue();
60 | handler.assertAuthorized(authzSpec);
61 | }
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/org/secnod/shiro/jersey/AuthorizationFilterFeature.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.jersey;
2 |
3 | import java.lang.annotation.Annotation;
4 | import java.util.ArrayList;
5 | import java.util.Arrays;
6 | import java.util.Collections;
7 | import java.util.List;
8 |
9 | import javax.ws.rs.Priorities;
10 | import javax.ws.rs.container.DynamicFeature;
11 | import javax.ws.rs.container.ResourceInfo;
12 | import javax.ws.rs.core.FeatureContext;
13 |
14 | import org.apache.shiro.authz.annotation.RequiresAuthentication;
15 | import org.apache.shiro.authz.annotation.RequiresGuest;
16 | import org.apache.shiro.authz.annotation.RequiresPermissions;
17 | import org.apache.shiro.authz.annotation.RequiresRoles;
18 | import org.apache.shiro.authz.annotation.RequiresUser;
19 |
20 | /**
21 | * Wraps {@link AuthorizationFilter filters} around JAX-RS resources that are
22 | * annotated with Shiro annotations.
23 | *
24 | * @see org.apache.shiro.web.jaxrs.ShiroFeature
25 | *
26 | * @deprecated replaced by the native Shiro filter feature
27 | * {@link org.apache.shiro.web.jaxrs.ShiroAnnotationFilterFeature}
28 | */
29 | @Deprecated
30 | public class AuthorizationFilterFeature implements DynamicFeature {
31 |
32 | private static List> shiroAnnotations = Collections.unmodifiableList(Arrays.asList(
33 | RequiresPermissions.class,
34 | RequiresRoles.class,
35 | RequiresAuthentication.class,
36 | RequiresUser.class,
37 | RequiresGuest.class));
38 |
39 | @Override
40 | public void configure(ResourceInfo resourceInfo, FeatureContext context) {
41 |
42 | List authzSpecs = new ArrayList<>();
43 |
44 | for (Class extends Annotation> annotationClass : shiroAnnotations) {
45 | // XXX What is the performance of getAnnotation vs getAnnotations?
46 | Annotation classAuthzSpec = resourceInfo.getResourceClass().getAnnotation(annotationClass);
47 | Annotation methodAuthzSpec = resourceInfo.getResourceMethod().getAnnotation(annotationClass);
48 |
49 | if (classAuthzSpec != null) authzSpecs.add(classAuthzSpec);
50 | if (methodAuthzSpec != null) authzSpecs.add(methodAuthzSpec);
51 | }
52 |
53 | if (!authzSpecs.isEmpty()) {
54 | context.register(new AuthorizationFilter(authzSpecs), Priorities.AUTHORIZATION);
55 | }
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/org/secnod/shiro/jersey/SubjectFactory.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.jersey;
2 |
3 | import org.apache.shiro.SecurityUtils;
4 | import org.apache.shiro.subject.Subject;
5 | import org.glassfish.hk2.api.PerLookup;
6 |
7 | /**
8 | * Creates {@link Subject subjects} to be used as injected values.
9 | */
10 | public class SubjectFactory extends TypeFactory {
11 |
12 | public SubjectFactory() {
13 | super(Subject.class);
14 | }
15 |
16 | @PerLookup
17 | @Override
18 | public Subject provide() {
19 | return SecurityUtils.getSubject();
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/org/secnod/shiro/jersey/TypeFactory.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.jersey;
2 |
3 | import java.util.function.Function;
4 |
5 | import org.glassfish.jersey.server.ContainerRequest;
6 | import org.glassfish.jersey.server.model.Parameter;
7 | import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
8 | import org.secnod.shiro.jaxrs.Auth;
9 |
10 | /**
11 | * Base class for factories that can instantiate object of a given type.
12 | *
13 | * @param the type of the objects that the factory creates
14 | */
15 | public abstract class TypeFactory implements ValueParamProvider, Function {
16 | public final Class type;
17 |
18 | public TypeFactory(Class type) {
19 | this.type = type;
20 | }
21 |
22 | /*
23 | * This class used to implement {@code org.glassfish.hk2.api.Factory} providing this method.
24 | * This method provides backwards compatibility for existing subclasses.
25 | */
26 | public abstract T provide();
27 |
28 | // org.glassfish.jersey.server.spi.internal.ValueParamProvider
29 |
30 | @Override
31 | public Function getValueProvider(Parameter parameter) {
32 | if (type.equals(parameter.getRawType()) && parameter.isAnnotationPresent(Auth.class)) {
33 | return this;
34 | }
35 | return null;
36 | }
37 |
38 | @Override
39 | public PriorityType getPriority() {
40 | return Priority.NORMAL;
41 | }
42 |
43 | // java.util.Function
44 |
45 | @Override
46 | public T apply(ContainerRequest request) {
47 | return provide();
48 | }
49 | }
--------------------------------------------------------------------------------
/src/test/java/org/secnod/example/webapp/ExampleApplication.java:
--------------------------------------------------------------------------------
1 | package org.secnod.example.webapp;
2 |
3 | import org.apache.shiro.web.jaxrs.ShiroFeature;
4 | import org.glassfish.jersey.server.ResourceConfig;
5 | import org.secnod.shiro.jersey.SubjectFactory;
6 |
7 | /**
8 | * An example JAX-RS application using Apache Shiro.
9 | */
10 | public class ExampleApplication extends ResourceConfig {
11 |
12 | public ExampleApplication() {
13 | super();
14 | register(ShiroFeature.class);
15 | register(new SubjectFactory());
16 | register(new HelloWorldResource());
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/test/java/org/secnod/example/webapp/HelloWorldResource.java:
--------------------------------------------------------------------------------
1 | package org.secnod.example.webapp;
2 |
3 | import javax.ws.rs.GET;
4 | import javax.ws.rs.Path;
5 | import javax.ws.rs.Produces;
6 | import javax.ws.rs.core.MediaType;
7 |
8 | @Path("/hello-world")
9 | @Produces(MediaType.TEXT_PLAIN)
10 | public class HelloWorldResource {
11 |
12 | @GET
13 | public String get() {
14 | return "Hello, world!";
15 | }
16 | }
--------------------------------------------------------------------------------
/src/test/java/org/secnod/example/webapp/User.java:
--------------------------------------------------------------------------------
1 | package org.secnod.example.webapp;
2 |
3 | import org.apache.shiro.authz.AuthorizationException;
4 | import org.apache.shiro.authz.UnauthorizedException;
5 | import org.apache.shiro.subject.Subject;
6 |
7 | public class User {
8 |
9 | private final Subject subject;
10 |
11 | public User(Subject subject) {
12 | super();
13 | if (subject == null)
14 | throw new NullPointerException();
15 | this.subject = subject;
16 | }
17 |
18 | public T unwrap(Class type) {
19 | if (Subject.class.equals(type)) return type.cast(subject);
20 |
21 | throw new IllegalArgumentException("User " + this + " cannot be unwrapped to " + type);
22 | }
23 |
24 | @Override
25 | public String toString() {
26 | String username = subject.getPrincipal() != null ? subject.getPrincipal().toString() : null;
27 | return username != null ? username : "anonymous";
28 | }
29 |
30 | public void checkPermissionBySomeRule() throws AuthorizationException {
31 | // Apply domain specific authorization rules based on data found in the user's data, subject principals etc.
32 | if (Math.random() < 0.5) throw new UnauthorizedException();
33 | }
34 |
35 | // Convenience delegate methods to the Subject
36 |
37 | public void checkPermission(String permission) throws AuthorizationException {
38 | subject.checkPermission(permission);
39 | }
40 |
41 | public void checkRole(String roleIdentifier) throws AuthorizationException {
42 | this.subject.checkRole(roleIdentifier);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/org/secnod/example/webapp/UserFactory.java:
--------------------------------------------------------------------------------
1 | package org.secnod.example.webapp;
2 |
3 | import org.apache.shiro.SecurityUtils;
4 | import org.secnod.shiro.jersey.TypeFactory;
5 |
6 |
7 | public class UserFactory extends TypeFactory {
8 |
9 | public UserFactory() {
10 | super(User.class);
11 | }
12 |
13 | @Override
14 | public User provide() {
15 | return new User(SecurityUtils.getSubject());
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/test/java/org/secnod/shiro/test/integration/AnnotationAuthTest.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.test.integration;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.util.HashMap;
6 | import java.util.Map;
7 | import java.util.Map.Entry;
8 |
9 | import javax.ws.rs.client.Client;
10 | import javax.ws.rs.client.ClientBuilder;
11 | import javax.ws.rs.client.Entity;
12 | import javax.ws.rs.client.WebTarget;
13 | import javax.ws.rs.core.Form;
14 | import javax.ws.rs.core.Response;
15 |
16 | import org.apache.http.auth.AuthScope;
17 | import org.apache.http.auth.UsernamePasswordCredentials;
18 | import org.apache.http.client.CredentialsProvider;
19 | import org.apache.http.impl.client.BasicCredentialsProvider;
20 | import org.glassfish.jersey.apache.connector.ApacheClientProperties;
21 | import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
22 | import org.glassfish.jersey.client.ClientConfig;
23 | import org.junit.AfterClass;
24 | import org.junit.Before;
25 | import org.junit.BeforeClass;
26 | import org.junit.Test;
27 |
28 | public class AnnotationAuthTest {
29 |
30 | private static final String USER1_PASSWORD = "user1pw";
31 | private static final String USER1 = "user1";
32 |
33 | private String baseUrl;
34 |
35 | private static Client client;
36 |
37 | @Before
38 | public void setup() {
39 | Integer port = Integer.getInteger("org.secnod.shiro.test.port");
40 | baseUrl = "http://localhost:" + port + "/api/";
41 | logout();
42 | }
43 |
44 | @BeforeClass
45 | public static void setupClass() throws Exception {
46 | client = ClientBuilder.newClient();
47 | }
48 |
49 | @AfterClass
50 | public static void tearDownClass() {
51 | client.close();
52 | }
53 |
54 | private WebTarget webTarget(String relativeUrl) {
55 | return client.target(baseUrl + relativeUrl);
56 | }
57 |
58 | private Client newClient(Map props) {
59 | ClientConfig config = new ClientConfig().connectorProvider(new ApacheConnectorProvider());
60 | if (props != null)
61 | for (Entry entry : props.entrySet())
62 | config.property(entry.getKey(), entry.getValue());
63 | return ClientBuilder.newClient(config);
64 | }
65 |
66 | private Client newClient() {
67 | return newClient(null);
68 | }
69 |
70 | private void auth(String username, String password) {
71 | client.close();
72 | client = null;
73 | CredentialsProvider credentials = new BasicCredentialsProvider();
74 | credentials.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
75 | Map props = new HashMap<>();
76 | props.put(ApacheClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION, true);
77 | props.put(ApacheClientProperties.CREDENTIALS_PROVIDER, credentials);
78 | client = newClient(props);
79 | }
80 |
81 | private void loginUser() {
82 | auth(USER1, USER1_PASSWORD);
83 | }
84 |
85 | private void loginSuperUser() {
86 | auth("user2", "user2pw");
87 | }
88 |
89 | private void logout() {
90 | client.close();
91 | client = null;
92 | client = newClient();
93 | }
94 |
95 | private void assertGetStatus(int expectedStatus, WebTarget t) {
96 | assertStatus(expectedStatus, "GET", t, null);
97 | }
98 |
99 | private void assertPutStatus(int expectedStatus, WebTarget t) {
100 | assertStatus(expectedStatus, "PUT", t, "dummy entity");
101 | }
102 |
103 | private void assertStatus(int expectedStatus, String method, WebTarget t, String entity) {
104 | Response response = entity != null ? t.request().method(method, Entity.text(entity))
105 | : t.request().method(method);
106 | int status = response.getStatus();
107 | response.close();
108 | assertEquals("Unexpected HTTP status for " + method + " " + t, expectedStatus, status);
109 | }
110 |
111 | @Test
112 | public void publicAccess() {
113 | assertGetStatus(200, webTarget("public"));
114 | loginUser();
115 | assertGetStatus(200, webTarget("public"));
116 | }
117 |
118 | private void protectedReadWrite(String resourcePath) {
119 | assertGetStatus(401, webTarget(resourcePath));
120 | loginUser();
121 | assertGetStatus(200, webTarget(resourcePath));
122 | assertPutStatus(403, webTarget(resourcePath));
123 | loginSuperUser();
124 | assertGetStatus(200, webTarget(resourcePath));
125 | assertPutStatus(200, webTarget(resourcePath));
126 | }
127 |
128 | @Test
129 | public void protectedByPermission() {
130 | protectedReadWrite("protected/permission");
131 | }
132 |
133 | @Test
134 | public void protectedByRole() {
135 | protectedReadWrite("protected/role");
136 | }
137 |
138 | @Test
139 | public void protectedOnlyUsers() {
140 | assertGetStatus(401, webTarget("protected/user"));
141 | loginUser();
142 | assertGetStatus(200, webTarget("protected/user"));
143 | }
144 |
145 | @Test
146 | public void guestOnly() {
147 | assertGetStatus(200, webTarget("guestonly"));
148 | loginUser();
149 | assertGetStatus(401, webTarget("guestonly"));
150 | }
151 |
152 | void rememberMeUserSession() {
153 | WebTarget sessionResource = webTarget("session");
154 | Form form = new Form()
155 | .param("username", USER1)
156 | .param("password", USER1_PASSWORD)
157 | .param("rememberMe", "true");
158 | Response response = sessionResource.request().post(Entity.form(form));
159 | int loginStatus = response.getStatus();
160 | response.close();
161 | assertEquals(200, loginStatus);
162 | }
163 |
164 | void invalidateSession() {
165 | WebTarget sessionResource = webTarget("session");
166 | Response response = sessionResource.request().delete();
167 | int logoutStatus = response.getStatus();
168 | response.close();
169 | assertEquals(200, logoutStatus);
170 | }
171 |
172 | @Test
173 | public void noRememberMe() {
174 | assertGetStatus(401, webTarget("protected/noRememberMe"));
175 | loginUser();
176 | assertGetStatus(200, webTarget("protected/noRememberMe"));
177 | logout();
178 | rememberMeUserSession();
179 | assertGetStatus(200, webTarget("protected/noRememberMe"));
180 | invalidateSession();
181 | assertGetStatus(401, webTarget("protected/noRememberMe"));
182 | assertGetStatus(200, webTarget("protected/permission"));
183 | }
184 |
185 | @Test
186 | public void userAndSubjectInjection() {
187 | assertGetStatus(200, webTarget("inject/usersubject"));
188 | loginUser();
189 | assertGetStatus(200, webTarget("inject/usersubject"));
190 | }
191 |
192 | @Test
193 | public void fieldInjectionFails() {
194 | assertGetStatus(500, webTarget("inject/field"));
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/src/test/java/org/secnod/shiro/test/integration/IntegrationTestSuite.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.test.integration;
2 |
3 | import org.eclipse.jetty.server.Server;
4 | import org.junit.ClassRule;
5 | import org.junit.rules.ExternalResource;
6 | import org.junit.runner.RunWith;
7 | import org.junit.runners.Suite;
8 | import org.junit.runners.Suite.SuiteClasses;
9 | import org.secnod.shiro.test.integration.webapp.JettyServer;
10 |
11 | @RunWith(Suite.class)
12 | @SuiteClasses({AnnotationAuthTest.class})
13 | public class IntegrationTestSuite {
14 |
15 | @ClassRule
16 | public static ExternalResource resource = new ExternalResource() {
17 | Server server;
18 | @Override
19 | protected void before() throws Throwable {
20 | int port = JettyServer.allocatePort();
21 | System.setProperty("org.secnod.shiro.test.port", Integer.toString(port));
22 | server = JettyServer.start(port);
23 | };
24 |
25 | @Override
26 | protected void after() {
27 | try {
28 | server.stop();
29 | } catch (Exception e) {
30 | throw new RuntimeException(e);
31 | }
32 | };
33 | };
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/test/java/org/secnod/shiro/test/integration/webapp/FieldInjectionResource.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.test.integration.webapp;
2 |
3 | import javax.ws.rs.GET;
4 | import javax.ws.rs.Path;
5 | import javax.ws.rs.Produces;
6 | import javax.ws.rs.WebApplicationException;
7 | import javax.ws.rs.core.MediaType;
8 | import javax.ws.rs.core.Response.Status;
9 |
10 | import org.apache.shiro.subject.Subject;
11 | import org.secnod.shiro.jaxrs.Auth;
12 |
13 | @Path("/inject/field")
14 | @Produces(MediaType.TEXT_PLAIN)
15 | public class FieldInjectionResource {
16 |
17 | @Auth Subject subject;
18 |
19 | @GET
20 | public String sessionUser() {
21 | try {
22 | return subject.getPrincipal().toString();
23 | } catch (NullPointerException e) {
24 | throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/src/test/java/org/secnod/shiro/test/integration/webapp/GuestOnlyResource.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.test.integration.webapp;
2 |
3 | import javax.ws.rs.GET;
4 | import javax.ws.rs.Path;
5 | import javax.ws.rs.Produces;
6 | import javax.ws.rs.core.MediaType;
7 |
8 | import org.apache.shiro.authz.annotation.RequiresGuest;
9 |
10 | @Path("/guestonly")
11 | @Produces(MediaType.TEXT_PLAIN)
12 | @RequiresGuest
13 | public class GuestOnlyResource {
14 |
15 | @GET
16 | public String get() {
17 | return "guest only";
18 | }
19 | }
--------------------------------------------------------------------------------
/src/test/java/org/secnod/shiro/test/integration/webapp/InjectionResource.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.test.integration.webapp;
2 |
3 | import javax.ws.rs.GET;
4 | import javax.ws.rs.Path;
5 | import javax.ws.rs.Produces;
6 | import javax.ws.rs.WebApplicationException;
7 | import javax.ws.rs.core.MediaType;
8 | import javax.ws.rs.core.Response.Status;
9 |
10 | import org.apache.shiro.subject.Subject;
11 | import org.secnod.example.webapp.User;
12 | import org.secnod.shiro.jaxrs.Auth;
13 |
14 | @Path("/inject")
15 | @Produces(MediaType.TEXT_PLAIN)
16 | public class InjectionResource {
17 |
18 | @Path("usersubject")
19 | @GET
20 | public String sessionUser(@Auth Subject subject, @Auth User user) {
21 | if (subject != user.unwrap(Subject.class)) {
22 | throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
23 | }
24 | return "User and Subject method param injection works.\n";
25 | }
26 | }
--------------------------------------------------------------------------------
/src/test/java/org/secnod/shiro/test/integration/webapp/IntegrationTestApplication.java:
--------------------------------------------------------------------------------
1 | package org.secnod.shiro.test.integration.webapp;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | import org.apache.shiro.web.jaxrs.ShiroFeature;
9 | import org.glassfish.jersey.server.ResourceConfig;
10 | import org.secnod.example.webapp.UserFactory;
11 | import org.secnod.shiro.jaxrs.ShiroExceptionMapper;
12 | import org.secnod.shiro.jersey.AuthorizationFilterFeature;
13 | import org.secnod.shiro.jersey.SubjectFactory;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 |
17 | import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
18 |
19 | /**
20 | * A JAX-RS application for running the integration tests.
21 | */
22 | @SuppressWarnings("deprecation")
23 | public class IntegrationTestApplication extends ResourceConfig {
24 |
25 | private static final Logger log = LoggerFactory.getLogger(IntegrationTestApplication.class);
26 |
27 | public IntegrationTestApplication() {
28 | super();
29 | if (Boolean.getBoolean("shiro.jersey")) {
30 | log.info("Using the shiro-jersey feature.");
31 | register(new AuthorizationFilterFeature());
32 | register(new ShiroExceptionMapper());
33 | } else {
34 | log.info("Using the native shiro-jaxrs feature.");
35 | register(ShiroFeature.class);
36 | }
37 | register(new SubjectFactory());
38 | register(new UserFactory());
39 | register(new JacksonJsonProvider());
40 | for (Object resource : createAllIntegrationTestResources()) {
41 | register(resource);
42 | }
43 | for (Class> resource : allIntegrationTestResourceClasses()) {
44 | register(resource);
45 | }
46 | }
47 |
48 | public static Set