1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.shiro.guice.web;
20
21 import com.google.inject.Binder;
22 import com.google.inject.Key;
23 import com.google.inject.TypeLiteral;
24 import com.google.inject.binder.AnnotatedBindingBuilder;
25 import com.google.inject.name.Names;
26 import com.google.inject.servlet.ServletModule;
27 import org.apache.shiro.config.ConfigurationException;
28 import org.apache.shiro.env.Environment;
29 import org.apache.shiro.guice.ShiroModule;
30 import org.apache.shiro.lang.util.StringUtils;
31 import org.apache.shiro.mgt.SecurityManager;
32 import org.apache.shiro.session.mgt.SessionManager;
33 import org.apache.shiro.web.env.WebEnvironment;
34 import org.apache.shiro.web.filter.InvalidRequestFilter;
35 import org.apache.shiro.web.filter.PathMatchingFilter;
36 import org.apache.shiro.web.filter.authc.AnonymousFilter;
37 import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
38 import org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter;
39 import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
40 import org.apache.shiro.web.filter.authc.LogoutFilter;
41 import org.apache.shiro.web.filter.authc.UserFilter;
42 import org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter;
43 import org.apache.shiro.web.filter.authz.IpFilter;
44 import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
45 import org.apache.shiro.web.filter.authz.PortFilter;
46 import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
47 import org.apache.shiro.web.filter.authz.SslFilter;
48 import org.apache.shiro.web.filter.mgt.FilterChainResolver;
49 import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
50 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
51 import org.apache.shiro.web.mgt.WebSecurityManager;
52 import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
53
54 import javax.servlet.Filter;
55 import javax.servlet.ServletContext;
56 import java.util.ArrayList;
57 import java.util.Arrays;
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.HashMap;
61 import java.util.LinkedHashMap;
62 import java.util.List;
63 import java.util.Map;
64
65 @SuppressWarnings("checkstyle:JavadocVariable")
66
67
68
69
70
71
72
73
74
75
76 public abstract class ShiroWebModule extends ShiroModule {
77 public static final Key<AnonymousFilter> ANON = Key.get(AnonymousFilter.class);
78 public static final Key<FormAuthenticationFilter> AUTHC = Key.get(FormAuthenticationFilter.class);
79 public static final Key<BasicHttpAuthenticationFilter> AUTHC_BASIC = Key.get(BasicHttpAuthenticationFilter.class);
80 public static final Key<BearerHttpAuthenticationFilter> AUTHC_BEARER = Key.get(BearerHttpAuthenticationFilter.class);
81 public static final Key<NoSessionCreationFilter> NO_SESSION_CREATION = Key.get(NoSessionCreationFilter.class);
82 public static final Key<LogoutFilter> LOGOUT = Key.get(LogoutFilter.class);
83 public static final Key<PermissionsAuthorizationFilter> PERMS = Key.get(PermissionsAuthorizationFilter.class);
84 public static final Key<PortFilter> PORT = Key.get(PortFilter.class);
85 public static final Key<HttpMethodPermissionFilter> REST = Key.get(HttpMethodPermissionFilter.class);
86 public static final Key<RolesAuthorizationFilter> ROLES = Key.get(RolesAuthorizationFilter.class);
87 public static final Key<SslFilter> SSL = Key.get(SslFilter.class);
88 public static final Key<IpFilter> IP = Key.get(IpFilter.class);
89 public static final Key<UserFilter> USER = Key.get(UserFilter.class);
90 public static final Key<InvalidRequestFilter> INVALID_REQUEST = Key.get(InvalidRequestFilter.class);
91
92 static final String NAME = "SHIRO";
93
94
95
96
97
98 private final Map<String, FilterConfig<? extends Filter>[]> filterChains =
99 new LinkedHashMap<String, FilterConfig<? extends Filter>[]>();
100 private final ServletContext servletContext;
101
102 public ShiroWebModule(ServletContext servletContext) {
103 this.servletContext = servletContext;
104 }
105
106 public static void bindGuiceFilter(Binder binder) {
107 binder.install(guiceFilterModule());
108 }
109
110 public static void bindGuiceFilter(final String pattern, Binder binder) {
111 binder.install(guiceFilterModule(pattern));
112 }
113
114 public static ServletModule guiceFilterModule() {
115 return guiceFilterModule("/*");
116 }
117
118 public static ServletModule guiceFilterModule(final String pattern) {
119 return new ServletModule() {
120 @Override
121 protected void configureServlets() {
122 filter(pattern).through(GuiceShiroFilter.class);
123 }
124 };
125 }
126
127 public List<FilterConfig<? extends Filter>> globalFilters() {
128 return Collections.singletonList(filterConfig(INVALID_REQUEST));
129 }
130
131 @Override
132 @SuppressWarnings("unchecked")
133 protected final void configureShiro() {
134 bindBeanType(TypeLiteral.get(ServletContext.class), Key.get(ServletContext.class, Names.named(NAME)));
135 bind(Key.get(ServletContext.class, Names.named(NAME))).toInstance(this.servletContext);
136 bindWebSecurityManager(bind(WebSecurityManager.class));
137 bindWebEnvironment(bind(WebEnvironment.class));
138 bind(GuiceShiroFilter.class).asEagerSingleton();
139 expose(GuiceShiroFilter.class);
140
141 this.configureShiroWeb();
142
143
144 if (!filterChains.containsKey("/**")) {
145
146 this.addFilterChain("/**", new FilterConfig[0]);
147 }
148
149 bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(setupFilterChainConfigs()));
150 }
151
152 @SuppressWarnings("unchecked")
153 private Map<String, Key<? extends Filter>[]> setupFilterChainConfigs() {
154
155
156 Map<Key<? extends Filter>, Map<String, String>> filterToPathToConfig =
157 new HashMap<Key<? extends Filter>, Map<String, String>>();
158
159
160 Map<String, Key<? extends Filter>[]> resultConfigMap = new LinkedHashMap<String, Key<? extends Filter>[]>();
161
162 for (Map.Entry<String, FilterConfig<? extends Filter>[]> filterChain : filterChains.entrySet()) {
163
164 String path = filterChain.getKey();
165
166
167 List<Key<? extends Filter>> keysForPath = new ArrayList<Key<? extends Filter>>();
168
169 List<FilterConfig<? extends Filter>> globalFilters = this.globalFilters();
170 FilterConfig<? extends Filter>[] pathFilters = filterChain.getValue();
171
172
173 List<FilterConfig<? extends Filter>> filterConfigs = new ArrayList<>(globalFilters.size() + pathFilters.length);
174 filterConfigs.addAll(globalFilters);
175 filterConfigs.addAll(Arrays.asList(pathFilters));
176
177 for (FilterConfig<? extends Filter> filterConfig : filterConfigs) {
178
179 Key<? extends Filter> key = filterConfig.getKey();
180 String config = filterConfig.getConfigValue();
181
182
183 if (filterToPathToConfig.get(key) == null) {
184
185 filterToPathToConfig.put((key), new LinkedHashMap<String, String>());
186 }
187
188 filterToPathToConfig.get(key).put(path, config);
189
190
191 if (StringUtils.hasText(config)
192 && !PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
193 throw new ConfigurationException(
194 "Config information requires a PathMatchingFilter - can't apply to "
195 + key.getTypeLiteral().getRawType());
196 }
197
198
199 keysForPath.add(key);
200 }
201
202
203 resultConfigMap.put(path, keysForPath.toArray(new Key[keysForPath.size()]));
204 }
205
206
207
208 for (Key<? extends Filter> key : filterToPathToConfig.keySet()) {
209 if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
210 bindPathMatchingFilter(castToPathMatching(key), filterToPathToConfig.get(key));
211 } else {
212 bind(key);
213 }
214 }
215
216 return resultConfigMap;
217 }
218
219
220 private <T extends PathMatchingFilter> void bindPathMatchingFilter(Key<T> filterKey, Map<String, String> configs) {
221 bind(filterKey).toProvider(new PathMatchingFilterProvider<T>(filterKey, configs)).asEagerSingleton();
222 }
223
224 @SuppressWarnings({"unchecked"})
225 private Key<? extends PathMatchingFilter> castToPathMatching(Key<? extends Filter> key) {
226 return (Key<? extends PathMatchingFilter>) key;
227 }
228
229 protected abstract void configureShiroWeb();
230
231 @SuppressWarnings({"unchecked"})
232 @Override
233 protected final void bindSecurityManager(AnnotatedBindingBuilder<? super SecurityManager> bind) {
234
235 bind.to(WebSecurityManager.class);
236 }
237
238
239
240
241
242
243
244
245 protected void bindWebSecurityManager(AnnotatedBindingBuilder<? super WebSecurityManager> bind) {
246 try {
247 bind.toConstructor(DefaultWebSecurityManager.class.getConstructor(Collection.class)).asEagerSingleton();
248 } catch (NoSuchMethodException e) {
249 throw new ConfigurationException("This really shouldn't happen. Either something has changed in Shiro, "
250 + "or there's a bug in ShiroModule.", e);
251 }
252 }
253
254
255
256
257
258
259
260
261 @Override
262 protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind) {
263 bind.to(ServletContainerSessionManager.class).asEagerSingleton();
264 }
265
266 @Override
267 protected final void bindEnvironment(AnnotatedBindingBuilder<Environment> bind) {
268
269 bind.to(WebEnvironment.class);
270 }
271
272 protected void bindWebEnvironment(AnnotatedBindingBuilder<? super WebEnvironment> bind) {
273 bind.to(WebGuiceEnvironment.class).asEagerSingleton();
274 }
275
276 @SuppressWarnings("unchecked")
277 protected final void addFilterChain(String pattern, Key<? extends Filter> key) {
278
279 if (key instanceof FilterConfigKey) {
280 addLegacyFilterChain(pattern, (FilterConfigKey) key);
281 } else {
282 addFilterChain(pattern, new FilterConfig<Filter>((Key<Filter>) key, ""));
283 }
284 }
285
286
287
288
289
290
291
292
293
294
295
296 @SafeVarargs
297 protected final void addFilterChain(String pattern, FilterConfig<? extends Filter>... filterConfigs) {
298 filterChains.put(pattern, filterConfigs);
299 }
300
301
302
303
304
305
306
307
308
309 protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey, String configValue) {
310 return new FilterConfig<T>(baseKey, configValue);
311 }
312
313
314
315
316
317
318
319
320
321 protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey) {
322 return filterConfig(baseKey, "");
323 }
324
325
326
327
328
329
330
331
332
333
334 protected static <T extends Filter> FilterConfig<T> filterConfig(TypeLiteral<T> typeLiteral, String configValue) {
335 return filterConfig(Key.get(typeLiteral), configValue);
336 }
337
338
339
340
341
342
343
344
345
346
347 protected static <T extends Filter> FilterConfig<T> filterConfig(Class<T> type, String configValue) {
348 return filterConfig(Key.get(type), configValue);
349 }
350
351
352
353
354
355
356
357
358 public static final class FilterConfig<T extends Filter> {
359 private Key<T> key;
360 private String configValue;
361
362 private FilterConfig(Key<T> key, String configValue) {
363 super();
364 this.key = key;
365 this.configValue = configValue;
366 }
367
368 public Key<T> getKey() {
369 return key;
370 }
371
372 public String getConfigValue() {
373 return configValue;
374 }
375 }
376
377
378
379
380
381 static boolean isGuiceVersion3() {
382 try {
383 Class.forName("com.google.inject.multibindings.MapKey");
384 return false;
385 } catch (ClassNotFoundException e) {
386 return true;
387 }
388 }
389
390 @SuppressWarnings("unchecked")
391 private void addLegacyFilterChain(String pattern, FilterConfigKey filterConfigKey) {
392 FilterConfig<Filter> filterConfig = new FilterConfig<>(filterConfigKey.getKey(), filterConfigKey.getConfigValue());
393 addFilterChain(pattern, filterConfig);
394 }
395
396
397
398
399
400
401
402
403
404
405 @Deprecated
406 @SuppressWarnings("unchecked")
407 protected final void addFilterChain(String pattern, Key<? extends Filter>... keys) {
408
409
410
411 FilterConfig[] filterConfigs = new FilterConfig[keys.length];
412 for (int ii = 0; ii < keys.length; ii++) {
413 Key<? extends Filter> key = keys[ii];
414
415 if (key instanceof FilterConfigKey) {
416
417 FilterConfigKey legacyKey = (FilterConfigKey) key;
418 filterConfigs[ii] = new FilterConfig(legacyKey.getKey(), legacyKey.getConfigValue());
419 } else {
420
421 filterConfigs[ii] = new FilterConfig(key, "");
422 }
423 }
424
425 filterChains.put(pattern, filterConfigs);
426 }
427
428 @Deprecated
429 protected static <T extends PathMatchingFilter> Key<T> config(Key<T> baseKey, String configValue) {
430
431 if (!isGuiceVersion3()) {
432 throw new ConfigurationException(
433 "Method ShiroWebModule.config(Key<? extends PathMatchingFilter>,"
434 + " String configValue), is not supported when using Guice 4+");
435 }
436
437 return new FilterConfigKey<T>(baseKey, configValue);
438 }
439
440 @Deprecated
441 protected static <T extends PathMatchingFilter> Key<T> config(TypeLiteral<T> typeLiteral, String configValue) {
442 return config(Key.get(typeLiteral), configValue);
443 }
444
445 @Deprecated
446 protected static <T extends PathMatchingFilter> Key<T> config(Class<T> type, String configValue) {
447 return config(Key.get(type), configValue);
448 }
449
450 @Deprecated
451 private static final class FilterConfigKey<T extends PathMatchingFilter> extends Key<T> {
452 private Key<T> key;
453 private String configValue;
454
455 private FilterConfigKey(Key<T> key, String configValue) {
456 super();
457 this.key = key;
458 this.configValue = configValue;
459 }
460
461 public Key<T> getKey() {
462 return key;
463 }
464
465 public String getConfigValue() {
466 return configValue;
467 }
468 }
469
470 }