在上一篇中我們研究了如何實現SpringSecurity中Jsp Tag的<security:authorize ifAllGranted="ROLE_SUPERVISOR">的功能。這一次我們一起研究一下如何實現在Tapestry5.1中添加一個Filter來對所有的操作進行權限的過濾控制。
在SpringSecurity中,我們一般是在application-context.xml中,添加一個SpringSecurity的Filter,然后在另外一個xml中詳細配置如何根據Url的規則進行權限的控制。而Tapestry的哲學是盡量減少Xml中的配置(其IOC容器也基本上是借鑒Guice而不Spring的),所以我們也是在代碼中實現權限規則的控制。
總體上來看,可以用兩種方式來實現url規則,一種是Request級別的Filter,一種是頁面組件級別的Filter,如果是Request級別的話,可以從Request對象中獲取Url路徑,這樣就與SpringSecurity基本一樣了。本文主要介紹頁面組件級別的Filter,從中我們也可以體會到Tapestry5.1中的IOC容器的強大和便利。
這就是Filter的代碼,這個Filter必須實現ComponentRequestFilter接口。值得注意的是其構造函數所需要用到的4個參數,這4個參數都是Tapestry5本身自有的服務,所以我們什么也不用做,Tapestry5自動會將服務的實例注入進來,這就是Tapestry-IOC的威力。
ComponentRequestFilter接口一共有4個方法需要實現,具體代碼如下:
1 public class RequiresLoginFilter implements ComponentRequestFilter {
2
3 private final PageRenderLinkSource renderLinkSource;
4
5 private final ComponentSource componentSource;
6
7 private final Response response;
8
9 private final ApplicationStateManager appStateManager;
10
11 public RequiresLoginFilter(PageRenderLinkSource renderLinkSource,
12 ComponentSource componentSource, Response response,
13 ApplicationStateManager appStateManager
14 ) {
15 this.renderLinkSource = renderLinkSource;
16 this.componentSource = componentSource;
17 this.response = response;
18 this.appStateManager = appStateManager;
19 }
20
21 public void handleComponentEvent(
22 ComponentEventRequestParameters parameters,
23 ComponentRequestHandler handler) throws IOException {
24
25 if (dispatchedToLoginPage(parameters.getActivePageName())) {
26 return;
27 }
28
29 handler.handleComponentEvent(parameters);
30
31 }
32
33 public void handlePageRender(PageRenderRequestParameters parameters,
34 ComponentRequestHandler handler) throws IOException {
35 if (dispatchedToLoginPage(parameters.getLogicalPageName())) {
36 return;
37 }
38 handler.handlePageRender(parameters);
39
40 }
41
42 private boolean dispatchedToLoginPage(String pageName) {
43 Component page = componentSource.getPage(pageName);
44
45 if (page.getClass().isAnnotationPresent(RequiresLogin.class)) {
46 if ( ! appStateManager.exists(Authentication.class)) {
47 redirect();
48 return true;
49 }
50 Authentication auth = appStateManager.get(Authentication.class);
51 if ( auth == null ) {
52 redirect();
53 return true;
54 }
55
56 if ( ! auth.isLoggedIn()) {
57 redirect();
58 return true;
59 }
60
61 RequiresLogin requireLogin = page.getClass().getAnnotation(
62 RequiresLogin.class);
63 String ifNotGranted = requireLogin.ifNotGranted();
64 String ifAllGranted = requireLogin.ifAllGranted();
65 String ifAnyGranted = requireLogin.ifAnyGranted();
66 boolean permitted = auth.checkPermission(ifNotGranted, ifAllGranted, ifAnyGranted);
67 if ( ! permitted ) {
68 return true;
69 }
70 }
71
72 return false;
73 }
74
75 private void redirect() {
76 Link link = renderLinkSource.createPageRenderLink("Logout");
77
78 try {
79 response.sendRedirect(link);
80 } catch (Exception e) {
81 }
82 }
83
84 }
在ComponentRequestFilter中,我們無法使用@SessionState注解來直接注入Session中的變量,但是我們可以通過ApplicationStateManager來取得。
現在我們需要把剛定義的Filter注冊到系統中,很簡單,只要在AppModule中添加以下函數就行了:
1 public static void contributeComponentRequestHandler(
2 OrderedConfiguration<ComponentRequestFilter> configuration) {
3 configuration.addInstance("RequiresLogin", RequiresLoginFilter.class);
4 }
5
從本例子中我們可以看到Tapesty Ioc容器使用的便利性,也認識到了Ioc容器在Tapestry體系中的重要性