作了一年多的python 方面的web开发工做,昨个有个同窗问我Django的安全机制,我是一脸的茫然。天天使用公司开发框架的我,对这些东西了解的甚少,俨然成为一个真正的"码农",只知其然而不知其因此然。我要改变,故从网络上,查了下,记下被查,内容以下:java
介绍python
Spring是Java语言开发的一站式Web框架。包括:SpringMVC,Spring,SpringSecurity,SpringAOP等子框架。Spring在数据库访问层能够整合Hibernate,iBatis等第三方框架。构成了一个完整的Web应用程序框架。程序员
Spring大量使用了策略模式、模板方法模式,提供了钩子回调第三方的API,于是能够整合大量第三方框架。web
Django是Python语言开发的一站式Web应用程序框架。其独立开发了从Web层到数据库访问层在内的全部框架。数据库
Spring和Django在功能上基本对等,都是Web应用程序开发的基础平台。django
Django利用了Python语言自身的优点,优雅地实现了一整套Web应用框架。编程
Spring为人广为诟病的一点就是,其大量使用了xml格式的配置文件。配置工做量至关大。在Java5引入annotation以后,虽然能够在Java源码中直接加上配置。可是每次修改配置必须从新编译程序,殊为不便!浏览器
Python是动态语言,于是Django直接使用Python源代码做为配置文件。Python程序能够即时编译,不须要额外的编译过程。安全
本文将对Spring和Django的安全机制作一系统比较。服务器
大部分Web应用程序在Action层实现安全控制。Action层负责接收请求和发出响应消息的这一层次。
一般的作法是,在数据库中为用户定义权限。每个权限使用一个URL标识。
用户登陆后发给用户浏览器一个cookie。服务器端也保存这个cookie,并把这个cookie和一个用户关联起来。
用户再次发出请求以后,根据用户发来的cookie到数据库中查询对应的用户,取得User对象和相应的权限集合。保存在HttpRequest或者HttpSession中,或者ThreadLocal中。
编写一个Filter,对Http请求进行预过滤。比对User的权限中是否有这个URL。若是没有,那么就直接返回错误消息,不会把这个request发送到URL对应的Action方法中处理。
这一方案能够在Action层实现安全控制,有效拦截非法访问。
Java的EJB框架也有本身的基于角色的一套安全控制机制。它能够对EJB对象而不是Action层实现对服务的访问控制,粒度更低。
可是使用EJB安全机制很麻烦。必须按照EJB的要求定义角色和安全模型,必须编写大段的xml配置文件指定访问控制策略。
EJB在Business层实现了安全控制,这对于EJB架构的程序是有意义的。由于EJB架构中,EJB是独立部署的服务组件。客户端使用RMI远程协议访问它。
EJB的客户端能够是Web服务器,也能够是富客户端程序。
可是,EJB这样的架构是否必须呢?这个在业界有很长时间的争论。不少人包括笔者本人都认为EJB这种架构已通过时了。
富客户端程序一样能够经过Http协议与Web服务器程序通讯。Web服务器能够同时支持B/S和C/S双架构。
Web服务器程序一样也能够提供TCP/UDP接口供富客户端程序访问。
最后一个问题,如EJB这样把Business组件单独部署是否有必要?EJB集群 VS Web服务器集群谁优谁劣?
Web服务器同时包括http接口和Business逻辑。Web服务器能够和EJB同样实现集群部署。EJB服务器使用RMI对外接口通信。Web服务器使用Http对外接口通信。应该说EJB集群没有提供比Web服务器集群更多的优点。
所以,我认为EJB安全机制并不比HttpRequest拦截安全机制更优秀。
Spring和Django都使用了AOP(面向方面编程)技术来实现安全控制。
SpringAOP是Spring开发的一个AOP框架。利用了Java动态运行的特性,使用反射技术实现了面向方面(AOP)编程。
Spring框架负责安全的子系统是Spring Security框架。Spring Security就是使用Spring AOP实现安全控制的。
Python与生俱来就支持AOP。Python的适配器函数就能够轻松实现AOP。
Python的装饰者函数在语法上和Java的Annotation很类似,但实际实现彻底不一样。
Java的Annotation是运行时能够经过发射获得的描述型数据。
Annotation在Python中的对应物是Python中的Doc。Doc也是在运行时能够获得的描述型数据,用于生成JavaDoc这样的文档,或者是运行时经过help(模块名/类名/函数名)获得帮助信息。Python的Doc通常没有像Java的Annotation这样使用的。
Python的装饰者函数不是运行时能够获得的元数据。Python的装饰者函数就是一个普通的Python函数。它的第一个参数是被修饰的函数。所以能够直接实现AOP中的round。咱们知道AOP包括3种拦截机制:before,after和round。Round是同时before和after。
所以Python的装饰者函数直接等价于Java的AOP。
下面内容摘自Spring3.0指南:
Spring Security能够用在多种不一样的验证环境下。咱们推荐人们使用Spring Security进行验证,而不是与现存的容器管理验证相结合,然而这种方式也是被支持的 - 做为与你本身的验证系统相整合的一种方式。
什么是Spring Security的验证呢?
让咱们考虑一种标准的验证场景,每一个人都很熟悉的那种。
1.一个用户想使用一个帐号和密码进行登录。
2.系统(成功的)验证了密码对于这个用户名 是正确的。
3.这个用户对应的信息呗获取 (他们的角色列表以及等等)。
4.为用户创建一个安全环境。
5.用户会执行一些操做,这些都是潜在被 权限控制机制所保护的,经过对操做的受权, 使用当前的安全环境信息。【函数须要校验用户的权限是否知足自身的要求。所以函数必须知道从哪里得到用户的受权信息】
前三个项目执行了验证过程,因此咱们能够看一下 Spring Security的做用。
1.用户名和密码被得到,并进行比对, 在一个UsernamePasswordAuthenticationToken的实例中 (它是Authentication接口的一个实例, 咱们在以前已经见过了)。
2.这个标志被发送给一个AuthenticationManager 的实例进行校验。
3.AuthenticationManager返回一个彻底的 Authentication实例, 在成功校验后。
4.安全环境被创建,经过调用 SecurityContextHolder.getContext().setAuthentication(...), 传递到返回的验证对象中。
In Spring Security, the responsibility for storing the
SecurityContext between requests falls to the SecurityContextPersistenceFilter,
which by default stores the context as an HttpSession attribute between HTTP requests.
It restores the context to the SecurityContextHolder for each request and, crucially, clears the SecurityContextHolder when the request completes.
技术说明:
SecurityContext是Spring Security框架保存用户受权信息的对象。在用户登陆时建立。
每一次请求开始时,Spring Security使用Filter把HttpSession中的SecurityContext恢复到 SecurityContextHolder中。SecurityContextHolder是一个Java类,包含多个静态函数。
用SecurityContextHolder的方法:public static void setContext(SecurityContext context)方法把SecurityContext对象保存到ThreadLocal中。
方法原型:
setContext
public static void setContext(SecurityContext context)
Associates a new SecurityContext with the current thread of execution.
Parameters:
context - the new SecurityContext (may not be null)
SecurityContextHolder.getContext()方法返回保存在ThreadLocal中的SecurityContext对象。
由于Action层和Service层都在同一个Thread下执行。所以Action层AOP存放下SecurityContext能够被Service层重用。
用户不该该直接操做HttpSession中的SecurityContext对象。老是应该用 SecurityContextHolder提供的方法获取SecurityContext对象。
每一次请求结束时,Filter都会把当前线程中的SecurityContext对象清除。由于线程可能会被重用。不清除可能会引起安全问题。
Spring Security可使用xml配置文件或者java annotation在业务层对方法声明安全限制。Spring Security使用Spring AOP技术在业务层方法执行时对其拦截,用SecurityContextHolder.getContext()对象内的用户受权和函数上声明的受权进行比对。若是不符合,就抛出异常返回。从而实现了对业务层方法的安全控制。
Django使用App的概念实现各个子框架。Django负责安全的子框架是Auth应用。
Django用户登陆后,会在HttpSession中保存User对象。在Aciton层(Django的术语是View,我在本文中为了和Java术语相同,使用Action代替View)能够获得User对象及其Perm受权集合。程序员能够在Action层中手工对用户的权限和Action要求的权限进行比对,实现访问控制。
这和前文HttpRequest拦截方法相似。
可是,Django的能力不止与此。使用Python装饰者函数,Django可使用相似于Spring Security的anontation的语法对Aciton函数实现声明式的安全控制!
如:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
# ...
这里的@login_required声明表示必须是登陆用户才能够调用这个Action(View)函数。@login_required就是Python的装饰者函数。
更进一步的访问限制:
from django.contrib.auth.decorators import permission_required
@permission_required('polls.can_vote', login_url="/login/")
def vote(request):
# ...
注意, permission_required() 也有一个可选的login_url 参数, 这个参数默认为'/accounts/login/' 。
这里能够根据用户的权限对Action进行限制。若是用户通不过受权,就返回给用户一个登陆页面。
这些都不须要程序员编写一行代码!神奇吧?!
比较:
Python Django框架只能在Action层进行ACL控制。由于它没有使用Filter在ThreadLocal中保存User信息。所以在Service层中没法得到User信息。
Python自身支持AOP,也支持ThreadLocal。所以Django也能够像Spring Security同样提供对业务层函数的安全控制。只是Django没有这样作而已。
也许是Django做为一个相对新生的社区,没有精力作这件事。或者更可能的是,Django社区的人认为在业务层实现安全控制没有必要。Action层直接控制不就完了吗?