Servlet之安全问题

在之前的博文中我们讲了如何新建和运行一个Servlet,也讲了一般的Servlet的知识点,如通配符映射等。

那么思考下,我们要访问一个写好了的Servlet,单独访问是没有问题的,如果有n个用户同时访问这个网址呢?会出现什么问题?

public class ConcurrentTest extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = -2153241422283937673L;
PrintWriter output;
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username;
response.setContentType("text/html;charset=gb2312");
username=request.getParameter("username");
output=response.getWriter();
try {
//为了突出并发问题,在这设置一个延时
Thread.sleep(10000);
output.println("用户名:"+username+"<BR>");
} catch (Exception e) {
e.printStackTrace();
}
}

编辑如上代码,当每次访问时都输出当前访问对象的username。但是当我们使用不同的浏览器同时访问时,错误出现了:

在同一个用户操作的浏览器上连续输出了两个username,虽然都为null,但是这也是不安全的。

怎么造成的呢?

这是因为在其中一个用户操作的时候,其他用户也可以访问servlet对象,当第二个用户还在操作的时候第一个用户操作完成,这就造成了原本是输出到b用户浏览器上的数据被a用户获取。这是很典型的无法同步线程的读操作,在我之前的JVM里面多线程访问内存数据也有讲到,当时讲的是有两种解决办法。

Servlet为了解决同步问题,才去的思路与JVM是很类似的。

首先第一种方法是:给被访问的对象加锁,途径是实现SingleThreadModel,即单线程模式。

来看下SingleThreadModel的官方定义和解释:

也就是说当Servlet实现了这个方法,那么程序保证在当前只能有一个此Servlet的对象在运行,也就不存在读的竞争关系。但是这个方法并没有完全解决所有的线程问题,比如说session的attribute以及静态变量仍然可以被多请求获得,获得同一时间的多个线程获得。也就是说虽然这个对象被保证了同一时间只能有一个,但是session的attribute是共享的,静态变量存在方法区,也是可以被获得并且可能存在安全读取的隐患的(必然)。所以这个接口也不再被建议使用。


第二种方式是:同步共享数据(加锁)

对要被获取的对象加锁,保证同一时间只能有一个线程访问这个对象。关键字Synchronized。

其中的代码就变成了这个样子:

username=request.getParameter("username");
synchronized(this){
output=response.getWriter();
try {
//为了突出并发问题,在这设置一个延时
Thread.sleep(5000);
output.println("用户名:"+username+"<BR>");
} catch (Exception e) {
e.printStackTrace();

把response这个对象加锁,保证在同一时间只能有一个对象能操作response及其操作。

第三种方式是:使用局部变量而不是实例变量

 在刚才写的Servlet类中,因为我们使用两个不同的线程去获取request对象(servlet的内置对象),并且打印出每个user的username,所以造成了输出的错误。

如果把request对象写在方法体中,那么就变成:

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String username;
PrintWriter output;

运行了几次没有出现错误。

把每个线程的休眠时间增加。

Thread.sleep(10000);

连续运行。

自动提示当前的request对象还没有释放,没有结束。问题解决。

究竟最后的两种方法哪种更好呢?思考一下。

一种是同步我们的要操作的对象;一种是使用局部对象。

个人感觉还是局部对象更胜一筹,因为我们不能每次都确切的知道需要同步的对象,而且需要手动的去添加同步方法,相比之下使用局部对象就方便的多了。

如果程序必须使用全局变量时,我们还有同步方法可以使用,不是吗?总会有办法的。

ps:

类变量是类中独立于方法之外的变量,用static 修饰。
实例变量也是独立于方法之外的变量,没用static修饰,也就是对着对象实例化而初始化。
方法变量时在方法之内声明的变量,生命周期与方法一致。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值