本内容摘自《Java Web程序设计基础教程》
第13章 系统安全控制
|
目标:
u 掌握使用登录功能对系统的用户进行认证;
u 掌握如何控制只有具有相应权限的用户才可以访问相应的资源;
u 掌握如何控制页面中的部分功能只能在特定的情况下才能使用;
u 掌握如何对多个文件的安全同时进行控制;
u 了解验证码的工作原理。
网站的很多功能是普通用户不能访问的,例如添加图书这样的功能只能由管理员完成,普通用户是无法访问的,如果普通用户要访问,应该提示普通用户没有相应的权限。本章介绍如何控制用户的权限。
13.1 完善登录功能
第4、5、6章中介绍的登录功能只是简单的模拟,现在对该功能进行完善。
13.1.1 功能描述
用户在登录界面输入用户ID和口令,系统根据用户输入的用户ID和口令进行处理:
l 用户ID和口令都正确,并且当前用户是管理员,则跳转到管理员的默认界面。
l 如果用户ID和口令正确,并且是普通用户,则跳转到普通用户的默认界面。
l 如果用户ID和口令不正确,则重新转向登录界面。
不管是管理员还是普通用户,登录之后的默认界面应该是用户使用频率最高的界面。如果网站比较智能,可以对用户的访问进行统计,为用户显示经常使用的界面,或者上次访问的界面。对于管理员来说,使用频率最高的功能应该是订单管理,所以应该把订单管理作为管理员的默认界面。对于普通用户来说,使用频率最高的功能应该是图书查看,所以应该把图书查看界面作为普通用户的默认界面。
13.1.2 思路分析
仍然采用MVC模式来设计该功能。
首先是M部分,可以使用UesrBean中原有的方法findUserById。
其次是V部分,登录界面在前面已经完成,图书查看功能是读者在前面实训中完成的功能,假设为findBooks,订单管理是下一章将要完成的实训内容,假设为findOrders。
最后是C部分,获取用户输入的用户ID和口令,根据ID调用模型的findUserById方法,根据返回值进行处理:
u 如果返回值为null,则说明用户不存在;
u 否则,判断口令是否匹配:
n 如果不匹配,说明口令不正确;
n 如果匹配,判断用户的类型:
u 如果用户类型为1,说明该用户为管理员;
u 否则为普通用户。
13.1.3 修改登录控制器Servlet
根据上面的分析,修改后的控制器的代码如下:
package bookstore.servlet;
import javax.servlet.http.*;
import java.io.*;
import bookstore.bean.*;
public class LoginServlet extends HttpServlet
{
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException,ServletException {
// 获取用户提交的信息
String userid = request.getParameter("userid");
String userpass = request.getParameter("userpass");
// 创建JavaBean对象
UserBean ub = new UserBean();
// 转向的文件
String forward=null;
// 提示信息
String info=null;
UserBean user=ub.findUserById(userid);
// 用户不存在
if(user == null){
forward="login.jsp";
}
// 口令不正确
else if(!user.getUserpass().equals(userpass)){
forward="login.jsp";
}
// 管理员
else if(user.getType().equals("1")){
forward="findOrders";
}
// 普通用户
else{
forward="findBooks";
}
// 定义跳转文件
RequestDispatcher rd=request.getRequestDispatcher(forward);
// 完成重定向
rd.forward(request,response);
}
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException,ServletException {
doGet(request,response);
}
}
13.2 用户管理的安全控制
13.2.1 功能描述
只有管理员可以使用图书添加功能,如果用户没有登录,提示用户“您还没有登录,请先登录!”,如果当前用户是普通用户,提示用户“您不是管理员,不能添加用户”。如果当前用户是管理员,则显示添加界面。
13.2.2 运行效果
首先,直接访问图书添加的页面,得到如图13.1所示界面。
图13.1 没有登录的提示界面 图13.2 没有权限的提示界面
然后,点击“登录”超链接跳转到登录界面,输入普通用户的ID和口令,然后再访问图书添加界面,得到如图13.2所示界面。
然后,点击“登录”超链接跳转到登录界面,输入管理员用户ID和口令,然后再访问图书添加界面,得到如图9.1所示的用户添加界面。
13.2.3 思路分析
要防止这个文件被没有权限的人访问,必须对每个访问这个文件的人的身份进行验证,要想知道当前用户的权限,在登录的时候必须记录当前用户的权限。
在访问这个文件的时候,首先应该从session中获取用户信息,如果用户不存在,跳转到“没有登录”的提示界面,如果用户是普通用户,跳转到“没有权限”的提示界面,不让用户访问当前页面,这样就保证了当前页面的安全。
为了方便用户的操作,需要在信息提示页面增加到登录页面的超链接。
13.2.4 操作session
使用session对象能够在多个页面之间共享信息,在Servlet中也可以使用session对象,但是与在JSP中不同,在JSP中session对象是内部对象,在Servlet中需要先获取session对象。要完成安全控制需要使用session对象保存用户信息,下面介绍关于session的常用操作。
获取session对象
session对象存储在request对象中,request对象也就是服务类方法的第一个参数,可以使用HttpServletRequest的下面的方法来获取session对象:
HttpSession getSession(boolean);
通常是在doGet方法或者doPost方法中,通过第一个参数request来获取。代码如下:
HttpSession session;
session =request.getSession(true);
在session中存储信息
获取session对象之后,如果希望在session中存储信息,可以使用下面的方法:
session.setAttribute("name",value);
用于在session中保存信息,有两个参数,第一个参数是字符串,是要存储信息的名字,第二个参数是保存的值本身,可以是各种类型的对象。如果第一个参数所指定的名字存在,会替换该名字原来对应的值;如果第一个参数所指定的名字不存在,会把第二个参数指定的值存储到session中。
从session中获取信息
如果要从session中获取已经保存的信息,可以使用下面的方法:
session.getAttribute("name");
参数就是要获取的信息的名字。在使用的时候对得到的信息必须进行强制类型转换,因为方法的返回值类型是Object。例如在session中存储的是String对象,要得到这个对象,可以使用下面的代码:
String str = (String)session.getAttribute("name");
删除session中存储的信息
如果要删除session中的某个信息,可以使用下面的方法进行删除:
session.remove("name");
参数就是要删除的session中的对象的名字。
使session失效
如果不想使用session了,可以使用下面的方法:
session.invalidate()
使用该方法之后,就不能继续使用session了,包括session中的所有信息。
13.2.5 修改登录处理的代码
在登录成功之后,把用户信息保存到session中。因为这个信息是与特定用户相关的,在用户的整个访问过程中都有效。
在Servlet中首先要获取session对象,在用户登录成功之后,把用户对象保存到session中。修改后的Servlet的代码如下:
package bookstore.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import bookstore.bean.*;
public class LoginServlet extends HttpServlet
{
public void doGet(HttpServletRequest request,HttpServletResponse response)
throws IOException,ServletException {
// 获取用户提交的信息
String userid = request.getParameter("userid");
String userpass = request.getParameter("userpass");
// 创建JavaBean对象
UserBean ub = new UserBean();
// 转向的文件
String forward=null;
// 提示信息
String info=null;
// 得到session对象
HttpSession session;
session =request.getSession(true);
UserBean user=ub.findUserById(userid);
// 用户不存在
if(user == null){
forward="login.jsp";
}
// 口令不正确
else if(!user.getUserpass().equals(userpass)){
forward="login.jsp";
}
// 管理员
else if(user.getType().equals("1")){
session.setAttribute("login",user);
forward="findOrders";
}
// 普通用户
else{
session.setAttribute("login",user);
forward="findBooks";
}
// 定义跳转文件
RequestDispatcher rd=request.getRequestDispatcher(forward);
// 完成重定向
rd.forward(request,response);
}
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws IOException,ServletException {
doGet(request,response);
}
}
13.2.6 在用户添加界面增加控制
在原来的添加界面的代码中,增加对用户是否登录已经是否有相应的权限的判断。判断过程如下:
u 从session中获取“login”对象,如果不存在表示没有登录,转向需要登录的提示界面;
u 如果用户存在,并且是普通用户,转向没有权限的提示界面;
u 如果是管理员,继续向下执行即可。
用于判断的代码如下:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!--判断session中是否存在login-->
<c:if test="${empty login}">
<!--跳转到需要登录的提示界面-- >
<jsp:forward page="needlogin.jsp"/>
</c:if>
<!--判断session中用户是否是管理员-->
<c:if test="${login.type==0}">
<!--跳转到没有权限的提示界面-->
<jsp:forward page="noright.jsp"/>
</c:if>
把这段代码添加到addUser.jsp页面的前面即可。
13.2.6 对控制器进行安全控制
上面完成了对添加用户的界面的安全控制,这样如果不是管理员就不能访问这个界面了。好像已经完成了安全控制,事实是不是这样呢?
添加用户的过程是:在添加界面输入用户信息,提交给控制器处理,控制器调用业务方法完成添加过程。如果用户直接访问控制器怎么办?因为控制器是Servlet,是允许访问的。
有人会说虽然可以访问,但是没有用户信息,仍然不能添加。实际上,可以通过问号传递参数,效果与表单提交完全相同。在前面讲分页显示的时候,页码就是通过问号传递的。所以要想保证用户添加功能的安全,还需要对控制器进行安全控制。
在控制器中进行安全控制,主要的过程:
u 从session中获取登录信息
u 然后对登录信息进行判断:
n 如果为null,说明用户没有登录,跳转到“需要登录”的提示界面;
n 如果不为