day53_电力系统_ztree动态生成&权限控制
来源:互联网 发布:邬桑为什么哭 知乎 编辑:程序博客网 时间:2024/06/06 07:05
项目第六天(系统登录)
1:struts2的validator校验(后台校验)
项目中进行数据校验的方式:
Js校验(前台校验)
Ajax校验(后台校验)
Struts2的validator校验(后台校验)
项目开发的时候,针对需求:提供校验机制,项目经理要求,即做前台校验又做后台校验(保证数据安全),同时由于校验,查询性能也相应降低。
所以要求:
如果数据不是很重要,可以只做前台校验
如果数据很重要,即做前台校验又要做后台校验
第一步:在Action中定义:如果出现校验问题,使用:
if(elecUser==null){ this.addFieldError("error", "用户名输入有误!"); return "error";}
第
二步:在struts.xml中定义:
<action name="elecMenuAction_*" class="elecMenuAction" method="{1}"> <result name="error">/WEB-INF/page/menu/index.jsp</result></action>
第三步:在menu/index.jsp中,定义:
使用struts2的标签,输出错误信息:
<s:fielderror/>
第四步:效果:
第五步:修改错误的样式:字体变红;去掉前面的圆圈
改变错误的样式 <s:fielderror>
标签的封装在:struts2的核心包下
只需要在项目的src下添加2个文件夹template/simple,将fielderror.ftl的文件放置到该文件夹下,此时启动的时候,就会覆盖struts核心包的下加载的内容
修改fielderror.ftl中的内容:
<#list eKeys as eKey><#t/> <#assign eValue = fieldErrors[eKey]><#t/> <#list eValue as eEachValue><#t/> <font color='red'><span><#if parameters.escape>${eEachValue!?html}<#else>${eEachValue!}</#if></span></font> </#list><#t/></#list><#t/>
2:hibernate的懒加载问题
产生:
当使用hibernate查询一个对象的时候,如果Session关闭,再调用该对象关联的集合或者对象的时候,会产生懒加载异常!
解决方案:
方案一:
在Session关闭之前,查询对象关联的集合或者对象,所有在业务层的方法上添加:
public ElecUser findUserByLogonName(String name) { String condition = " and o.logonName = ?"; Object [] params = {name}; List<ElecUser> list = elecUserDao.findCollectionByConditionNoPage(condition, params, null); //数据库表中存在该用户,返回ElecUser对象 ElecUser elecUser = null; if(list!=null && list.size()>0){ elecUser = list.get(0); } /*** * 解决懒加载异常 除了OID之外的其他属性 */ elecUser.getElecRoles().size(); return elecUser; }
方案二:在Service层的方法中(Session关闭之前),初始化对象关联的集合或者对象
public ElecUser findUserByLogonName(String name) { String condition = " and o.logonName = ?"; Object [] params = {name}; List<ElecUser> list = elecUserDao.findCollectionByConditionNoPage(condition, params, null); //数据库表中存在该用户,返回ElecUser对象 ElecUser elecUser = null; if(list!=null && list.size()>0){ elecUser = list.get(0); } /*** * 解决懒加载异常 */ Hibernate.initialize(elecUser.getElecRoles()); return elecUser; }
方案三:在ElecUser.hbm.xml中,添加lazy=”false”,查询用户的同时,立即检索查询用户关联的角色集合:
<set name="elecRoles" table="elec_user_role" inverse="true" lazy="false"> <key> <column name="userID"></column> </key> <many-to-many class="cn.itcast.elec.domain.ElecRole" column="roleID"/></set>
表示查询用户的时候,立即检索用户所关联的角色
建议项目开发中不要在.hbm.xml中添加过多的lazy=false,这样如果表关联比较多,不需要查询的对象也被加载了,性能会出现问题。
方案四:使用spring提供的过滤器OpenSessionInViewFilter,在web容器中添加该过滤器
在web.xml中添加:
要求:该过滤器一定要放置到strtus2的过滤器的前面,先执行该过滤器。
<!-- 添加spring提供的过滤器,解决hibernate的懒加载问题 --> <filter> <filter-name>OpenSessionInViewFilter</filter-name> <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenSessionInViewFilter</filter-name> <url-pattern>*.do</url-pattern> <url-pattern>*.jsp</url-pattern> </filter-mapping> <!-- 配置struts2的过滤器,这是struts2运行的核心 --> <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> <url-pattern>*.jsp</url-pattern> </filter-mapping>
表示:OpenSessionInViewFilter过滤器实现的原理:
1:事务提交:spring提供的声明式事务控制,仍然在业务层的方法进行处理,方法执行完毕后,事务会自动提交,如果出现异常,事务就会回滚。但是它延迟了Session关闭的时间。
2:Session关闭:Session在页面上进行关闭,此时当页面上的数据加载完成之后,再关闭Session。
问题:如果你开发的系统对页面数据加载比较大的时候,不适合使用
OpenSessionInViewFilter,这样Session不能及时关闭,另一个Session就无法访问,连接不够使用,就会产生“假死”现象。
3:验证码
作用:防止恶意的测试系统的用户名和密码(利用循环输入用户名和密码测试),采用验证码,每次到登录页面的时候,验证码的值是不同的,需要重新输入。
第一步:index.jsp页面:
<tr> <td width="100"><img border="0" src="${pageContext.request.contextPath}/images/check.jpg" width="75" height="20"></td> <td> <table> <tr> <td> <input type="text" name="checkNumber" id="checkNumber" value="" maxlength="4" size="7"> </td> <td> <img src="${pageContext.request.contextPath}/image.jsp" name="imageNumber" id="imageNumber" style="cursor:hand" title="点击可更换图片" height="20" onclick="checkNumberImage()"/> </td> </tr> </table> </td></tr>
添加:
Js方法
function checkNumberImage(){ var imageNumber = document.getElementById("imageNumber"); imageNumber.src = "${pageContext.request.contextPath}/image.jsp?timestamp="+new Date().getTime();}
image.jsp(生成4位随机数字验证码)
Random random = new Random(); String sRand = ""; for (int i = 0; i < 4; i++) { String rand = String.valueOf(random.nextInt(10)); sRand += rand; } // 将认证码存入SESSION session.setAttribute("CHECK_NUMBER_KEY", sRand);// 输出图象到页面 try { ImageIO.write(image, "JPEG", response.getOutputStream()); } catch (Exception e) { }
第二步:在Action中进行校验,创建LoginUtils类
public class LogonUtils { /**验证验证码输入是否正确*/ public static boolean checkNumber(HttpServletRequest request) { //从页面中获取输入框的值 String checkNumber = request.getParameter("checkNumber"); if(StringUtils.isBlank(checkNumber)){ return false; } //从Session中获取验证码的值 String CHECK_NUMBER_KEY = (String)request.getSession().getAttribute("CHECK_NUMBER_KEY"); if(StringUtils.isBlank(CHECK_NUMBER_KEY)){ return false; } return checkNumber.equalsIgnoreCase(CHECK_NUMBER_KEY); }}
4:记住我
作用:记住当前用户名和密码,下次登录名不需要用户再次输入
第一步:index.sp页面
<tr> <td width="100"><img border="0" src="${pageContext.request.contextPath}/images/remeber.jpg" width="75" height="20"></td> <td> <input type="checkbox" name="remeberMe" id="remeberMe" value="yes"/> </td></tr>
第二步:Action代码的处理,创建LoginUtils类
public class LogonUtils { /**记住我功能*/ public static void remeberMe(String name, String password, HttpServletRequest request, HttpServletResponse response) { //1:创建2个Cookie,存放指定值 Cookie nameCookie = new Cookie("name",name); Cookie passwordCookie = new Cookie("password",password); //2:设置Cookie的有效路径(指定当前项目) nameCookie.setPath(request.getContextPath()+"/"); passwordCookie.setPath(request.getContextPath()+"/"); //3:设置Cookie的有效时间(1周) //获取页面复选框的值(用作判断) String remeberMe = request.getParameter("remeberMe"); //此时表示复选框选中 if(remeberMe!=null && remeberMe.equals("yes")){ nameCookie.setMaxAge(7*24*60*60); passwordCookie.setMaxAge(7*24*60*60); } //此时表示复选框没有被选中 else{ nameCookie.setMaxAge(0); passwordCookie.setMaxAge(0); } //4:将Cookie存放到response中 response.addCookie(nameCookie); response.addCookie(passwordCookie); }}
第三步:在index.jsp页面中读取Cookie中的数据,jsp中嵌套java代码
<%String name = "";String password = "";String checked = "";Cookie [] cookies = request.getCookies();if(cookies!=null && cookies.length>0){ for(Cookie cookie:cookies){ if(cookie.getName().equals("name")){ name = cookie.getValue(); checked = "checked"; } if(cookie.getName().equals("password")){ password = cookie.getValue(); } }}%>
缺点:将java代码放置到jsp上,要求jsp先要执行编译java代码,然后再执行。效率会降低,能否将java代码抽取出去呢?
分析:在跳转到index.jsp页面之前先从Cookie中获取数据,放置到HttpRequest对象中进行显示,这样可以使用过滤器(filter)完成:
第四步:添加过滤器
public class SystemFilter implements Filter { /**web容器启动的时候,执行的方法*/ public void init(FilterConfig config) throws ServletException { } /**每次访问URL连接的时候,先执行过滤器的doFilter的方法*/ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; //获取访问的连接地址 String path = request.getServletPath(); //在访问首页index.jsp页面之前,先从Cookie中获取name,password的值,并显示在页面上(完成记住我) this.forwordIndexPage(path,request); //放行 chain.doFilter(request, response); } /**销毁*/ public void destroy() { } /**在访问首页index.jsp页面之前,先从Cookie中获取name,password的值,并显示在页面上(完成记住我)*/ private void forwordIndexPage(String path, HttpServletRequest request) { if(path!=null && path.equals("/index.jsp")){ String name = ""; String password = ""; String checked = ""; Cookie [] cookies = request.getCookies(); if(cookies!=null && cookies.length>0){ for(Cookie cookie:cookies){ if(cookie.getName().equals("name")){ name = cookie.getValue(); /** * 如果name出现中文,对中文进行解码 */ try { name = URLDecoder.decode(name, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } checked = "checked"; } if(cookie.getName().equals("password")){ password = cookie.getValue(); } } } request.setAttribute("name", name); request.setAttribute("password", password); request.setAttribute("checked", checked); } }}
第五步:在web.xml中添加:
<!-- 自定义过滤器,要求添加到struts2过滤器的前面 --><filter> <filter-name>SystemFilter</filter-name> <filter-class>cn.itcast.elec.util.SystemFilter</filter-class></filter><filter-mapping> <filter-name>SystemFilter</filter-name> <url-pattern>*.do</url-pattern> <url-pattern>*.jsp</url-pattern></filter-mapping>测试后:发现问题:如果name中存在中文,此时中文字符是不能存放到Cookie对象中,使用HttpResponse对象添加Cookie会抛出异常。解决方案:使用URLEncode类和URLDecode类进行编码和解码在LogonUtils类对name进行编码:try { name = URLEncoder.encode(name, "UTF-8");} catch (UnsupportedEncodingException e) { e.printStackTrace();}//1:创建2个Cookie,存放指定值Cookie nameCookie = new Cookie("name",name);Cookie passwordCookie = new Cookie("password",password);在过滤器SystemFilter类对name进行解码:if(cookie.getName().equals("name")){ name = cookie.getValue(); /** * 如果name出现中文,对中文进行解码 */ try { name = URLDecoder.decode(name, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } checked = "checked";}
5:jquery的ztree插件的使用(完成动态加载树型结构)
第一步:在left.jsp中
<script language="JavaScript" src="${pageContext.request.contextPath }/script/jquery-1.4.2.js"></script><script language="JavaScript" src="${pageContext.request.contextPath }/script/jquery-ztree-2.5.js"></script><script language="JavaScript" src="${pageContext.request.contextPath }/script/treeMenu.js"></script><link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath }/css/menu.css" /><link rel="stylesheet" href="${pageContext.request.contextPath }/css/zTreeStyle/zTreeStyle.css" type="text/css">Left.jsp中使用<ul><TABLE border=0 width="20"> <TR> <TD width=340px align=center valign=top> <div class="zTreeDemoBackground"> <ul id="menuTree" class="tree" ></ul> </div> </TD> </TR></TABLE>
第二步:在treeMenu.js中定义:
var menu = { setting: { isSimpleData: true, treeNodeKey: "mid", treeNodeParentKey: "pid", showLine: true, root: { isRoot: true, nodes: [] } }, loadMenuTree:function(){ $.post("elecMenuAction_showMenu.do",{},function(data){ $("#menuTree").zTree(menu.setting, data); }); }};$().ready(function(){ menu.loadMenuTree();});
第三步:在Action中添加:
public String showMenu(){ //获取Session中存放的权限字符串(格式:aa@ab@ac) String popedom = (String) request.getSession().getAttribute("globle_popedom"); //1:查询当前用户所具有的功能权限,使用权限,查询权限表,返回List<ElecPopedom> List<ElecPopedom> list = elecRoleService.findPopedomListByUser(popedom); //2:将list放置到栈顶,栈顶的对象转换成json数组的形式 ValueStackUtils.setValueStack(list); return "showMenu";}
第四步:(hql语句嵌套查询),Service类定义:
public List<ElecPopedom> findPopedomListByUser(String popedom) { //hql语句和sql语句的嵌套查询String condition = " and o.mid IN('"+popedom.replace("@", "','")+"') AND isMenu = ?"; Object [] params = {true}; Map<String, String> orderby = new LinkedHashMap<String, String>(); orderby.put("o.mid", "asc"); List<ElecPopedom> list = elecPopedomDao.findCollectionByConditionNoPage(condition, params, orderby); return list;}
第五步:在struts.xml中添加:
<!-- 将集合压入到栈顶,集合返回页面的时候,转换成json的形式 --><result name="showMenu" type="json"></result>
6:自定义标签
使用当前用户具有的权限,控制页面上的按钮或者链接是否可见。
详情请见技术资料【技术资料\自定义标签+struts2标签控制访问链接权限】中的《自定义标签(帮助).doc》
7:粗颗粒度权限控制(使用过滤器完成)
分析:
精确到Session的权限控制(判断Session是否存在)
使用过滤器完成粗颗粒的权限控制,如果Session不存在就跳转到首页,如果存在可以通过URL链接访问到对应的操作。
第一步:定义一个过滤器:
public class SystemFilter implements Filter { /**web容器启动的时候,执行的方法*/ //存放没有Session之前,需要放行的连接 List<String> list = new ArrayList<String>(); public void init(FilterConfig config) throws ServletException { list.add("/index.jsp"); list.add("/image.jsp"); list.add("/system/elecMenuAction_menuHome.do"); } /**每次访问URL连接的时候,先执行过滤器的doFilter的方法*/ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; //获取访问的连接地址 String path = request.getServletPath(); //在访问首页index.jsp页面之前,先从Cookie中获取name,password的值,并显示在页面上(完成记住我) this.forwordIndexPage(path,request); //如果访问的路径path包含在放行的List的存放的连接的时候,此时需要放行 if(list.contains(path)){ chain.doFilter(request, response); return; } //获取用户登录的Session ElecUser elecUser = (ElecUser)request.getSession().getAttribute("globle_user"); //放行 if(elecUser!=null){ chain.doFilter(request, response); return; } //重定向到登录页面 response.sendRedirect(request.getContextPath()+"/index.jsp"); } /**销毁*/ public void destroy() { } /**在访问首页index.jsp页面之前,先从Cookie中获取name,password的值,并显示在页面上(完成记住我)*/ private void forwordIndexPage(String path, HttpServletRequest request) { if(path!=null && path.equals("/index.jsp")){ String name = ""; String password = ""; String checked = ""; Cookie [] cookies = request.getCookies(); if(cookies!=null && cookies.length>0){ for(Cookie cookie:cookies){ if(cookie.getName().equals("name")){ name = cookie.getValue(); /** * 如果name出现中文,对中文进行解码 */ try { name = URLDecoder.decode(name, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } checked = "checked"; } if(cookie.getName().equals("password")){ password = cookie.getValue(); } } } request.setAttribute("name", name); request.setAttribute("password", password); request.setAttribute("checked", checked); } }}
第二步:在web容器中添加对应的过滤器:
<!-- 自定义过滤器,要求添加到struts2过滤器的前面 --> <filter> <filter-name>SystemFilter</filter-name> <filter-class>cn.itcast.elec.util.SystemFilter</filter-class> </filter> <filter-mapping> <filter-name>SystemFilter</filter-name> <url-pattern>*.do</url-pattern> <url-pattern>*.jsp</url-pattern> </filter-mapping>
问题:不够友好,访问链接,最好提示【非法操作,系统将会5秒后跳转到登录页面】
修改过滤器类中的操作:
(1)在过滤器中的init方法中添加2个放行的连接:
list.add("/error.jsp");list.add("/system/elecMenuAction_logout.do");
(2)在doFilter的方法重定向到error.jsp
将:
//重定向到登录页面response.sendRedirect(request.getContextPath()+"/index.jsp");修改成://重定向到error.jsp(5秒跳转到登录名页面)response.sendRedirect(request.getContextPath()+"/error.jsp");
(3)error.jsp的内容:
<script>var i=6;var t;function showTimer(){ if(i==0){//如果秒数为0的话,清除t,防止一直调用函数,对于反应慢的机器可能实现不了跳转到的效果,所以要清除掉 setInterval() parent.location.href="${pageContext.request.contextPath }/system/elecMenuAction_logout.do"; window.clearInterval(t); }else{ i = i - 1 ; // 秒数减少并插入 timer 层中 document.getElementById("timer").innerHTML= i+"秒"; }}// 每隔一秒钟调用一次函数 showTimer()t = window.setInterval(showTimer,1000);</script>
注意:Session不应该在服务器一直不清空,如果Session过多,会导致Session压力大,系统变慢,于是要求10分钟如果不操作系统,将Session自动清空。在web.xml中配置
<session-config> <session-timeout>10</session-timeout></session-config>
粗颗粒的权限控制的面试:
使用过滤器
在过滤器中定义放行的连接,因为不是每个操作都会存在Session
在过滤器中获取登录后存放的Session,如果Session不为空,则放行,即可以操作定义的业务功能,如果Session为空,则跳转到登录页面。
控制访问的系统必须要存在Session
8:系统中的异常处理+日志备份(使用struts2的拦截器)
操作:
添加异常处理,详情请见【技术资料\自定义拦截器(实现异常处理+细颗粒权限控制)\异常处理】中的《struts2的拦截器实现异常处理.doc》
项目中的异常处理面试:
使用struts2的拦截器
在拦截器中的doIntercept()方法定义try… catch异常
如果Action、Service、Dao没有抛出异常,则在try模块中指定正确操作的页面,例如:
result = actioninvocation.invoke();
return result;
result跳转到正确的页面
如果Action、Service、Dao抛出异常,则在catch模块中,获取异常,使用log4j存放到指定的日志文件中,通过return “errorMsg”;跳转到错误页面。
9:细颗粒权限控制(使用struts2的拦截器)
操作:
详情参考【技术资料\自定义拦截器(实现异常处理+细颗粒权限控制)\细颗粒度权限控制】中的《struts2的拦截器实现细颗粒度权限控制.doc》
要求:登录操作ElecMenuAction类中的方法,要求不需要添加到struts2的自定义拦截器中
此时可以在struts.xml中定义:
问题:为什么在struts2的拦截器中都要使用roleID,mid,pid去查询一遍数据库,而为什么不从Session中获取mid的值和注解上定义的mid的值进行比较呢?
回答:此时不安全,如果盗用账户,登录系统(一定有Session),即可以操作每一个执行的方法。但是由于挂失后,数据库存放的数据发生变化,操作每一个功能之前都会先查询权限,这样查询才能保证数据安全。
细颗粒的权限控制的面试:
使用struts2的拦截器
定义一个注解(mid和pid),对应权限code和父级权限的code,将注解添加到Action类中方法的上面
每个Action类的方法上添加注解(mid=””,pid=””),表示方法的惟一标识(即该方法所具有的权限)
在struts2的拦截器中,从Session中获取角色ID,获取Action类方法上的注解(mid和pid),使用角色ID,mid和pid查询角色权限表,判断当前用户是否可以操作该方法。
10:今天知识点总结
系统上线前的准备:
权限表的数据:
11:需要掌握的知识点总结
重点:登录操作,项目中后台校验和前台校验,hibernate的懒加载处理
了解:验证码,记住我
必须掌握的分析问题的能力思想:
例如:粗颗粒权限控制
异常处理+日志备份
细颗粒权限控制
- day53_电力系统_ztree动态生成&权限控制
- 权限控制--js动态生成的html
- 【转载】【权限控制】角色访问动态生成用户权限菜单树
- 动态URL权限控制
- 动态URL权限控制
- 电力系统远程控制项目总结
- 动态生成控制项
- Android6.0动态权限控制
- 电力系统
- 电力系统
- 第十九章 动态URL权限控制
- 第十九章 动态URL权限控制
- 动态生成的内容,如何控制布局
- 动态生成表格,列数控制
- 控制一个动态生成view 的位置
- 电力系统潮流的计算及其分析控制(上)
- 电力系统潮流的计算及其分析控制(中)
- 电力系统潮流的计算及其分析控制(下)
- android学习笔记(1)
- Android(5)Service总结
- 梯度下降优化算法综述
- Windows live writer's plugins
- ReactNative-触摸事件
- day53_电力系统_ztree动态生成&权限控制
- QQ空间操作抓包【MD5-RSA加密】【qqtoken计算】等
- 《Web全端工程师就业课程【第三周】》课程作业
- 创建节点、添加节点
- 嵌套循环
- ReactNative(API)Alert
- 编译opencv2.4.9+cuda6.5+vs2013生成自己的X64库
- 最小费用流模板(Bellman-ford)
- 了解sizeof和strlen以及关于数组的相关计算