`
wade6
  • 浏览: 273470 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

利用maven创建webx3项目——实现简单的留言板(八)

 
阅读更多

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>留言列表&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
	<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>

分享到:
评论
2 楼 wade6 2013-10-10  
xujun122 写道
正在学习使用webx,网上完整的例子比较少,看了你的这系列webx笔记,受益良多,特地留言感谢下博主

1 楼 xujun122 2013-09-11  
正在学习使用webx,网上完整的例子比较少,看了你的这系列webx笔记,受益良多,特地留言感谢下博主

相关推荐

    基于maven创建web项目

    基于maven创建web项目 maven是一种基于项目对象模型(Project Object Model,POM)的项目管理工具,由Apache软件基金会开发和维护。maven提供了一种标准化的方式来构建、打包和部署项目,它可以帮助开发者简化项目...

    使用Idea14.1.4和maven创建java web项目

    ### 使用Idea14.1.4和Maven创建Java Web项目 #### 一、概述 在本篇文章中,我们将详细介绍如何使用IntelliJ IDEA 14.1.4版本结合Maven来创建一个Java Web项目。这种方法不仅能够提高开发效率,还能确保项目的结构...

    idea使用maven创建web项目详细教程

    IDEA 使用 Maven 创建 Web 项目详细教程 本教程将指导您使用 IDEA 创建一个 Maven Web 项目,从安装 Maven 到配置 Maven 环境、创建 Maven 工程、pom.xml 文件配置、依赖管理等。 1. 安装 Maven 在 IDEA 中使用 ...

    idea使用maven创建的web项目

    【标题】:“idea使用maven创建的web项目” 在Java开发中,IntelliJ IDEA(简称Idea)作为一款强大的集成开发环境,结合Maven构建工具,能够帮助开发者高效地管理项目的依赖、构建和部署。Maven是一个项目管理和...

    使用Maven构建多模块项目

    本文将深入探讨如何利用Maven构建多模块项目,以及在实际操作中需要注意的关键点。 1. Maven多模块项目结构: Maven多模块项目通常遵循一个标准的目录结构,包括一个顶级父 pom.xml(如test-hd-parent),和若干子...

    项目管理利器——maven

    项目管理利器——maven,技术开发学习类稳定档案顶顶顶顶

    java创建一个简单的Maven项目

    java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一个简单的Maven项目java创建一...

    mavenWeb空项目

    【标题】"mavenWeb空项目"所涉及的知识点主要...综上所述,"mavenWeb空项目"是一个包含Java Web开发基础的项目模板,利用Maven进行构建管理和单元测试,为开发者提供了一个良好的起点,便于快速开发和迭代Web应用程序。

    maven创建实战web项目

    本篇文章将详细介绍如何利用Maven创建一个Servlet版本为3.0、Java版本为1.7的Web项目,并对创建过程中可能出现的问题及解决方案进行深入探讨。 #### 二、准备工作 1. **安装环境**: - Eclipse IDE(推荐使用最新...

    maven创建web工程

    通过 Maven 创建 Web 项目能够帮助开发者快速搭建开发环境,实现自动化构建、依赖管理和部署等功能。本文将详细介绍如何使用 Maven 在 Eclipse 中创建一个 Web 工程。 #### 二、准备工作 在开始之前,请确保已经...

    idea中利用maven创建web项目, 并通过tomcat进行部署,运行项目

    idea中利用maven创建web项目, 并通过tomcat进行部署,运行项目

    使用maven创建多模块项目

    在当今的软件开发领域,Java作为一种编程语言广泛应用于各个项目开发中,而Maven作为Java项目中一个十分流行的项目管理和构建工具,扮演着非常重要的角色。Maven不仅能帮助开发者管理项目依赖、自动化构建过程、提供...

    Eclipse使用Maven无法建web项目

    本文将深入探讨如何在Eclipse中利用Maven创建一个Web应用程序项目。 首先,我们需要理解“Eclipse使用Maven无法建web项目”这个问题可能涉及到的几个关键点。在Eclipse中创建Maven Web项目时,可能会遇到诸如Maven...

    Maven项目创建流程

    Maven项目创建流程是Java开发中一个至关重要的环节,它可以帮助开发者规范地组织项目结构,并自动管理项目的依赖关系。以下是一个详细的Maven项目创建步骤,包括创建父项目、子模块以及设置相关的配置信息。 1. **...

    maven多模块web项目

    在IDEA(IntelliJ IDEA)中创建这样的项目,开发者可以充分利用其强大的代码编辑、调试、版本控制和项目管理功能,提高开发效率。IDEA的Maven支持允许用户轻松管理依赖关系,构建和运行项目。 【标签】"maven web...

    构建基于Maven的SSH原型项目

    1. **初始化项目**:创建一个新的Maven项目,设置POM.xml文件,这是Maven项目的配置中心,包含项目的基本信息和依赖。 2. **添加SSH框架依赖**:在POM.xml中,你需要指定SSH框架的版本号,并添加相应的依赖。 3. *...

    Eclipse创建基于MAVEN的web项目

    "Eclipse创建基于MAVEN的web项目" 标题解释 本文主要介绍如何使用Eclipse创建基于Maven的Web项目,包括建立Maven项目、配置项目、构建框架等步骤。 描述解释 使用Eclipse创建Maven版的Web项目需要通过Maven的...

    Maven创建Java工程

    本教程通过实例演示了如何利用Maven创建一个多模块Java工程,并将其集成到Eclipse环境中,为开发者提供了一条清晰的项目构建和管理路径。无论是初学者还是经验丰富的开发者,都能从中受益,提升项目构建效率和代码...

Global site tag (gtag.js) - Google Analytics