本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ttff521xx/archive/2009/08/10/4430143.aspx
权限验证
(代码中部分删除,保留框架)
1.struts框架安全隐患
使用Struts框架时,权限通常控制在Action级(比如将权限验证放在Action的基类中,这样新的Action都继承于这个Action基类,所有Action就可以专注于业务逻辑,而不需要重复地进行权限控制了),这也符合MVC中的角色划分。然而,这会产生一个安全隐患。因为权限控制在Action中,所以,页面也就没有安全屏障了。一般的新增数据、更新数据不会有什么问题,因为这些数据必须通过HTML的Form提交到Struts的中心控制器,最终由相应的Action来处理,所以Action中就可以验证该用户的权限了。然而,对于一些不需要Action进行数据存取,或者有的页面没有严格按照MVC的角色划分而在页面中有获取数据的代码,那么这个页面就危险了。比如,显示一张通知页面,通常可以通过配置权限,使部分授权的用户才可以看到该级别的通知。这个通知页面是不需要从数据库中获取数据的。所以,可以不通过Action的调用来显示,而是直接敲入显示该通知的页面的链接就可以看到了。甚至不需要登录系统,不用管是否有查看该通知的权限!
比如下图中:
请求1:
是通过正常的途径访问的,需要经过权限验证。
而请求2:
则完全绕过了权限检查,任何人,不需要登录系统就可以访问到该信息了。
2.解决方案
解决的办法: 采用servlet过滤器
servlet过滤器是小型的web组件,它能够处理传入的请求和传出的响应。Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在离开servlet时处理response。它具有高度的透明性,无需更改应用程序代码,就可以根据需要添加、修改或从应用程序中将它删除。
一个filter 包括:
1. 在servlet被调用之前截获;
2. 在servlet被调用之前检查servlet request;
3. 根据需要修改request头和request数据;
4. 根据需要修改response头和response数据;
5. 在servlet被调用之后截获.
你能够配置一个filter 到一个或多个servlet;单个servlet或servlet组能够被多个filter 使用。几个实用的filter包括:用户辨认filter,日志filter,审核filter,加密filter,符号filter,能改变xml内容的XSLT filter等。
一个客户化的过滤器要实现Filter接口的三个方法:init()、destroy()和doFilter()。
1. init():在容器实例化过滤器时调用,该方法接受一个FilterConfig类型的对象做为输入。
2. destroy():执行一些清理操作。
3. doFilter():类似servlet的doPost()、doGet()方法,执行具体的过滤任务。
设置URL拦截器,对截获的请求url(两部分包括XXX.do和XXX.Jsp)做权限验证,具有该权限则继续下一步,否则跳转到相应页面显示用户没有权限。
3.具体步骤
统过滤器配置具体步骤:
1. 首先写一个权限过滤filter类,实现Filter接口
jsp权限过滤器permissionFilter
package dao;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import po.login;
/*
* jsp安全过滤器
* filter:permissionFilter
* */
public class permissionFilter implements Filter {
// 1,doFilter方法的第一个参数为ServletRequest对象。
// 此对象给过滤器提供了对进入的信息(包括表单数据、cookie和HTTP请求头)的完全访问。
// 第二个参数为ServletResponse,通常在简单的过滤器中忽略此参数。
// 最后一个参数为FilterChain,此参数用来调用servlet或JSP页。
private FilterConfig filterConfig;
private FilterChain chain;
private HttpServletRequest request;
private HttpServletResponse response;
public void destroy() {
this.filterConfig = null;
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain chain) {
this.chain = chain;
this.request = (HttpServletRequest) servletRequest;
// 如果处理HTTP请求,并且需要访问诸如getHeader或getCookies等在ServletRequest中无法得到的方法,
// 就要把此request对象构造成HttpServletRequest
this.response = ((HttpServletResponse) servletResponse);
// 获取当前页面文件名此处url为:/Gzlkh/login.jsp
String url = request.getRequestURI();
// 此处截取的url为:login.jsp
url = url.substring(url.lastIndexOf("/") + 1, url.length());
try {
HttpSession session = request.getSession();
// 获取网站访问根目录
String accessPath = request.getContextPath();
// 获取用户登录验证信息
login st = (login) session.getAttribute("st");
if (noFileUrl(url, request)) {
// 不需要判断权限的请求如登录页面,则跳过
chain.doFilter(request, response);// 继续执行请求
} else if (st == null) {
response.sendRedirect(accessPath + "/login.jsp");
// 未登录或超时,返回登陆页面
} else {
verifyUrl(url, st);// 判断当前user是否拥有访问此url的权限
}
} catch (Exception sx) {
sx.printStackTrace();
}
}
/**
* 判断当前user是否拥有访问此url的权限
* @param url
* 当前请求的url
* @param st
* 当前登录用户信息
* @throws IOException
* @throws ServletException
*/
private void verifyUrl(String url, login st) throws IOException,
ServletException {
// 获取user拥有的所有资源串,此处全部提取出来,以方便以后项目扩展
//更好的方式是把此处的权限页面存储在数据库中,与角色建立关系,以后可以直接根据用户角色从
//数据库中取出
String sturl = null;
// 拥有权限页1的jsp页面
String url1="";
// // 拥有权限页2的jsp页面
String url2 = ""
// // 拥有权限页3的jsp页面
String url3 = "";
// 拥有权限页4的jsp页面
String url4 = "";
// // 拥有权限页5的jsp页面
String url5 = "";
// 拥有权限页6的jsp页面
String url6 = "";
// 公共
String url7 = "";
sturl = url7;
//以下判断用户是否有进入该页面的权限,有则加入
if ("1".equals(st.getKhbfqx())) {
sturl = sturl + "," + url1;
}
if ("1".equals(st.getRcgzqx())) {
sturl = sturl + "," + url2;
}
if ("1".equals(st.getKhzbqx())) {
sturl = sturl + "," + url3;
}
if ("1".equals(st.getKhjgqx())) {
sturl = sturl + "," + url4;
}
if ("1".equals(st.getXtpzqx())) {
sturl = sturl + "," + url6;
}
if ("1".equals(st.getYhglqx())) {
sturl = sturl + "," + url5;
}
if (sturl.indexOf(url) >= 0) {
//判断用户权限页面包含请求url里面的页面
chain.doFilter(request, response);
} else {
//用户无权限跳转提示
response.setContentType("text/html;charset=GBK");
response.getWriter().println("<div style='margin: 100 auto;text-align: center; "
+ "font: bold 18px 宋体;color: #0066CC;vertical-align: middle'> Sorry,您没有权限访问该资源!</div>");
}
}
/**
* 特殊页面判断
* 是否需要判断权限,如客户端浏览、登录页面则不需要判断权限
*/
protected boolean noFileUrl(String url, HttpServletRequest request) {
//不需要权限验证的页面动作等
String exclude = "xx.jsp";
//判断请求页面是否是特殊页面
if (exclude.indexOf(url) >= 0) {
return true;
}
return false;
}
}
Action权限过滤器permissiondoFilter
package dao;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.FilterChain;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import po.login;
public class permissiondoFilter implements Filter {
//1,doFilter方法的第一个参数为ServletRequest对象。
//此对象给过滤器提供了对进入的信息(包括表单数据、cookie和HTTP请求头)的完全访问。
//第二个参数为ServletResponse,通常在简单的过滤器中忽略此参数。
//最后一个参数为FilterChain,此参数用来调用servlet或JSP页。
private FilterConfig filterConfig;
private FilterChain chain;
private HttpServletRequest request;
private HttpServletResponse response;
public void destroy() {
this.filterConfig = null;
}
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain chain) {
this.chain = chain;
this.request = (HttpServletRequest) servletRequest;
//如果处理HTTP请求,并且需要访问诸如getHeader或getCookies等在ServletRequest中无法得到的方法,就要把此request对象构造成HttpServletRequest
this.response = ((HttpServletResponse) servletResponse);
// 获取当前页面文件名此处url为:/Gzlkh/login.jsp
String url = request.getRequestURI();
// 此处截取的url为:login.jsp
url = url.substring(url.lastIndexOf("/") + 1, url.length());
try {
// 排除后台不作权限控制的页面名
// 登陆页面无需验证文件
HttpSession session = request.getSession();
// 获取网站访问根目录
String accessPath = request.getContextPath();
login st = (login) session.getAttribute("st");
if (noFileUrl(url, request)) { // 不需要判断权限的请求如登录页面,则跳过
chain.doFilter(request, response);
} else if (st == null) {
response.sendRedirect(accessPath + "/login.jsp");// 返回登陆页面(未登录或超时)
} else {
System.out.println(st.getRolename()+st.getUsername()+"-访问-"+url);
verifyUrl(url, st);// 判断当前user是否拥有访问此url的权限
}
} catch (Exception sx) {
sx.printStackTrace();
}
}
/**
* @param url
* 当前请求的url
* @param user
* 当前登录用户
* @throws IOException
* @throws ServletException
*/
private void verifyUrl(String url, login st)throws IOException, ServletException {
// 获取user拥有的所有资源串
String sturl=null;
String url1="";
String url2="";
// 考核结果页面
String url3="";
// 用户管理页面
String url4="";
// 权重配置页面
String url5="";
// 个人密码修改公共
String url6="";
sturl=url6;
if("1".equals(st.getRcgzqx())){
sturl=sturl+","+url1;
}
if("1".equals(st.getKhzbqx())){
sturl=sturl+","+url2;
}
if("1".equals(st.getKhjgqx())){
sturl=sturl+","+url3;
}
if("1".equals(st.getXtpzqx())){
sturl=sturl+","+url4;
}
if("1".equals(st.getYhglqx())){
sturl=sturl+","+url5;
}
if(sturl.indexOf(url)>=0){
System.out.println("有权访问!");
chain.doFilter(request, response);
}else{
System.out.println("无权限!");
response.setContentType("text/html;charset=GBK");
response.getWriter().println("<div style='margin: 100 auto;text-align: center;background-image:url(images/bg.jpg); "
+ "font: bold 18px 宋体;color: #0066CC;vertical-align: middle'> Sorry,您没有权限访问该资源!</div>");
}
}
/**
* 是否需要判断权限,如客户端浏览、登录页面则不需要判断权限
*/
protected boolean noFileUrl(String url, HttpServletRequest request) {
String exclude = "login.do";
if (exclude.indexOf(url)>=0) {
return true;
}
return false;
}
}
2.配置 Servlet过滤器
容器通过 Web 应用程序中的配置描述符 web.xml 文件了解过滤器。有两个新的标记与过滤器相关:<filter> 和 <filter-mapping>。应该指定它们为 web.xml 文件内 <web-app> 标记的子标记。
过滤器定义的元素
<filter> 标记是一个过滤器定义,它必定有一个 <filter- name> 和 <filter-class> 子元素。
<filter-name> 子元素给出了一个与过滤器实例相关的、基于文本的名字。
<filter-class> 指定了由容器载入的实际类。您能随意地包含一个 <init-param> 子元素为过滤器实例提供初始化参数。
<!--权限jsp过滤器-->
<filter >
<filter-name>permission </filter-name>
<filter-class>dao.permissionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>permission </filter-name>
<!-- 只过滤 .jsp 结尾的url, 其余的如 .do, .html, .jpg, .css 等不作过滤-->
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<!--权限do过滤器-->
<filter >
<filter-name>permissiondo </filter-name>
<filter-class>dao.permissiondoFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>permissiondo </filter-name>
<!-- 只过滤 .do 结尾的url, 其余的如 .jsp, .html, .jpg, .css 等不作过滤-->
<url-pattern>*.do</url-pattern>
</filter-mapping>