队友就是你NB的时候还能让你冷静思考的那货;言归正传
1、放置路径:
Struts2默认的配置文件为struts.xml ,该文件需要存放在项目中的WEB-INF/classes文件下
2、启动方式:采用过滤器的方式启动:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.htm</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
3.具体的注解方式的配置:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<!-- 约定Action类执行完毕以后返回资源的结果路径,必须以 "/" 开头 -->
<constant name="struts.convention.result.path" value="/main,/members" />
<!-- 这个属性指定的是Strtus.xml中配置的 <package>节点的父节点 -->
<!-- <constant name="struts.convention.default.parent.package" value="admin-default" /> -->
<!-- 确定搜索包的路径。只要是结尾为action的包都要搜索 -->
<constant name="struts.convention.package.locators" value="action" />
<!-- 约定Action 类的项目根包 :值得注意的命名空间-->
<constant name="struts.convention.package.locators.basePackage" value="com.san.console.action" />
<package name="admin-default" extends="convention-default">
<interceptors>
<!--权限拦截器-->
<interceptor name="permissionInterceptor" class="com.san.console.interceptor.PermissionInterceptor" />
<interceptor-stack name="permitStack">
<interceptor-ref name="permissionInterceptor"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<!-- 一定要加上默认的拦截器 如果不写会被覆盖的 -->
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<!-- 配置struts2框架运行时,默认执行自定义拦截器栈 -->
<default-interceptor-ref name="permitStack" />
<!-- 全局返回值 -->
<global-results>
<!-- 登录页 -->
<result name="loginpage" type="redirect">/login.jsp</result>
<!-- 无权提示页 -->
<result name="nopermit" type="redirect">/main/common/nopermit.jsp</result>
<!-- 异常错误页 -->
<result name="allException">/main/common/error.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="allException" exception="java.lang.Exception" />
</global-exception-mappings>
</package>
</struts>
解释几点概念吧
<constant name="struts.convention.result.path" value="/main,/members" />
就是struts2返回页面的结果(页面)的结合。就是说返回页面也在main,或者members这两个文件夹下面;
还有一个权限拦截器:(在未来的某篇博客中我会重点解释下sessionId这个穿越APP和服务端的好东西)
public class PermissionInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation) throws Exception { Object action = invocation.getAction(); String classType = action.getClass().getName(); Class className = Class.forName(classType); Method method = action.getClass().getMethods()[0]; String res = ""; String reqActionName = invocation.getProxy().getActionName(); String actionname = invocation.getProxy().getActionName(); String methodname = invocation.getProxy().getMethod(); ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(ServletActionContext.getServletContext()); String actionDesc = LocalizedTextUtil.findDefaultText(className.getSimpleName()+"."+methodname, new Locale(ApplicationConfig.DEFAULT_LOCALE)); LogRecord logRecord = method.getAnnotation(LogRecord.class); if (!StringUtils.isNotBlank(actionDesc) && logRecord != null) { actionDesc = logRecord.actionDesc(); } if (action instanceof Protected) { Protected pro = (Protected) action; if (!pro.hasPermission(actionname, methodname)) { //return "nopermission";注释掉,先不使用 } if(StringUtils.equals(actionname, "systemlogin") && StringUtils.equals(methodname, "login")){//跳过登陆action return invocation.invoke(); }else{ //根据session获取用户相关的权限 ManageUser gmacUser = (ManageUser) Struts2Utils.getSession().getAttribute(WebConstant.SESSION_EMPLOYEE_BEAN); if(gmacUser==null){ return "loginpage"; } } } return invocation.invoke(); } }
4、最少依赖JAR包:
struts2-core-2.x.x.jar :Struts 2框架的核心类库
xwork-core-2.x.x.jar :XWork类库,Struts 2在其上构建
ognl-2.6.x.jar :对象图导航语言(Object Graph Navigation Language),struts2框架通过其读写对象的属性
freemarker-2.3.x.jar :Struts 2的UI标签的模板使用FreeMarker编写
commons-logging-1.x.x.jar :ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。
commons-fileupload-1.2.1.jar 文件上传组件,2.1.6版本后必须加入此文件;
5、案例分析
登录案例,只做路径和配置方面的介绍
5.1 登录页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@include file="/main/common/taglibs.jsp"%> <!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> <title>${ APP_NAME}</title> <%@include file="/main/common/meta.jsp"%> <link href="${ctx }/css/login.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="${ctx }/js/plugin/jquery.cookie.js"></script> <!-- 引用登录的js --> <script type="text/javascript" src="${ctx }/js/login.js"></script> </head> <body> <form action="#" method="post" name="loginForm"> <dl> <dd> <table border="0" cellpadding="4" cellspacing="0"> <tr> <td><div id="loading" style="position:absolute; text-align:center; display: none;"><img src="images/ajax-loader.gif" alt="登录中……" /> </div></td> <td colspan="2"><div id="msg" class="red" style="height:15px;"></div></td> </tr> <tr> <td align="right" width="40">用户名</td> <td align="left" width="198"><input name="userName" type="text" class="input_bg" id="name" /></td> <td rowspan="2" align="left"><img src="images/login_button.jpg" width="62" height="62" style="cursor: pointer;" onClick="javascript:login();"/></td> </tr> <tr> <td align="right">密 码</td> <td align="left"><input name="password" type="password" class="input_bg" id="pwd" /></td> </tr> <tr> <td> </td> <td align="left" valign="top"><input type="checkbox" name="rememberloginname" id="rememberloginname" /> 记住用户名</td> <td> </td> </tr> </table> </dd> </dl> </form> </body> </html> <script type="text/javascript"> //get cookie name if($.cookie('loginname')!=null){ $("#name").val($.cookie('loginname')); $("#rememberloginname").attr("checked",true); } </script>
5.2 login.js
// JavaScript Document if (self != top) { parent.location.href = webroot+"index.jsp"; } function showMsg(msg) { var obj = document.getElementById("msg"); obj.innerHTML=msg; } function vaildateForm() { var name = document.getElementById("name"); var pwd = document.getElementById("pwd"); var v = document.getElementById("vaildate"); if(name.value=='') { showMsg("帐号不能为空!"); name.focus(); return false; }else if(!(/^[A-Za-z]{1}[A-Za-z0-9\_]+$/.test(name.value) && name.value.length >=5 && name.value.length<=35)){ showMsg("帐号必须是由5~35位英文字母或数字组成的字符!"); name.focus(); return false; } if(pwd.value=='') { showMsg("密码不能为空!"); pwd.focus(); return false; } /*if(!/(?=^.{8,16}$)(?=(?:.*?\d){1})(?=.*[a-z])(?=(?:.*?[A-Z]){1})(?=(?:.*?[!@#$%*()_+^&}{:;?.]){1})(?!.*\s)[0-9a-zA-Z!@#$%*()_+^&]*$/.test(pwd.value)){ showMsg("密码必须由8~16位大小写字母、数字和特殊字符组成!"); pwd.focus(); return false; }*/ return true; } function login(){ if(vaildateForm()){ //登录跳转 $.ajax({ type:"POST", //传值方式 url:"manage/systemlogin!login.action", //请求的路径 dataType: "json", data:{username:$("#name").val(),password:$("#pwd").val()}, //数据格式 beforeSend:function(){ $("#loading").css('display','block');//加载效果 }, success:function(d, textStatus){ if(d.success==true){ if($("#rememberloginname").attr("checked")){//记住用户名 $.cookie('loginname', $("#name").val(),{expires: 7}); }else{ $.cookie('loginname', null); } //登录成功,跳转到导航页面 window.location.href = "manage/systemmanage!toIndexPage.action"; }else{ //师表小时 $("#loading").css('display','none'); showMsg(d.message); } }, //错误展示 error: function (xhttp, textStatus, errorThrown) { $("#loading").css('display','none'); //showMsg("System error!"); alert(errorThrown); } }); } } function fireFoxHandler(evt){ if(evt.keyCode==13){ login(); } } function ieHandler(evt){ if(evt.keyCode==13){ login(); } } if(document.addEventListener){//Firefox document.addEventListener("keypress",fireFoxHandler, true); }else{ document.attachEvent("onkeypress",ieHandler); }注意strust2的命名空间:url:"manage/systemlogin!login.action"
5.3 strust2后台配置调用
@Namespace("/manage") @Actions({ @Action(value="systemlogin", results={ @Result(name="loginpage", location="/index.jsp", type="redirect") }), @Action(value="systemmanage", results={ @Result(name="main", location="/main/main.jsp"), @Result(name="desktopPage", location="/main/desktop.jsp"), }) }) public class LoginAction extends BaseActionSupport<ManageUser>{ <pre name="code" class="html">@LogRecord(logType="5", actionDesc = "用户登录") public void login() { String username = null; String password = null; try{ username = Struts2Utils.getParameter("username"); password = Struts2Utils.getParameter("password"); }catch(Exception e){ e.printStackTrace(); } boolean loginResult = false; String loginMessage = ""; Des3Util des3Util = new Des3Util(ApplicationConfig.PASSWORD_KEY); ManageUser gmacUser = userService.findGmacUserByLoginName(username); //记录登录日志 SysOperateLog operateLog = new SysOperateLog(); operateLog.setLogTime(new Date()); operateLog.setLogOperate(WebConstant.LOG_OPERATE_SECURITY); operateLog.setLogUser(username); operateLog.setLogOperateClass(LoginAction.class.getSimpleName()); operateLog.setLogOperateMethod("login"); operateLog.setLogOperateActionName("systemlogin_login"); if(gmacUser==null){//用户不存在,记录不存在用户尝试登录日志 loginMessage = "用户不存在"; // loginMessage = LocalizedTextUtil.findDefaultText("tips.login.noexist", new Locale(ApplicationConfig.DEFAULT_LOCALE)); //记录无效用户名登录尝试记录日志 operateLog.setLogOperateResult(WebConstant.OPERATE_FAIL);//操作失败 // operateLog.setLogContent(MessageFormat.format(LocalizedTextUtil.findDefaultText("tips.login.fail", new Locale(ApplicationConfig.DEFAULT_LOCALE)),Struts2Utils.getRequest().getRemoteAddr())+loginMessage); }else {//用户存在,也记录登录日志 String enCodePassward = des3Util.getEncString(password);//加密后匹配 if(userService.userLogin(username, enCodePassward)==true){//验证初始密码 loginResult = true; }else{//密码错误 loginMessage = "密码错误"; // loginMessage = LocalizedTextUtil.findDefaultText("tips.login.passwrong", new Locale(ApplicationConfig.DEFAULT_LOCALE)); } if(loginResult){//验证成功,进行登录操作并存储用户信息到session,存储最近登录信息 gmacUser.setLastIp(gmacUser.getLoginIp()); gmacUser.setLastTime(gmacUser.getLoginTime()==null?(new Date()):gmacUser.getLoginTime());//2012.03.05修改用户未过期禁用bug gmacUser.setLoginFails(0); gmacUser.setLoginTime(new Date()); gmacUser.setLoginIp(Struts2Utils.getRequest().getRemoteAddr()); List list = userService.findRootSysMenuListByUserId(gmacUser.getId()); if((list==null || list.size()<1) && !ApplicationConfig.getSupperUserList().contains(gmacUser.getUserLoginName())){ loginResult = false; operateLog.setLogOperateResult(WebConstant.OPERATE_FAIL);//操作失败 }else{ Struts2Utils.getSession().setAttribute(WebConstant.SESSION_EMPLOYEE_BEAN, gmacUser); //设置用户可用的action name属性 ServletContext sctx = Struts2Utils.getSession().getServletContext(); String accesslist = "systemmanage_openDeskTopPage,systemmanage_toIndexPage,account_openChangePwdPage,systemlogin_logout,account_getUserPrivateMenuList,account_changePwd,ira_getUploadStatusBar4Br4"; sctx.setAttribute(WebConstant.ACCESS_ACTIONLIST, accesslist); operateLog.setLogOperateResult(WebConstant.OPERATE_SUCCESS);//操作成功 } }else{//登录失败 gmacUser.setLoginFails(1); gmacUser.setLoginTime(new Date()); gmacUser.setLoginIp(Struts2Utils.getRequest().getRemoteAddr()); if(!StringUtils.isNotBlank(loginMessage)){ loginMessage = LocalizedTextUtil.findDefaultText("tips.login.systemerror", new Locale(ApplicationConfig.DEFAULT_LOCALE)); } operateLog.setLogOperateResult(WebConstant.OPERATE_FAIL);//操作失败 // operateLog.setLogContent(MessageFormat.format(LocalizedTextUtil.findDefaultText("tips.login.fail", new Locale(ApplicationConfig.DEFAULT_LOCALE)),gmacUser.getLoginIp())+loginMessage); } userService.modifyAccount(gmacUser); } operateLog.setIsAuthed(true); operateLog.setLogType(WebConstant.LOG_ENVENT_TYPE6); operateLogService.writeOperateLog(operateLog); Struts2Utils.renderJson("{\"success\":"+loginResult+",\"message\":\""+loginMessage+"\"}"); }
这里为什么要返回的json字符串。理由是ajax请求要拿到返回值解析啊!
找一个相对比较正规的解析:
add信息(js中的部分内容):
case 'add':
openDialog(webroot+'san/cusInfo!cusInfoAddPage.action'+'?channel_no='+channel_no,700,150);
break;
后台action:Namespace和action和URL:san/cusInfo!对应;
@Namespace("/san")
@Actions({
@Action(value="cusInfo",
results={
//返回值和对应页面路径
@Result(name="cusInfoAddPage", location="/main/san/cusinfo_add.jsp"),
@Result(name="channelCusInfoAddPage", location="/main/san/channelcusInfo_add.jsp")
})
})
/***
* 打开新增页面的方法
*/
public String cusInfoAddPage(){
String channel_no = Struts2Utils.getParameter("channel_no");
if(StringUtils.isNotBlank(channel_no)){
Struts2Utils.getRequest().setAttribute("channel_no", channel_no);
}
return "cusInfoAddPage";
}
以上完整Ok