github:https://github.com/wade6/messageboard-webx3
增加权限和白名单
8、权限验证
前面已经说过,创建的留言板系统没有权限验证,导致每个页面不用登陆就能访问,那么现在就来加上权限验证,增加留言板系统的安全性。
首先了解一下权限验证的原理,前面也简单的提到过,其实就是利用cookie和session机制。
cookie实现
只用cookie是可以实现权限验证的,其过程为:
1、当登陆请求到来时,生成一个cookie,将其响应给用户;
Cookie cookie = new Cookie("login","success");
response.addCookie(cookie);
然后客户端浏览器将创建一个cookie;
2、当请求再次到达时,检查请求报头中的cookie信息即可实现权限验证;
3、用户退出时,设置login=flase;
4、可以设置cookie,当浏览器关闭后,cookie马上销毁;也可以设置有效期。
但这种方式的安全性极低,如果人为的修改了cookie,那么系统是完全透明的。
session实现
session一般是保存在服务器中的一个记录请求会话的对象。
利用session实现权限验证,其过程为:
1、请求第一次到达时,新建一个session,将其唯一标识sessionId保存在cookie中,返回给客户端;
2、登录请求到达时,根据cookie中的sessionId,在此session种创建用户的信息<key,value>;
3、用户请求需要权限验证的页面时,根据cookie中的sessionId获取session,然后就可以拿到当前用户的信息。
4、用户退出时,将用户的信息从session中删除。
5、可以在用户退出时调用session方法清除session,也可以设置有效期控制session的生命周期。
这种方式通过服务器主动的进行权限验证,相对cookie来说比较安全。但也存在风险,比如csrf攻击。
webx3可以防御这种攻击,就是在页面中增加一个隐藏的字段。
webx3中的session可以保存在服务器端,也可以保存在客户端的cookie中,只需要在webx.xml中设置一下即可。
如下图:
设置将session保存在客户端的cookie中,还可以对session和cookie进行一些参数的设置,其实webx3实现一个session框架,具体内容参见《webx框架指南》。
设置好session后,就可以在系统中利用session进行权限验证了。
权限验证是通过在pipeline.xml中配置验证valve来实现的。
首先创建一个权限vavle类,如下:
com.alibaba.webx3.messageboard.util.AuthorizationValve.java
package com.alibaba.webx3.messageboard.util; import java.util.List; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import com.alibaba.citrus.service.pipeline.PipelineContext; import com.alibaba.citrus.service.pipeline.support.AbstractValve; import com.alibaba.citrus.service.uribroker.URIBrokerService; import com.alibaba.citrus.service.uribroker.uri.URIBroker; import com.alibaba.citrus.turbine.TurbineRunData; import com.alibaba.citrus.turbine.util.TurbineUtil; import com.alibaba.citrus.util.ServletUtil; import com.alibaba.citrus.util.StringUtil; public class AuthorizationValve extends AbstractValve { @Autowired private HttpServletRequest request; @Autowired private URIBrokerService uriBrokerService; @Resource(name="whiteListForLogin") private List<String> whiteListForLogin; public void invoke(PipelineContext pipelineContext) throws Exception { // 获取session HttpSession session = request.getSession(); TurbineRunData rundata = TurbineUtil.getTurbineRunData(request); String sessionUser=null; //获取session中的用户名,到相应的sessionStore中获取,若设置为cookie,则到cookie中查找 sessionUser = (String) session.getAttribute("login_user"); //取得request所请求的资源路径。 String path = ServletUtil.getResourcePath(request); if (sessionUser == null) { //不是白名单的页面,跳回登陆页面 if (!checkUri(path)) { URIBroker loginUrl = uriBrokerService.getURIBroker("loginLink"); rundata.setRedirectLocation(loginUrl.render()); return; } } pipelineContext.invokeNext(); } //检查白名单 private boolean checkUri(String path) { int lastSlashIndex = path.lastIndexOf("/"); //最后的页面下划线大写处理 if (lastSlashIndex >= 0) { path = path.substring(0, lastSlashIndex) + "/" + StringUtil.toCamelCase(path.substring(lastSlashIndex + 1)); } else { path = StringUtil.toCamelCase(path); } return whiteListForLogin != null && whiteListForLogin.contains(path) ? true : false; } }
其中checkUri()方法是检查设置的白名单,在白名单中的页面可以面登录访问,其中白名单对象就是List<String> whiteListForLogin,是通过注入得到的,其真实的身份是在webx.xml中配置的bean,如下:
...... <!-- 装载模块。 --> <services:module-loader> <ml-factories:class-modules> <ml-factories:search-packages type="$1" packages="com.alibaba.webx3.common.module.*" /> </ml-factories:class-modules> </services:module-loader> <!-- 免登陆访问的白名单 --> <beans:bean id="whiteListForLogin" class="java.util.ArrayList" > <beans:constructor-arg> <beans:list> <beans:value>/index.htm</beans:value> <beans:value>/register.htm</beans:value> </beans:list> </beans:constructor-arg> </beans:bean> </beans:beans>
修改uri.xml配置"loginLink",如下:
<?xml version="1.0" encoding="UTF-8" ?> <beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:services="http://www.alibaba.com/schema/services" xmlns:uris="http://www.alibaba.com/schema/services/uris" xmlns="http://www.alibaba.com/schema/services/uris" xmlns:uri-interceptors="http://www.alibaba.com/schema/services/uris/interceptors" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation=" http://www.alibaba.com/schema/services http://localhost:8080/schema/services.xsd http://www.alibaba.com/schema/services/uris http://localhost:8080/schema/services-uris.xsd http://www.alibaba.com/schema/services/uris/interceptors http://localhost:8080/schema/services-uris-interceptors.xsd http://www.springframework.org/schema/beans http://localhost:8080/schema/www.springframework.org/schema/beans/spring-beans.xsd "> <services:uris> <uri id="server" requestAware="true" /> <turbine-uri id="messageBoardLink" exposed="true" extends="server"> <componentPath>/</componentPath> </turbine-uri> <turbine-content-uri id="loginLink" extends="messageBoardLink" exposed="true"> <contentPath>/index.htm</contentPath> </turbine-content-uri> </services:uris> </beans:beans>
然后在pipeline.xml中配置该valve,如下:
...... <!-- 检查csrf token,防止csrf攻击和重复提交。假如request和session中的token不匹配,则出错,或显示expired页面。 --> <checkCsrfToken /> <!-- 登陆权限验证 --> <valve class="com.alibaba.webx3.messageboard.util.AuthorizationValve" /> <loop> ......
此时只是完成了验证的功能,但是还能没有给系统增加授权的语句。
在userAction.java中增加授权的语句。
当用户登录时,在session中增加用户信息,退出时清除session。
package com.alibaba.webx3.messageboard.module.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import com.alibaba.citrus.turbine.Context; import com.alibaba.citrus.turbine.Navigator; import com.alibaba.citrus.turbine.dataresolver.FormGroup; import com.alibaba.webx3.messageboard.biz.service.UserService; import com.alibaba.webx3.messageboard.dao.object.UserDO; import com.alibaba.webx3.messageboard.module.vo.UserVO; public class UserAction { @Autowired private UserService userService; @Autowired private HttpServletResponse response; @Autowired private HttpServletRequest request; // 登陆 public void doLogin(@FormGroup("login") UserVO user, Context context, Navigator nav, HttpSession session) { String username = user.getUsername(); String password = user.getPassword(); UserDO userdo = null; boolean success; // 根据用户名获得用户记录 userdo = userService.getByUsername(username); if (userdo == null) { context.put("message", "用户名不存在!"); nav.forwardTo("index"); return; } // 校验密码是否正确 if (password.equals(userdo.getPassword())) { success = true; } else { success = false; } // 判断执行转向和重定向 if (success) { session.setAttribute("login_user", user.getUsername()); context.put("username", user.getUsername()); nav.redirectTo("messageLink").withTarget("/message/messageList"); } else { context.put("message", "密码错误!"); nav.forwardTo("index"); } } // 退出 public void doLogout(Navigator nav, HttpSession session) { session.invalidate(); // 转到首页 nav.redirectTo("messageBoardLink").withTarget("index"); } }
在messageList.vm中增加权限的判断,admin用户可以删除和编辑,其他用户只能操作自己添加的留言,如下
<div style="font-size:10pt"> <p>留言列表 <a href="$messageBoardLink.setTarget("message/addMessage")">添加留言</a> </p> <p style="color:red">$!message</p> <p>--------------------------------------------------------</p> <form action="" method="post" target="_self"> $csrfToken.hiddenField <input type="hidden" name="action" value="messageAction"/> #foreach($messageItem in $messageList) <p>标题:$!messageItem.title</p> <p>作者:$!messageItem.author</p> <div style="color:blue"> <p>$!messageItem.content</p> </div> #if($admin==true||$username==$messageItem.author) <a href='#' onclick="deleteMessage($messageItem.id)">删除</a> <a href='$messageBoardLink.setTarget("message/modifyMessage").addQueryData("messageId", $messageItem.id)'>编辑</a> #end <p>--------------------------------------------------------</p> #end <input type="hidden" id="messageId" name="messageId" value="$messageItem.id"> <input type="submit" id="delete" style="display:none;" name="event_submit_do_delete" /> </form> </div> <script> function deleteMessage(id){ if(confirm("确定要删除?")){ document.getElementById("messageId").value=id; document.getElementById("delete").click(); } } </script>
至此,权限验证就完成了。
现在可以把webx-app1.xml删掉,代码可以不用管,这样子应用app1就不会加载了,其实不删也没什么关系。
留言板系统也就此结束~
9、总结
1、在实现权限系统时出现了一些问题,在配置白名单的bean时,将其配置到webx-messageboard.xml中,启动容器的时候加载app1出错:no bean named whiteListForLogin,折腾了好久才知道,app1和messageboard先前设置的公用同一个pipeline,但是whiteListForLogin 这个vale只配在了messageboard的子容器中,所以会出现上述错误;
解决办法:将whiteListForLogin 配置到webx.xml中
2、解决了上述问题后,又报错:No matching bean of type [java.lang.String] found for dependency [collection of java.lang.String],经过网上搜刮,发现原因是自动注入的问题;.AuthorizationValve.java中注入whiteListForLogin不能用@Autowired,改用@Resource(name="whiteListForLogin")就解决了。具体原因可以参考 http://stackoverflow.com/questions/1363310/auto-wiring-a-list-using-util-schema-gives-nosuchbeandefinitionexception
3、通过这个小实践,加深了对webx3开发的理解,同时学到了不少web开发知识~在此感谢身边的同事,谢谢他们对我的帮助。
4、对webx3的原理还不是很熟悉,下一步准备研究一下~
关于webx3的session
webx3中实现了一个session框架,可以通过webx.xml中的设置将session保存在不同的地方,常用的有服务器内存和cookie;还可以将session的不同部分分别保存到不同的地方。
session框架
session ID是唯一标示,一般将其保存在cookie中,这样相同cookie值的请求都看作是同一个session的请求。
session的生命周期:第一个请求时创建;在访问期间可以不断地更新;超过配置的最大不活动时间就会结束,还可以通过调用session.invalidate()方法,直接清除session的所有内容;
Session Store是session框架中最核心的部分,定义了session保存的位置,可以设置多个store,这样就可以将session不同的部分保存在不同的地方。
在session框架中,有一个重要的特殊对象,用来保存session生命期的状态。这个对象叫作session model。
Session Model是用来记录当前session的生命期数据的,例如:session的创建时间、最近更新时间等。
SessionModelEncoder
默认情况下,SessionModel对象将被转换成一个JSON字符串,然后这个字符串将被保存在某个session store中;读取时需要解码成SessionModel对象。
默认实现为:
<session-model-encoders>
<model-encoders:default-session-model-encoder />
</session-model-encoders>
Session Interceptor拦截器的作用是拦截特定的事件,甚至干预该事件的执行结果。
Cookie Store
Cookie Store的作用,是将session对象保存在客户端cookie中。Cookie Store减轻了服务器维护session数据的压力,从而提高了应用的扩展性和可用性。
但是读写cookie比较麻烦,还要在代码中设置很多参数:domain、path、httpOnly...等,所以通过操作HttpSession,session框架就帮我们读写cookie了(那些参数在配置文件里配置就ok)。webx主张把一切对cookie的读写,都转换成对session的读写。
Session Encoders
Session里保存的是Java对象,而cookie中只能保存字符串。如何把Java对象转换成合法的cookie字符串(或者将字符串恢复成对象)呢?这就是Session Encoder所要完成的任务。详细见下面。
Cookie Store需要依赖其它两个Request Contexts: <buffered>(将所有的输出到response.getWriter()或getOutputStream()的内容缓存在内存里,直到最后一刻才真正
输出到浏览器) 和 <lazy-commit>(拦截了response对象中引起提交的方法,将它们延迟到最后才执行。)
Cookie Store分为多值和单值;
多值Cookie Store是在一组cookie(如tmp0, tmp1, ...)中保存一组attributes的名称和对象。它所创建的cookie值,只有session框架自己才能解读,如<key,value>的形式。
单值cookie store就是在一个cookie中仅保存一个值或对象,如<object>。
Session Encoders和Session Value Encoder
这两个cookie store的结构是不一样的。因此解码的方法也不一样。单值的cookieStore使用Session Value Encoder解码;多值的cookieStore使用Session Encoder。
Session Encoders
Session Encoder需要转换一组session attributes的key-values。Session框架提供了一种encoder的实现,编码的基本过程为:序列化、加密(可选)、压缩、Base64编码、URL encoding编码。
保存session数据时,session框架将使用第一个encoder来将对象转换成cookie可接受的字符串;
读取session数据时,session框架将依次尝试所有的encoders,直到解码成功为止。
默认实现为:
<session-stores:encoders>
<session-encoders:serialization-encoder />
</session-stores:encoders>
Session Value Encoder
session Value Encoder只转换sessionattribute的值。
和SessionModelEncoder以及SessionEncoder类似,session框架也支持多个session valueencoders同时存在。
• 保存session数据时,session框架将使用第一个encoder来将对象转换成cookie可接受的字符串;
• 读取session数据时,session框架将依次尝试所有的encoders,直到解码成功为止。
这种编码、解码方案可让使用不同session value encoders的系统之间共享cookie数据,也有利于平滑迁移系统。
目前有两种基本的session value encoders实现。<simple-value-encoder>和<mappedvalues-encoder>
Simple Memory Store
SimpleMemoryStore是最简单的session store。它将所有的session对象都保存在内存里面。这种store不支持多台机器的session同步,而且也不关心内存是否被用尽。因此这种简单的store一般只应使用于测试环境。
<stores>
<session-stores:simple-memory-store id="simple" />
</stores>
相关推荐
基于maven创建web项目 maven是一种基于项目对象模型(Project Object Model,POM)的项目管理工具,由Apache软件基金会开发和维护。maven提供了一种标准化的方式来构建、打包和部署项目,它可以帮助开发者简化项目...
### 使用Idea14.1.4和Maven创建Java Web项目 #### 一、概述 在本篇文章中,我们将详细介绍如何使用IntelliJ IDEA 14.1.4版本结合Maven来创建一个Java Web项目。这种方法不仅能够提高开发效率,还能确保项目的结构...
IDEA 使用 Maven 创建 Web 项目详细教程 本教程将指导您使用 IDEA 创建一个 Maven Web 项目,从安装 Maven 到配置 Maven 环境、创建 Maven 工程、pom.xml 文件配置、依赖管理等。 1. 安装 Maven 在 IDEA 中使用 ...
【标题】:“idea使用maven创建的web项目” 在Java开发中,IntelliJ IDEA(简称Idea)作为一款强大的集成开发环境,结合Maven构建工具,能够帮助开发者高效地管理项目的依赖、构建和部署。Maven是一个项目管理和...
本文将深入探讨如何利用Maven构建多模块项目,以及在实际操作中需要注意的关键点。 1. Maven多模块项目结构: Maven多模块项目通常遵循一个标准的目录结构,包括一个顶级父 pom.xml(如test-hd-parent),和若干子...
项目管理利器——maven,技术开发学习类稳定档案顶顶顶顶
java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一...
【标题】"mavenWeb空项目"所涉及的知识点主要...综上所述,"mavenWeb空项目"是一个包含Java Web开发基础的项目模板,利用Maven进行构建管理和单元测试,为开发者提供了一个良好的起点,便于快速开发和迭代Web应用程序。
本篇文章将详细介绍如何利用Maven创建一个Servlet版本为3.0、Java版本为1.7的Web项目,并对创建过程中可能出现的问题及解决方案进行深入探讨。 #### 二、准备工作 1. **安装环境**: - Eclipse IDE(推荐使用最新...
通过 Maven 创建 Web 项目能够帮助开发者快速搭建开发环境,实现自动化构建、依赖管理和部署等功能。本文将详细介绍如何使用 Maven 在 Eclipse 中创建一个 Web 工程。 #### 二、准备工作 在开始之前,请确保已经...
idea中利用maven创建web项目, 并通过tomcat进行部署,运行项目
在当今的软件开发领域,Java作为一种编程语言广泛应用于各个项目开发中,而Maven作为Java项目中一个十分流行的项目管理和构建工具,扮演着非常重要的角色。Maven不仅能帮助开发者管理项目依赖、自动化构建过程、提供...
本文将深入探讨如何在Eclipse中利用Maven创建一个Web应用程序项目。 首先,我们需要理解“Eclipse使用Maven无法建web项目”这个问题可能涉及到的几个关键点。在Eclipse中创建Maven Web项目时,可能会遇到诸如Maven...
Maven项目创建流程是Java开发中一个至关重要的环节,它可以帮助开发者规范地组织项目结构,并自动管理项目的依赖关系。以下是一个详细的Maven项目创建步骤,包括创建父项目、子模块以及设置相关的配置信息。 1. **...
在IDEA(IntelliJ IDEA)中创建这样的项目,开发者可以充分利用其强大的代码编辑、调试、版本控制和项目管理功能,提高开发效率。IDEA的Maven支持允许用户轻松管理依赖关系,构建和运行项目。 【标签】"maven web...
1. **初始化项目**:创建一个新的Maven项目,设置POM.xml文件,这是Maven项目的配置中心,包含项目的基本信息和依赖。 2. **添加SSH框架依赖**:在POM.xml中,你需要指定SSH框架的版本号,并添加相应的依赖。 3. *...
"Eclipse创建基于MAVEN的web项目" 标题解释 本文主要介绍如何使用Eclipse创建基于Maven的Web项目,包括建立Maven项目、配置项目、构建框架等步骤。 描述解释 使用Eclipse创建Maven版的Web项目需要通过Maven的...
本教程通过实例演示了如何利用Maven创建一个多模块Java工程,并将其集成到Eclipse环境中,为开发者提供了一条清晰的项目构建和管理路径。无论是初学者还是经验丰富的开发者,都能从中受益,提升项目构建效率和代码...