需要实现的功能:判断用户是否已登录,未登录用户禁止访问任何页面或action,自动跳转到登录页面。
过程:因为对过滤器和拦截器都不熟悉,开始两种方式都问题不断,后调试通过,贴在这里留作小结和备忘
过滤器filter实现
配置:web.xml
1. <filter>
2. <filter-name>RightFilter</filter-name>
3. <filter-class>com.***.rights.RightFilter</filter-class>
4. </filter>
5. <filter-mapping>
6. <filter-name>RightFilter</filter-name>
7. <url-pattern>*.jsp</url-pattern>
8. </filter-mapping>
9. <filter-mapping>
10. <filter-name>RightFilter</filter-name>
11. <url-pattern>*.action</url-pattern>
12. </filter-mapping>
代码:
1. mport java.io.IOException;
2.
3. import javax.servlet.Filter;
4. import javax.servlet.FilterChain;
5. import javax.servlet.FilterConfig;
6. import javax.servlet.ServletException;
7. import javax.servlet.ServletRequest;
8. import javax.servlet.ServletResponse;
9. import javax.servlet.http.HttpServlet;
10. import javax.servlet.http.HttpServletRequest;
11. import javax.servlet.http.HttpServletResponse;
12. import javax.servlet.http.HttpSession;
13.
14. public class RightFilter extends HttpServlet implements Filter {
15.
16. public void doFilter(ServletRequest arg0, ServletResponse arg1,
17. FilterChain arg2) throws IOException, ServletException {
18. response = (HttpServletResponse) arg1;
19. request=(HttpServletRequest)arg0;
20. session = request.getSession(true);
21. usercode = (String) session.getAttribute("usercode");//
22. url=request.getRequestURI();
23. usercode==null || usercode.equals(""))
24. {
25. //判断获取的路径不为空且不是访问登录页面或执行登录操作时跳转
26. <0 && url.indexOf("login")<0 ))
27. {
28. response.sendRedirect("登录路径");
29. return ;
30. }
31. }
32. //已通过验证,用户访问继续
33. arg2.doFilter(arg0, arg1);
34. return;
35. }
36.
37. public void init(FilterConfig arg0) throws ServletException {
38. // TODO Auto-generated method stub
39.
40. }
配置中的filter-mapping,定义的是需过滤的请求类型,上面的配置即过滤所有对jsp页面和action的请求。过滤器的实现与struts2、spring框架无关,在用户请求被相应前执行,在过滤器中,可使用response.sendRedirect("")等方法
跳转到需要的链接,如登录页面、错误页面等,不需要跳转时,arg2.doFilter(arg0, arg1);即可继续执行用户的请求。注意使用filter时避免连续两次跳转,否则会报java.lang.IllegalStateException错误,具体配置方法网上有,除非必要,不建议使用/*(过滤所有访问)的配置方式,这样配置,图片、js文件、css文件等访问都会被过滤
拦截器interceptor实现:
配置:struts.xml
1. <interceptors>
2. <!--定义一个名为authority的拦截器-->
3. <interceptor class="com.***.rights.RightInterceptor" name="rightInterceptor"/>
4. <!--定义一个包含权限检查的拦截器栈-->
5. <interceptor-stack name="mydefault">
6. <!--配置内建默认拦截器-->
7. <interceptor-ref name="defaultStack"/>
8. <!--配置自定义的拦截器-->
9. <interceptor-ref name="rightInterceptor"/>
10. </interceptor-stack>
11. </interceptors>
12. <default-interceptor-ref name="mydefault" />
13. <!--定义全局Result-->
14. <global-results>
15. <result name="login">Login.jsp</result>
16. <result name="error">/error.jsp </result>
17. </global-results>
代码:
1. import java.util.HashMap;
2. import java.util.Map;
3. import com.opensymphony.xwork2.Action;
4. import com.opensymphony.xwork2.ActionInvocation;
5. import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
6. import com.opensymphony.xwork2.ActionContext;
7. public class RightInterceptor extends AbstractInterceptor {
8.
9. @Override
10. public String intercept(ActionInvocation invocation) throws Exception {
11. //System.out.println("拦截器开始验证");
12. try
13. {
14. actionContext = ActionContext.getContext();
15. <String,Object> session = actionContext.getSession();
16. user=session.get("usercode").toString();
17. //当前用户session无效且访问的action不是登录action时,执行拦截,跳转
18. user==null || user.equals("")) && !invocation.getAction().getClass().getName().equals("登录action"))
19. {
20. return Action.LOGIN;
21. }
22. }
23. catch(Exception e)
24. {
25. e.printStackTrace();
26. return Action.LOGIN;
27. }
28. //System.out.println("拦截器通过验证");
29. return invocation.invoke();//执行访问的action
30.
31.
32. }
33.
34. }
拦截器由spring管理,只对action起作用,不能拦截jsp页面、图片等其他资源。拦截器截获用户对action的访问,如需要跳转,只需如action一样返回一个result,spring根据result的配置执行跳转。如无需跳转,可调用invocation.invoke();方法来执行用户请求的action。拦截器在action之前开始,在action完成后结束(如被拦截,action根本不执行)
如不进行处理,过滤器和拦截器都会将正常的登录操作屏蔽,因此过滤器中需要判断用户访问的url是否为登录操作或登录页面,拦截器中需要判断用户访问的action是否登录action。
------------------=================================================================================
Struts2项目通过使用Struts的if标签进行了session判断,使得未登录的用户不能看到页面,但是这 种现仅仅在view层进行,如果未登录用户直接在地址栏输入登录用户才能访问的地址,那么相应的action还是会执行,仅仅是不让用户看到罢了。这样显然是不好的,所以研究了一下Struts2的权限验证。
权限最核心的是业务逻辑,具体用什么技术来实现就简单得多。
通常:用户与角色建立多对多关系,角色与业务模块构成多对多关系,权限管理在后者关系中。
对权限的拦截,如果系统请求量大,可以用Struts2拦截器来做,请求量小可以放在filter中。但一般单级拦截还不够,要做到更细粒度的权限控制,还需要多级拦截。
不大理解filter(过滤器)和interceptor(拦截器)的区别,遂google之。博文中有介绍:
1、拦截器是基于java的反射机制的,而过滤器是基于函数回调 。
2、过滤器依赖与servlet容器,而拦截器不依赖与servlet容器 。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求 起作用 。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能 。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容 器初始化时被调用一次 。
为了学习决定把两种实现方式都试一下,然后再决定使用哪个。
权限验证的Filter实现:
web.xml代码片段
1. <!-- authority filter 最好加在Struts2的Filter前面-->
2. <filter>
3. <filter-name>SessionInvalidate</filter-name>
4. <filter-class>filter.SessionCheckFilter</filter-class>
5. <init-param>
6. <param-name>checkSessionKey</param-name>
7. <param-value>loginName</param-value>
8. </init-param>
9. <init-param>
10. <param-name>redirectURL</param-name>
11. <param-value>/entpLogin.jsp</param-value>
12. </init-param>
13. <init-param>
14. <param-name>notCheckURLList</param-name>
15. <param-value>/entpLogin.jsp,/rois/loginEntp.action,/entpRegister.jsp,/test.jsp,/rois/registerEntp.action</param-value>
16. </init-param>
17. </filter>
18. <!--过滤/rois命名空间下所有action -->
19. <filter-mapping>
20. <filter-name>SessionInvalidate</filter-name>
21. <url-pattern>/rois/*</url-pattern>
22. </filter-mapping>
23. <!--过滤/jsp文件夹下所有jsp -->
24. <filter-mapping>
25. <filter-name>SessionInvalidate</filter-name>
26. <url-pattern>/jsp/*</url-pattern>
27. </filter-mapping>
SessionCheckFilter.java代码
1. package filter;
2. import java.io.IOException;
3. import java.util.HashSet;
4. import java.util.Set;
5. import javax.servlet.Filter;
6. import javax.servlet.FilterChain;
7. import javax.servlet.FilterConfig;
8. import javax.servlet.ServletException;
9. import javax.servlet.ServletRequest;
10. import javax.servlet.ServletResponse;
11. import javax.servlet.http.HttpServletRequest;
12. import javax.servlet.http.HttpServletResponse;
13. import javax.servlet.http.HttpSession;
14. /**
15. * 用于检测用户是否登陆的过滤器,如果未登录,则重定向到指的登录页面 配置参数 checkSessionKey 需检查的在 Session 中保存的关键字
16. * redirectURL 如果用户未登录,则重定向到指定的页面,URL不包括 ContextPath notCheckURLList
17. * 不做检查的URL列表,以分号分开,并且 URL 中不包括 ContextPath
18. */
19. public class SessionCheckFilter implements Filter {
20. protected FilterConfig filterConfig = null;
21. private String redirectURL = null;
22. private Set<String> notCheckURLList = new HashSet<String>();
23. private String sessionKey = null;
24. @Override
25. public void destroy() {
26. notCheckURLList.clear();
27. }
28. @Override
29. public void doFilter(ServletRequest servletRequest,
30. ServletResponse servletResponse, FilterChain filterChain)
31. throws IOException, ServletException {
32. HttpServletRequest request = (HttpServletRequest) servletRequest;
33. HttpServletResponse response = (HttpServletResponse) servletResponse;
34. HttpSession session = request.getSession();
35. if (sessionKey == null) {
36. filterChain.doFilter(request, response);
37. return;
38. }
39. if ((!checkRequestURIIntNotFilterList(request))
40. null) {
41. response.sendRedirect(request.getContextPath() + redirectURL);
42. return;
43. }
44. filterChain.doFilter(servletRequest, servletResponse);
45. }
46. private boolean checkRequestURIIntNotFilterList(HttpServletRequest request) {
47. String uri = request.getServletPath()
48. null ? "" : request.getPathInfo());
49. String temp = request.getRequestURI();
50. 1);
51. // System.out.println("是否包括:"+uri+";"+notCheckURLList+"=="+notCheckURLList.contains(uri));
52. return notCheckURLList.contains(uri);
53. }
54. @Override
55. public void init(FilterConfig filterConfig) throws ServletException {
56. this.filterConfig = filterConfig;
57. "redirectURL");
58. "checkSessionKey");
59. String notCheckURLListStr = filterConfig
60. "notCheckURLList");
61. if (notCheckURLListStr != null) {
62. System.out.println(notCheckURLListStr);
63. ",");
64. for (int i = 0; i < params.length; i++) {
65. notCheckURLList.add(params[i].trim());
66. }
67. }
68. }
69. }
权限验证的Interceptor实现:
使用Interceptor不需要更改web.xml,只需要对struts.xml进行配置
struts.xml片段
1. <!-- 用户拦截器定义在该元素下 -->
2. <interceptors>
3. <!-- 定义了一个名为authority的拦截器 -->
4. <interceptor name="authenticationInterceptor" class="interceptor.AuthInterceptor" />
5. <interceptor-stack name="defualtSecurityStackWithAuthentication">
6. <interceptor-ref name="defaultStack" />
7. <interceptor-ref name="authenticationInterceptor" />
8. </interceptor-stack>
9. </interceptors>
10. <default-interceptor-ref name="defualtSecurityStackWithAuthentication" />
11. <!-- 全局Result -->
12. <global-results>
13. <result name="error">/error.jsp</result>
14. <result name="login">/Login.jsp</result>
15. </global-results>
16. <action name="login" class="action.LoginAction">
17. <param name="withoutAuthentication">true</param>
18. <result name="success">/WEB-INF/jsp/welcome.jsp</result>
19. <result name="input">/Login.jsp</result>
20. </action>
21. <action name="viewBook" class="action.ViewBookAction">
22. <result name="sucess">/WEB-INF/viewBook.jsp</result>
23. </action>
AuthInterceptor.java代码
1. package interceptor;
2. import java.util.Map;
3. import com.opensymphony.xwork2.Action;
4. import com.opensymphony.xwork2.ActionContext;
5. import com.opensymphony.xwork2.ActionInvocation;
6. import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
7. public class AuthInterceptor extends AbstractInterceptor {
8. private static final long serialVersionUID = -5114658085937727056L;
9. private String sessionKey="loginName";
10. private String parmKey="withoutAuthentication";
11. private boolean excluded;
12. @Override
13. public String intercept(ActionInvocation invocation) throws Exception {
14.
15. ActionContext ac=invocation.getInvocationContext();
16. Map<?, ?> session =ac.getSession();
17. String parm=(String) ac.getParameters().get(parmKey);
18.
19. if(parm!=null){
20. "TRUE");
21. }
22.
23. String user=(String)session.get(sessionKey);
24. if(excluded || user!=null){
25. return invocation.invoke();
26. }
27. "tip", "您还没有登录!");
28. //直接返回 login 的逻辑视图
29. return Action.LOGIN;
30. }
31. }
使用自定义的default-interceptor的话有需要注意几点:
1.一定要引用一下Sturts2自带defaultStack。否则会用不了Struts2自带的拦截器。
2.一旦在某个包下定义了上面的默认拦截器栈,在该包下的所有 Action 都会自动增加权限检查功能。所以有可能会出现永远登录不了的情况。
解决方案:
1.像上面的代码一样,在action里面增加一个参数表明不需要验证,然后在interceptor实现类里面检查是否不需要验证
2.将那些不需要使用权限控制的 Action 定义在另一个包中,这个新的包中依然使用 Struts 2 原有的默认拦截器栈,将不会有权限控制功能。
3.Interceptor是针对action的拦截,如果知道jsp地址的话在URL栏直接输入JSP的地址,那么权限验证是没有效果滴!
解决方案:把所有page代码(jsp)放到WEB-INF下面,这个目录下的东西是“看不见”的