实现NetCTOSS项目的登录功能,要求在登录页面上输入账号、密码后,点击登录则自动校验登录信息是否正确,若校验成功则跳转至系统首页index.jsp,否则跳转回登录页并给予错误的提示信息。
本案例中,只要求打开登录页面即可。
由于打开登录页面不需要任何业务,只是把请求转发给JSP即可,因此无需增加新的Action,只需要在struts.xml中对请求加以配置即可。
步骤一:struts.xml中,配置访问登录页的请求action
由于登录功能与资费功能无关,是一个新的模块,因此在struts.xml中追加一个新的package用于封装登录模块的配置信息,该package以模块名login命名。在该package下,配置当前请求对应的action,此action由于没有业务逻辑,因此不必指定class属性,Struts2会自动实例化默认的Action类ActionSupport,该类中存在方法execute,该方法返回success。另外此action也不需要配置method,当不配置此属性时,Struts2会默认调用execute方法。代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!—此处略去其他Action的配置... --> </package> #cold_bold <!--登录模块 --> #cold_bold <package name="login" namespace="/login" extends="struts-default"> #cold_bold <!-- #cold_bold 打开登录页面: #cold_bold 1、action的class属性可以省略,省略时Struts2 #cold_bold 会自动实例化默认的Action类ActionSupport, #cold_bold 该类中有默认业务方法execute,返回success。 #cold_bold 2、action的method属性可以省略,省略时Struts2 #cold_bold 会自动调用execute方法。 #cold_bold --> #cold_bold <action name="toLogin"> #cold_bold <result name="success"> #cold_bold /WEB-INF/main/login.jsp #cold_bold </result> #cold_bold </action> </package> </struts>
步骤二:增加登录页面
在WEB-INF/main/下创建登录页面login.jsp,先在该页面上指定其编码类型为utf-8,然后将静态页面中的代码复制到此页面中。再修改页面上引用的样式文件以及图片的路径,最后将页面默认的一些错误提示信息删除。代码如下:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <div class="login_box"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"><input name="" type="text" class="width150" /></td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"><input name="" type="password" class="width150" /></td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> <td><img src="../images/valicode.jpg" alt="验证码" title="点击更换" /></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="index.html"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"></span></td> </tr> </table> </div> </body> </html>
步骤三:测试
重新部署项目NETCTOSS并启动tomcat,然后访问NETCTOSS,路径为http://localhost:8088/NETCTOSS/login/toLogin,效果如下:
图-1
本案例的完整代码如下。
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!-- 查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!-- 正常情况下跳转到资费列表页面。 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。 --> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> <!-- 错误情况下,跳转到错误页面。 错误页面可以被所有模块复用,因此放在main下, 该文件夹用于存放公用的页面。 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> <!-- 登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 打开登录页面: 1、action的class属性可以省略,省略时Struts2 会自动实例化默认的Action类ActionSupport, 该类中有默认业务方法execute,返回success。 2、action的method属性可以省略,省略时Struts2 会自动调用execute方法。 --> <action name="toLogin"> <result name="success"> /WEB-INF/main/login.jsp </result> </action> </package> </struts>
login.jsp完整代码:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <div class="login_box"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"><input name="" type="text" class="width150" /></td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"><input name="" type="password" class="width150" /></td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> <td><img src="../images/valicode.jpg" alt="验证码" title="点击更换" /></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="index.html"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"></span></td> </tr> </table> </div> </body> </html>
完成NetCTOSS登录验证。
登录页面上,输入完账号、密码后,点击登录按钮时触发登录验证,该请求的过程如下图:
图-2
图中红色字体为需要开发的代码,其中最重要的是要在Action中写校验逻辑,具体校验逻辑如下图:
图-3
实现此案例需要按照如下步骤进行。
步骤一:创建管理员实体类
在com.netctoss.entity包下,创建管理员实体类Admin,用于封装管理员信息表admin_info中的数据,代码如下:
package com.netctoss.entity; import java.sql.Date; /** * 管理员实体类 */ public class Admin { private Integer id;// 主键 private String adminCode;// 账号 private String password;// 密码 private String name;// 姓名 private String telephone;// 电话 private String email;// 邮件 private Date enrollDate;// 创建日期 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAdminCode() { return adminCode; } public void setAdminCode(String adminCode) { this.adminCode = adminCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getEnrollDate() { return enrollDate; } public void setEnrollDate(Date enrollDate) { this.enrollDate = enrollDate; } }
步骤二:创建登录模块DAO,追加根据账号查询管理员的方法
在com.netctoss.dao包下,创建登录DAO的接口ILoginDao,并增加根据账号查询管理员的方法,代码如下:
package com.netctoss.dao; import com.netctoss.entity.Admin; /** * 登录模块DAO */ public interface ILoginDao { /** * 根据管理员账号查询管理员 * @param code 账号 * @return */ Admin findByCode(String code); }
在com.netctoss.dao包下,创建ILoginDao接口的实现类LoginDaoImpl,并实现根据账号查询管理员的方法,代码如下:
package com.netctoss.dao; import com.netctoss.entity.Admin; /** * 当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。 * 同学们可以使用JDBC/MyBatis自行实现该DAO。 */ public class LoginDaoImpl implements ILoginDao { @Override public Admin findByCode(String code) { // 模拟根据账号查询管理员信息 Admin a = new Admin(); a.setId(1); a.setAdminCode("tarena"); a.setPassword("123"); a.setName("达内"); a.setTelephone("110"); a.setEmail("tarena@tarena.com.cn"); return a; } }
为了方便后续Action访问此DAO,将该DAO的实例化封装在DAOFactory中代码如下:
package com.netctoss.dao; /** * DAO工厂,负责统一的创建DAO示例。 */ public class DAOFactory { /** * 资费DAO实例 */ private static ICostDao costDao = new CostDaoImpl(); #cold_bold /** #cold_bold * 登录DAO实例 #cold_bold */ #cold_bold private static ILoginDao loginDao = new LoginDaoImpl(); /** * 返回资费DAO实例 */ public static ICostDao getCostDAO() { return costDao; } #cold_bold /** #cold_bold * 返回登录DAO实例 #cold_bold */ #cold_bold public static ILoginDao getLoginDAO() { #cold_bold return loginDao; #cold_bold } }
步骤四:创建登录校验Action
在com.netctoss.action包下,创建Action的父类BaseAction,用于封装Action公用的方法,当前要将获取session的方法封装在该类中,以便于复用。代码如下:
package com.netctoss.action; import java.util.Map; import org.apache.struts2.interceptor.SessionAware; /** * Action基类,用于封装Action通用的方法。 */ public class BaseAction implements SessionAware { protected Map<String, Object> session; /* * 采用接口注入的方式统一获取session */ @Override public void setSession(Map<String, Object> arg0) { session = arg0; } }
在com.netctoss.action包下,创建登录校验Action类LoginAction,继承于BaseAction。在该Action的业务方法中,根据传入的账号及密码,调用登录DAO校验用户登录是否成功,成功时将登录信息记录到session中,并跳转至系统首页,否则跳转回登录页,并给予错误提示。代码如下:
package com.netctoss.action; import com.netctoss.dao.DAOFactory; import com.netctoss.dao.ILoginDao; import com.netctoss.entity.Admin; /** * 登录校验Action */ public class LoginAction extends BaseAction { // input private String adminCode;// 账号 private String password;// 密码 // output private String errorMsg;// 错误信息 public String execute() { ILoginDao dao = DAOFactory.getLoginDAO(); Admin admin = null; try { // 根据账号查询管理员 admin = dao.findByCode(adminCode); } catch (Exception e) { e.printStackTrace(); return "error"; } if (admin == null) { // 如果管理员为空,则说明账号有误,校验失败 errorMsg = "账号不存在."; return "fail"; } else { // 如果管理员不为空,进一步校验密码 if (password != null &&password.equals(admin.getPassword())) { // 如果密码一致,校验成功 session.put("admin", admin); return "success"; } else { // 如果密码不一致,校验失败 errorMsg = "密码有误."; return "fail"; } } } public String getAdminCode() { return adminCode; } public void setAdminCode(String adminCode) { this.adminCode = adminCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } }
步骤五:在struts.xml中配置校验Action
在struts.xml中配置登录校验action,登录成功则转至系统首页,否则转至登录页,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!--查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!—此处略去其他Action的配置... --> </action> </package> <!--登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!—此处略去其他Action的配置... --> #cold_bold <!--登录校验 --> #cold_bold <action name="login" class="com.netctoss.action.LoginAction"> #cold_bold <!--校验成功,跳转到系统首页 --> #cold_bold <result name="success"> #cold_bold /WEB-INF/main/index.jsp #cold_bold </result> #cold_bold <!--登录失败,跳转回登录页面 --> #cold_bold <result name="fail"> #cold_bold /WEB-INF/main/login.jsp #cold_bold </result> #cold_bold <!--报错,跳转到错误页面 --> #cold_bold <result name="error"> #cold_bold /WEB-INF/main/error.jsp #cold_bold </result> #cold_bold </action> </package> </struts>
步骤六:创建系统首页
在WEB-INF/main下创建系统首页index.jsp,首先设置其编码为utf-8,然后从静态页面中将HTML代码复制到此页面上,最后修改样式引用的路径。代码如下:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <!--导航区域开始--> <div id="index_navi"> <ul id="menu"> <li><a href="index.html" class="index_on"></a></li> <li><a href="role/role_list.html" class="role_off"></a></li> <li><a href="admin/admin_list.html" class="admin_off"></a></li> <li><a href="fee/fee_list.html" class="fee_off"></a></li> <li><a href="account/account_list.html" class="account_off"></a></li> <li><a href="service/service_list.html" class="service_off"></a></li> <li><a href="bill/bill_list.html" class="bill_off"></a></li> <li><a href="report/report_list.html" class="report_off"></a></li> <li><a href="user/user_info.html" class="information_off"></a></li> <li><a href="user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> </body> </html>
步骤七:在登录页面上设置表单项
在login.jsp中,追加表单设置,将账号及密码放在表单内,并且表单提交路径设置为登录action。然后对账号、密码文本框的name进行设置,使用基本属性注入的方式将其值注入给登录Action,并给登录按钮图片设置提交事件。最后使用Struts2显示标签+OGNL表达式,在页面上输出错误信息。代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <div class="login_box"> #cold_bold <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> #cold_bold <td colspan="2"><input name="adminCode" type="text" class="width150" /></td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> #cold_bold <td colspan="2"><input name="password" type="password" class="width150" /></td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> <td><img src="../images/valicode.jpg" alt="验证码" title="点击更换" /></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> #cold_bold <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> #cold_bold <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
步骤八:测试
部署项目NETCTOSS并重新启动tomcat,访问NETCTOSS登录页,在页面上输入正确的账号及密码,点击登录后,效果如下图:
图-4
后退,在页面上输入错误的账号或密码,点击登录后,效果如下图:
图-5
从上图可以看出,校验的结果满足要求,但是由于页面的刷新,导致文本框中原先输入的内容丢失了,应该予以保留才对。实际上只需要在文本框中追加value属性,并将Action中的数据设置给它既可,因此需要对login.jsp进行完善。
步骤九:完善
在login.jsp上,给文本框设置默认值,以便登录失败回到登录页时,无法保留住刚刚输入的账号及密码,代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <div class="login_box"> <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"> #cold_bold <input name="adminCode" type="text" class="width150" #cold_bold value="<s:property value="adminCode"/>"/> </td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"> #cold_bold <input name="password" type="password" class="width150" #cold_bold value="<s:property value="password"/>"/> </td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> <td><img src="../images/valicode.jpg" alt="验证码" title="点击更换" /></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
完善后,刷新地址栏,重新输入错误的账号或密码后,点击登录,效果如下图:
图-6
注意:实际上Struts2有更好的办法实现上述显示默认值(步骤九)的逻辑,即使用Struts2的UI标签。但目前还没有讲到这些内容,因此暂且这样写,以后可以进行重构。希望大家理解这一点,实际项目中不要写出这样设置默认值的代码。
本案例的完整代码如下。
实体类Admin完整代码:
package com.netctoss.entity; import java.sql.Date; /** * 管理员实体类 */ public class Admin { private Integer id;// 主键 private String adminCode;// 账号 private String password;// 密码 private String name;// 姓名 private String telephone;// 电话 private String email;// 邮件 private Date enrollDate;// 创建日期 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getAdminCode() { return adminCode; } public void setAdminCode(String adminCode) { this.adminCode = adminCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getTelephone() { return telephone; } public void setTelephone(String telephone) { this.telephone = telephone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getEnrollDate() { return enrollDate; } public void setEnrollDate(Date enrollDate) { this.enrollDate = enrollDate; } }
ILoginDao完整代码:
package com.netctoss.dao; import com.netctoss.entity.Admin; /** * 登录模块DAO */ public interface ILoginDao { /** * 根据管理员账号查询管理员 * @param code 账号 * @return */ Admin findByCode(String code); }
LoginDaoImpl完整代码:
package com.netctoss.dao; import com.netctoss.entity.Admin; /** * 当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。 * 同学们可以使用JDBC/MyBatis自行实现该DAO。 */ public class LoginDaoImpl implements ILoginDao { @Override public Admin findByCode(String code) { // 模拟根据账号查询管理员信息 Admin a = new Admin(); a.setId(1); a.setAdminCode("tarena"); a.setPassword("123"); a.setName("达内"); a.setTelephone("110"); a.setEmail("tarena@tarena.com.cn"); return a; } }
DAOFactory完整代码:
package com.netctoss.dao; /** * DAO工厂,负责统一的创建DAO示例。 */ public class DAOFactory { /** * 资费DAO实例 */ private static ICostDao costDao = new CostDaoImpl(); /** * 登录DAO实例 */ private static ILoginDao loginDao = new LoginDaoImpl(); /** * 返回资费DAO实例 */ public static ICostDao getCostDAO() { return costDao; } /** * 返回登录DAO实例 */ public static ILoginDao getLoginDAO() { return loginDao; } }
BaseAction完整代码:
package com.netctoss.action; import java.util.Map; import org.apache.struts2.interceptor.SessionAware; /** * Action基类,用于封装Action通用的方法。 */ public class BaseAction implements SessionAware { protected Map<String, Object> session; /* * 采用接口注入的方式统一获取session */ @Override public void setSession(Map<String, Object> arg0) { session = arg0; } }
LoginAction完整代码:
package com.netctoss.action; import com.netctoss.dao.DAOFactory; import com.netctoss.dao.ILoginDao; import com.netctoss.entity.Admin; /** * 登录校验Action */ public class LoginAction extends BaseAction { // input private String adminCode;// 账号 private String password;// 密码 // output private String errorMsg;// 错误信息 public String execute() { ILoginDao dao = DAOFactory.getLoginDAO(); Admin admin = null; try { // 根据账号查询管理员 admin = dao.findByCode(adminCode); } catch (Exception e) { e.printStackTrace(); return "error"; } if (admin == null) { // 如果管理员为空,则说明账号有误,校验失败 errorMsg = "账号不存在."; return "fail"; } else { // 如果管理员不为空,进一步校验密码 if (password != null &&password.equals(admin.getPassword())) { // 如果密码一致,校验成功 session.put("admin", admin); return "success"; } else { // 如果密码不一致,校验失败 errorMsg = "密码有误."; return "fail"; } } } public String getAdminCode() { return adminCode; } public void setAdminCode(String adminCode) { this.adminCode = adminCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } }
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!-- 查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!-- 正常情况下跳转到资费列表页面。 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。 --> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> <!-- 错误情况下,跳转到错误页面。 错误页面可以被所有模块复用,因此放在main下, 该文件夹用于存放公用的页面。 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> <!-- 登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 打开登录页面: 1、action的class属性可以省略,省略时Struts2 会自动实例化默认的Action类ActionSupport, 该类中有默认业务方法execute,返回success。 2、action的method属性可以省略,省略时Struts2 会自动调用execute方法。 --> <action name="toLogin"> <result name="success"> /WEB-INF/main/login.jsp </result> </action> <!-- 登录校验 --> <action name="login" class="com.netctoss.action.LoginAction"> <!-- 校验成功,跳转到系统首页 --> <result name="success"> /WEB-INF/main/index.jsp </result> <!-- 登录失败,跳转回登录页面 --> <result name="fail"> /WEB-INF/main/login.jsp </result> <!-- 报错,跳转到错误页面 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> </struts>
index.jsp完整代码:
<%@page pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <!--导航区域开始--> <div id="index_navi"> <ul id="menu"> <li><a href="index.html" class="index_on"></a></li> <li><a href="role/role_list.html" class="role_off"></a></li> <li><a href="admin/admin_list.html" class="admin_off"></a></li> <li><a href="fee/fee_list.html" class="fee_off"></a></li> <li><a href="account/account_list.html" class="account_off"></a></li> <li><a href="service/service_list.html" class="service_off"></a></li> <li><a href="bill/bill_list.html" class="bill_off"></a></li> <li><a href="report/report_list.html" class="report_off"></a></li> <li><a href="user/user_info.html" class="information_off"></a></li> <li><a href="user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> </body> </html>
login.jsp完整代码:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> </head> <body class="index"> <div class="login_box"> <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"> <input name="adminCode" type="text" class="width150" value="<s:property value="adminCode"/>"/> </td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"> <input name="password" type="password" class="width150" value="<s:property value="password"/>"/> </td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> <td><img src="../images/valicode.jpg" alt="验证码" title="点击更换" /></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
NetCTOSS登录的案例中,提交时只考虑了账号、密码,没有考虑验证码,那么本案例中,要求给登录功能追加生成验证码的功能,以及在登录时要对验证码进行校验。
本案例中,完成在登录页面上生成验证码图片即可。
我们可以在服务端动态生成验证码图片,然后使用stream类型的result将图片输出给页面img元素。
在生成图片后,需要将图片中的文本记录到session,以便后续提交登录时,方便对验证码的校验。
实现此案例需要按照如下步骤进行。
步骤一:创建生成图片工具类
在com.netctoss.util包下,创建生成图片工具类ImageUtil,并提供创建图片的代码,以及将图片转换成输入流的代码,代码如下:
package com.netctoss.util; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Random; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; /** * 生成验证码图片工具类 */ public final class ImageUtil { private static final char[] chars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' }; private static final int SIZE = 4; private static final int LINES = 5; private static final int WIDTH = 80; private static final int HEIGHT = 40; private static final int FONT_SIZE = 30; /** * 生成验证码图片,封装与Map中。 * 其中Map的key是验证码,Map的value是验证码图片。 */ public static Map<String, BufferedImage> createImage() { StringBuffer sb = new StringBuffer(); BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics graphic = image.getGraphics(); graphic.setColor(Color.LIGHT_GRAY); graphic.fillRect(0, 0, WIDTH, HEIGHT); Random ran = new Random(); // 画随机字符 for (int i = 1; i <= SIZE; i++) { int r = ran.nextInt(chars.length); graphic.setColor(getRandomColor()); graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE)); graphic.drawString(chars[r] + "", (i - 1) * WIDTH / SIZE, HEIGHT / 2); sb.append(chars[r]);// 将字符保存,存入Session } // 画干扰线 for (int i = 1; i <= LINES; i++) { graphic.setColor(getRandomColor()); graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT), ran.nextInt(WIDTH), ran.nextInt(HEIGHT)); } Map<String, BufferedImage> map = new HashMap<String, BufferedImage>(); map.put(sb.toString(), image); return map; } /** * 将图片转换为输入流 */ public static InputStream getInputStream(BufferedImage image) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos); encoder.encode(image); byte[] imageBts = bos.toByteArray(); InputStream in = new ByteArrayInputStream(imageBts); return in; } private static Color getRandomColor() { Random ran = new Random(); Color color = new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256)); return color; } }
步骤二:在Action中生成图片
在com.netctoss.action包下,创建CreateImageAction类,用于处理生成图片的请求,代码如下:
package com.netctoss.action; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.util.Map; import com.netctoss.util.ImageUtil; /** * 生成验证码图片Action */ public class CreateImageAction extends BaseAction { /** * 验证码图片输入流 */ private InputStream imageStream; public String execute() { // 创建验证码图片 Map<String, BufferedImage> imageMap = ImageUtil.createImage(); // 取出验证码,放入session String code = imageMap.keySet().iterator().next(); session.put("imageCode", code); // 取出图片 BufferedImage image = imageMap.get(code); try { // 将图片转变为输入流 imageStream = ImageUtil.getInputStream(image); } catch (IOException e) { e.printStackTrace(); return "error"; } return "success"; } public InputStream getImageStream() { return imageStream; } public void setImageStream(InputStream imageStream) { this.imageStream = imageStream; } }
步骤三:在struts.xml中配置action
在struts.xml中对此action进行配置,利用stream类型的result,将CreateImageAction中的输入流的内容输出给页面,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!—此处略去其他Action的配置... --> </package> <!--登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!—此处略去其他Action的配置... --> #cold_bold <!--生成验证码 --> #cold_bold <action name="createImage" class="com.netctoss.action.CreateImageAction"> #cold_bold <!--使用stream类型的result --> #cold_bold <result name="success" type="stream"> #cold_bold <!--指定输出的内容 --> #cold_bold <param name="inputName">imageStream</param> #cold_bold </result> #cold_bold </action> </package> </struts>
步骤四:登录页面上,修改图片路径
在登录页面login.jsp上,将img的src属性指定为生成验证码图片的action,即 createImage。并且为了每次点击该图片时都刷新为新验证码,给该img的单击事件绑定函数,在函数中刷新img的src属性,代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script type="text/javascript" language="javascript"> #cold_bold //刷新验证码图片 #cold_bold function change(image){ #cold_bold //改变img的src即可,由于该URL并没有变化,因此追加动态参数伪装成变化的URL。 #cold_bold image.src = "createImage?date=" + new Date().getTime(); #cold_bold } </script> </head> <body class="index"> <div class="login_box"> <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"> <input name="adminCode" type="text" class="width150" value="<s:property value="adminCode"/>"/> </td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"> <input name="password" type="password" class="width150" value="<s:property value="password"/>"/> </td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> #cold_bold <td><img src="createImage" alt="验证码" title="点击更换" onclick="change(this);"/></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
步骤五:测试
重新部署项目NETCTOSS并重启tomcat,访问登录页面,效果如下图:
图-7
本案例的完整代码如下。
ImageUtil完整代码:
package com.netctoss.util; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Random; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; /** * 生成验证码图片工具类 */ public final class ImageUtil { private static final char[] chars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I' }; private static final int SIZE = 4; private static final int LINES = 5; private static final int WIDTH = 80; private static final int HEIGHT = 40; private static final int FONT_SIZE = 30; /** * 生成验证码图片,封装与Map中。 * 其中Map的key是验证码,Map的value是验证码图片。 */ public static Map<String, BufferedImage> createImage() { StringBuffer sb = new StringBuffer(); BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics graphic = image.getGraphics(); graphic.setColor(Color.LIGHT_GRAY); graphic.fillRect(0, 0, WIDTH, HEIGHT); Random ran = new Random(); // 画随机字符 for (int i = 1; i <= SIZE; i++) { int r = ran.nextInt(chars.length); graphic.setColor(getRandomColor()); graphic.setFont(new Font(null, Font.BOLD + Font.ITALIC, FONT_SIZE)); graphic.drawString(chars[r] + "", (i - 1) * WIDTH / SIZE, HEIGHT / 2); sb.append(chars[r]);// 将字符保存,存入Session } // 画干扰线 for (int i = 1; i <= LINES; i++) { graphic.setColor(getRandomColor()); graphic.drawLine(ran.nextInt(WIDTH), ran.nextInt(HEIGHT), ran.nextInt(WIDTH), ran.nextInt(HEIGHT)); } Map<String, BufferedImage> map = new HashMap<String, BufferedImage>(); map.put(sb.toString(), image); return map; } /** * 将图片转换为输入流 */ public static InputStream getInputStream(BufferedImage image) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos); encoder.encode(image); byte[] imageBts = bos.toByteArray(); InputStream in = new ByteArrayInputStream(imageBts); return in; } private static Color getRandomColor() { Random ran = new Random(); Color color = new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256)); return color; } }
CreateImageAction完整代码:
package com.netctoss.action; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.InputStream; import java.util.Map; import com.netctoss.util.ImageUtil; /** * 生成验证码图片Action */ public class CreateImageAction extends BaseAction { /** * 验证码图片输入流 */ private InputStream imageStream; public String execute() { // 创建验证码图片 Map<String, BufferedImage> imageMap = ImageUtil.createImage(); // 取出验证码,放入session String code = imageMap.keySet().iterator().next(); session.put("imageCode", code); // 取出图片 BufferedImage image = imageMap.get(code); try { // 将图片转变为输入流 imageStream = ImageUtil.getInputStream(image); } catch (IOException e) { e.printStackTrace(); return "error"; } return "success"; } public InputStream getImageStream() { return imageStream; } public void setImageStream(InputStream imageStream) { this.imageStream = imageStream; } }
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!-- 查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!-- 正常情况下跳转到资费列表页面。 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。 --> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> <!-- 错误情况下,跳转到错误页面。 错误页面可以被所有模块复用,因此放在main下, 该文件夹用于存放公用的页面。 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> <!-- 登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 打开登录页面: 1、action的class属性可以省略,省略时Struts2 会自动实例化默认的Action类ActionSupport, 该类中有默认业务方法execute,返回success。 2、action的method属性可以省略,省略时Struts2 会自动调用execute方法。 --> <action name="toLogin"> <result name="success"> /WEB-INF/main/login.jsp </result> </action> <!-- 登录校验 --> <action name="login" class="com.netctoss.action.LoginAction"> <!-- 校验成功,跳转到系统首页 --> <result name="success"> /WEB-INF/main/index.jsp </result> <!-- 登录失败,跳转回登录页面 --> <result name="fail"> /WEB-INF/main/login.jsp </result> <!-- 报错,跳转到错误页面 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!-- 生成验证码 --> <action name="createImage" class="com.netctoss.action.CreateImageAction"> <!-- 使用stream类型的result --> <result name="success" type="stream"> <!-- 指定输出的内容 --> <param name="inputName">imageStream</param> </result> </action> </package> </struts>
login.jsp完整代码:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script type="text/javascript" language="javascript"> //刷新验证码图片 function change(image){ //改变img的src即可,由于该URL并没有变化,因此追加动态参数伪装成变化的URL。 image.src = "createImage?date=" + new Date().getTime(); } </script> </head> <body class="index"> <div class="login_box"> <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"> <input name="adminCode" type="text" class="width150" value="<s:property value="adminCode"/>"/> </td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"> <input name="password" type="password" class="width150" value="<s:property value="password"/>"/> </td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="" type="text" class="width70" /></td> <td><img src="createImage" alt="验证码" title="点击更换" onclick="change(this);"/></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
NetCTOSS登录时,追加对验证码的校验。
点击登录按钮提交表单时,在Action中先对验证码进行校验。
实现此案例需要按照如下步骤进行。
步骤一:Action中对验证码进行校验
在LoginAction中,追加输入属性验证码,并在业务方法中校验账号密码前对其进行校验,代码如下:
package com.netctoss.action; import com.netctoss.dao.DAOFactory; import com.netctoss.dao.ILoginDao; import com.netctoss.entity.Admin; /** * 登录校验Action */ public class LoginAction extends BaseAction { // input private String adminCode;// 账号 private String password;// 密码 #cold_bold private String verifyCode;// 验证码 // output private String errorMsg;// 错误信息 public String execute() { #cold_bold // 从session中取出生成的验证码 #cold_bold String imageCode = (String) session.get("imageCode"); #cold_bold // 验证用户输入的验证码是否与生成验证码一致 #cold_bold if(imageCode == null || !imageCode.equalsIgnoreCase(verifyCode)) { #cold_bold //如果不一致,提示错误 #cold_bold errorMsg = "验证码有误."; #cold_bold return "fail"; #cold_bold } // 此处略去登录验证逻辑... } public String getAdminCode() { return adminCode; } public void setAdminCode(String adminCode) { this.adminCode = adminCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } #cold_bold public String getVerifyCode() { #cold_bold return verifyCode; #cold_bold } #cold_bold #cold_bold public void setVerifyCode(String verifyCode) { #cold_bold this.verifyCode = verifyCode; #cold_bold } }
步骤二:JSP上设置表单项
在login.jsp上,设置验证码文本框的name属性值,使其与LoginAction中追加的属性名相同,代码如下:
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script type="text/javascript" language="javascript"> //刷新验证码图片 function change(image){ //改变img的src即可,由于该URL并没有变化,因此追加动态参数伪装成变化的URL。 image.src = "createImage?date=" + new Date().getTime(); } </script> </head> <body class="index"> <div class="login_box"> <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"> <input name="adminCode" type="text" class="width150" value="<s:property value="adminCode"/>"/> </td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"> <input name="password" type="password" class="width150" value="<s:property value="password"/>"/> </td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> #cold_bold <td class="width70"><input name="verifyCode" type="text" class="width70" /></td> <td><img src="createImage" alt="验证码" title="点击更换" onclick="change(this);"/></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
步骤三:测试
重新部署项目,然后访问登录页,输入错误的验证码后点击登录,效果如下图:
图-8
本案例的完整代码如下所示:
LoginAction完整代码 package com.netctoss.action; import com.netctoss.dao.DAOFactory; import com.netctoss.dao.ILoginDao; import com.netctoss.entity.Admin; /** * 登录校验Action */ public class LoginAction extends BaseAction { // input private String adminCode;// 账号 private String password;// 密码 private String verifyCode;// 验证码 // output private String errorMsg;// 错误信息 public String execute() { // 从session中取出生成的验证码 String imageCode = (String) session.get("imageCode"); // 验证用户输入的验证码是否与生成验证码一致 if(imageCode == null || !imageCode.equalsIgnoreCase(verifyCode)) { //如果不一致,提示错误 errorMsg = "验证码有误."; return "fail"; } ILoginDao dao = DAOFactory.getLoginDAO(); Admin admin = null; try { // 根据账号查询管理员 admin = dao.findByCode(adminCode); } catch (Exception e) { e.printStackTrace(); return "error"; } if (admin == null) { // 如果管理员为空,则说明账号有误,校验失败 errorMsg = "账号不存在."; return "fail"; } else { // 如果管理员不为空,进一步校验密码 if (password != null &&password.equals(admin.getPassword())) { // 如果密码一致,校验成功 session.put("admin", admin); return "success"; } else { // 如果密码不一致,校验失败 errorMsg = "密码有误."; return "fail"; } } } public String getAdminCode() { return adminCode; } public void setAdminCode(String adminCode) { this.adminCode = adminCode; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public String getVerifyCode() { return verifyCode; } public void setVerifyCode(String verifyCode) { this.verifyCode = verifyCode; } }
login.jsp完整代码
<%@page pageEncoding="utf-8"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script type="text/javascript" language="javascript"> //刷新验证码图片 function change(image){ //改变img的src即可,由于该URL并没有变化,因此追加动态参数伪装成变化的URL。 image.src = "createImage?date=" + new Date().getTime(); } </script> </head> <body class="index"> <div class="login_box"> <form action="login" method="post"> <table> <tr> <td class="login_info">账号:</td> <td colspan="2"> <input name="adminCode" type="text" class="width150" value="<s:property value="adminCode"/>"/> </td> <td class="login_error_info"><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">密码:</td> <td colspan="2"> <input name="password" type="password" class="width150" value="<s:property value="password"/>"/> </td> <td><span class="required">30长度的字母、数字和下划线</span></td> </tr> <tr> <td class="login_info">验证码:</td> <td class="width70"><input name="verifyCode" type="text" class="width70" /></td> <td><img src="createImage" alt="验证码" title="点击更换" onclick="change(this);"/></td> <td><span class="required"></span></td> </tr> <tr> <td></td> <td class="login_button" colspan="2"> <a href="javascript:document.forms[0].submit();"><img src="../images/login_btn.png" /></a> </td> <td><span class="required"><s:property value="errorMsg"/></span></td> </tr> </table> </form> </div> </body> </html>
增加资费的删除功能,用于删除一条资费数据。
点击删除按钮时,发起一次删除的请求,并传入资费ID。本次请求的Action负责调用DAO将数据删除,然后再将请求重定向给查询Action,从而实现列表页面的刷新。
步骤一:DAO中追加删除方法
在ICostDao中追加删除方法,参数为资费ID,代码如下:
package com.netctoss.dao; import java.util.List; import com.netctoss.entity.Cost; /** * 资费DAO接口 */ public interface ICostDao { /** * 查询全部资费数据 */ List<Cost>findAll(); #cold_bold /** #cold_bold * 删除一条资费数据 #cold_bold * @param id 主键 #cold_bold */ #cold_bold void delete(int id); }
在CostDaoImpl中,实现删除方法,代码如下:
package com.netctoss.dao; import java.util.ArrayList; import java.util.List; import com.netctoss.entity.Cost; /** * 当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。 * 同学们可以使用JDBC/MyBatis自行实现该DAO。 */ public class CostDaoImpl implements ICostDao { // 此处略去其他方法的实现... @Override public void delete(int id) { // 模拟根据id删除资费数据 System.out.println("删除ID为[" + id + "]的资费数据."); }
步骤二:Action中处理删除请求
在com.netctoss.action包下,创建删除资费Action类DeleteCostAction,并增加输入属性id,在业务方法中调用DAO的删除方法来删除该资费数据即可,代码如下:
package com.netctoss.action; import com.netctoss.dao.DAOFactory; import com.netctoss.dao.ICostDao; /** * 删除资费Action */ public class DeleteCostAction { // input private int id; public String execute() { ICostDao dao = DAOFactory.getCostDAO(); try { // 删除资费 dao.delete(id); } catch (Exception e) { e.printStackTrace(); return "error"; } return "success"; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
步骤三:struts.xml中配置删除action
在struts.xml中,资费package下配置删除资费action,删除成功时用 redirectAction类型的result将请求重定向给资费查询Action,代码如下:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!—此处略去其他Action的配置... --> #cold_bold <!--删除资费 --> #cold_bold <action name="deleteCost" #cold_bold class="com.netctoss.action.DeleteCostAction"> #cold_bold <!--删除完之后,重定向到查询action --> #cold_bold <result name="success" type="redirectAction"> #cold_bold findCost #cold_bold </result> #cold_bold <result name="error"> #cold_bold /WEB-INF/main/error.jsp #cold_bold </result> #cold_bold </action> </package> <!--登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!—此处略去其他Action的配置... --> </package> </struts>
步骤四:JSP中发起删除请求
在find_cost.jsp中,给删除按钮的事件绑定函数,并传入id值,在该函数中请求资费删除action,代码如下:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee(id) { var r = window.confirm("确定要删除此资费吗?"); if(r) { // 如果用户确认,则调用删除资费action window.location.href = "deleteCost?id="+id; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!--使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" /> #cold_bold <input type="button" value="删除" class="btn_delete" onclick="deleteFee(<s:property value="id"/>);" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <a href="#">上一页</a> <a href="#" class="current_page">1</a> <a href="#">2</a> <a href="#">3</a> <a href="#">4</a> <a href="#">5</a> <a href="#">下一页</a> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司</p> </div> </body> </html>
步骤五:测试
重新部署项目并重启tomcat,打开资费列表页面,点选任意资费数据的删除按钮,效果如下图:
图-9
点击确定后,该数据被删除,页面被刷新,效果如下图:
图-10
本案例的完整代码如下。
ICostDao完整代码:
package com.netctoss.dao; import java.util.List; import com.netctoss.entity.Cost; /** * 资费DAO接口 */ public interface ICostDao { /** * 查询全部资费数据 */ List<Cost>findAll(); /** * 删除一条资费数据 * @param id 主键 */ void delete(int id); }
CostDaoImpl完整代码:
package com.netctoss.dao; import java.util.ArrayList; import java.util.List; import com.netctoss.entity.Cost; /** * 当前阶段学习重点是Struts2,对于DAO的实现就模拟实现了。 * 同学们可以使用JDBC/MyBatis自行实现该DAO。 */ public class CostDaoImpl implements ICostDao { @Override public List<Cost> findAll() { // 模拟查询全部资费数据 List<Cost> list = new ArrayList<Cost>(); Cost c1 = new Cost(); c1.setId(95); c1.setName("6元套餐"); c1.setBaseDuration(66); c1.setBaseCost(6.6); c1.setUnitCost(0.6); c1.setDescr("6元套餐"); c1.setStatus("0"); c1.setCostType("2"); list.add(c1); Cost c2 = new Cost(); c2.setId(96); c2.setName("8元套餐"); c2.setBaseDuration(88); c2.setBaseCost(8.8); c2.setUnitCost(0.8); c2.setDescr("8元套餐"); c2.setStatus("0"); c2.setCostType("2"); list.add(c2); Cost c3 = new Cost(); c3.setId(97); c3.setName("tarena"); c3.setBaseDuration(99); c3.setBaseCost(9.9); c3.setUnitCost(0.9); c3.setDescr("tarena套餐"); c3.setStatus("0"); c3.setCostType("2"); list.add(c3); return list; } @Override public void delete(int id) { // 模拟根据id删除资费数据 System.out.println("删除ID为[" + id + "]的资费数据."); }
DeleteCostAction完整代码:
package com.netctoss.action; import com.netctoss.dao.DAOFactory; import com.netctoss.dao.ICostDao; /** * 删除资费Action */ public class DeleteCostAction { // input private int id; public String execute() { ICostDao dao = DAOFactory.getCostDAO(); try { // 删除资费 dao.delete(id); } catch (Exception e) { e.printStackTrace(); return "error"; } return "success"; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
struts.xml完整代码:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 资费模块配置信息: 一般情况下,一个模块的配置单独封装在一个package下, 并且以模块名来命名package的name和namespace。 --> <package name="cost" namespace="/cost" extends="struts-default"> <!-- 查询资费数据 --> <action name="findCost" class="com.netctoss.action.FindCostAction"> <!-- 正常情况下跳转到资费列表页面。 一般一个模块的页面要打包在一个文件夹下,并且文件夹以模块名命名。 --> <result name="success"> /WEB-INF/cost/find_cost.jsp </result> <!-- 错误情况下,跳转到错误页面。 错误页面可以被所有模块复用,因此放在main下, 该文件夹用于存放公用的页面。 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!-- 删除资费 --> <action name="deleteCost" class="com.netctoss.action.DeleteCostAction"> <!-- 删除完之后,重定向到查询action --> <result name="success" type="redirectAction"> findCost </result> <result name="error"> /WEB-INF/main/error.jsp </result> </action> </package> <!-- 登录模块 --> <package name="login" namespace="/login" extends="struts-default"> <!-- 打开登录页面: 1、action的class属性可以省略,省略时Struts2 会自动实例化默认的Action类ActionSupport, 该类中有默认业务方法execute,返回success。 2、action的method属性可以省略,省略时Struts2 会自动调用execute方法。 --> <action name="toLogin"> <result name="success"> /WEB-INF/main/login.jsp </result> </action> <!-- 登录校验 --> <action name="login" class="com.netctoss.action.LoginAction"> <!-- 校验成功,跳转到系统首页 --> <result name="success"> /WEB-INF/main/index.jsp </result> <!-- 登录失败,跳转回登录页面 --> <result name="fail"> /WEB-INF/main/login.jsp </result> <!-- 报错,跳转到错误页面 --> <result name="error"> /WEB-INF/main/error.jsp </result> </action> <!-- 生成验证码 --> <action name="createImage" class="com.netctoss.action.CreateImageAction"> <!-- 使用stream类型的result --> <result name="success" type="stream"> <!-- 指定输出的内容 --> <param name="inputName">imageStream</param> </result> </action> </package> </struts>
find_cost.jsp完整代码:
<%@page pageEncoding="utf-8" isELIgnored="false"%> <%@taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>达内-NetCTOSS</title> <link type="text/css" rel="stylesheet" media="all" href="../styles/global.css" /> <link type="text/css" rel="stylesheet" media="all" href="../styles/global_color.css" /> <script language="javascript" type="text/javascript"> //排序按钮的点击事件 function sort(btnObj) { if (btnObj.className == "sort_desc") btnObj.className = "sort_asc"; else btnObj.className = "sort_desc"; } //启用 function startFee() { var r = window.confirm("确定要启用此资费吗?资费启用后将不能修改和删除。"); document.getElementById("operate_result_info").style.display = "block"; } //删除 function deleteFee(id) { var r = window.confirm("确定要删除此资费吗?"); if(r) { // 如果用户确认,则调用删除资费action window.location.href = "deleteCost?id="+id; } } </script> </head> <body> <!--Logo区域开始--> <div id="header"> <img src="../images/logo.png" alt="logo" class="left"/> <a href="#">[退出]</a> </div> <!--Logo区域结束--> <!--导航区域开始--> <div id="navi"> <ul id="menu"> <li><a href="../index.html" class="index_off"></a></li> <li><a href="../role/role_list.html" class="role_off"></a></li> <li><a href="../admin/admin_list.html" class="admin_off"></a></li> <li><a href="../fee/fee_list.html" class="fee_on"></a></li> <li><a href="../account/account_list.html" class="account_off"></a></li> <li><a href="../service/service_list.html" class="service_off"></a></li> <li><a href="../bill/bill_list.html" class="bill_off"></a></li> <li><a href="../report/report_list.html" class="report_off"></a></li> <li><a href="../user/user_info.html" class="information_off"></a></li> <li><a href="../user/user_modi_pwd.html" class="password_off"></a></li> </ul> </div> <!--导航区域结束--> <!--主要区域开始--> <div id="main"> <form action="" method=""> <!--排序--> <div class="search_add"> <div> <!--<input type="button" value="月租" class="sort_asc" onclick="sort(this);" />--> <input type="button" value="基费" class="sort_asc" onclick="sort(this);" /> <input type="button" value="时长" class="sort_asc" onclick="sort(this);" /> </div> <input type="button" value="增加" class="btn_add" onclick="location.href='fee_add.html';" /> </div> <!--启用操作的操作提示--> <div id="operate_result_info" class="operate_success"> <img src="../images/close.png" onclick="this.parentNode.style.display='none';" /> 删除成功! </div> <!--数据区域:用表格展示数据--> <div id="data"> <table id="datalist"> <tr> <th>资费ID</th> <th class="width100">资费名称</th> <th>基本时长</th> <th>基本费用</th> <th>单位费用</th> <th>创建时间</th> <th>开通时间</th> <th class="width50">状态</th> <th class="width200"></th> </tr> <!-- 使用Struts2标签遍历集合,使用OGNL表达式输出内容。 --> <s:iterator value="costs"> <tr> <td><s:property value="id"/></td> <td><a href="fee_detail.html"><s:property value="name"/></a></td> <td><s:property value="baseDuration"/></td> <td><s:property value="baseCost"/></td> <td><s:property value="unitCost"/></td> <td><s:property value="createTime"/></td> <td><s:property value="startTime"/></td> <td> <s:if test="status==0">开通</s:if> <s:else>暂停</s:else> </td> <td> <input type="button" value="启用" class="btn_start" onclick="startFee();" /> <input type="button" value="修改" class="btn_modify" onclick="location.href='fee_modi.html';" /> <input type="button" value="删除" class="btn_delete" onclick="deleteFee(<s:property value="id"/>);" /> </td> </tr> </s:iterator> </table> <p>业务说明:<br /> 1、创建资费时,状态为暂停,记载创建时间;<br /> 2、暂停状态下,可修改,可删除;<br /> 3、开通后,记载开通时间,且开通后不能修改、不能再停用、也不能删除;<br /> 4、业务账号修改资费时,在下月底统一触发,修改其关联的资费ID(此触发动作由程序处理) </p> </div> <!--分页--> <div id="pages"> <a href="#">上一页</a> <a href="#" class="current_page">1</a> <a href="#">2</a> <a href="#">3</a> <a href="#">4</a> <a href="#">5</a> <a href="#">下一页</a> </div> </form> </div> <!--主要区域结束--> <div id="footer"> <p>[源自北美的技术,最优秀的师资,最真实的企业环境,最适用的实战项目]</p> <p>版权所有(C)加拿大达内IT培训集团公司 </p> </div> </body> </html>