Spring Security 3.2.x 配置

 

使用 Spring Security 保护 Web 应用的安全 http://www.ibm.com/developerworks/cn/java/j-lo-springsecurity/
SpringSecurity方法层4种方式使用 http://blog.csdn.net/wyabc1986/article/details/8424688
Spring Security 3.1.4 版本开发解读 http://fantasyeye.iteye.com/blog/1938046
Spring Security 3.1 自定义 authentication provider http://www.xeclipse.com/?p=1359
SSH框架结合Spring Security3新手入门 http://jusesgod.iteye.com/blog/1141408
Spring Security 中的盐值加密 http://nhy520.iteye.com/blog/801488
FilterInvocationSecurityMetadataSource 资源角色授权器需要实现FilterInvocationSecurityMetadataSource接口。请求的资源所需要的角色权限在服务器启动时候就已经确定的,所以在该实现类的构造方法中需要确定每一种资源需要那些角色权限。详细参考:SpringSecurity—–登录用户权限验证demohttp://201205164957.iteye.com/blog/1627200和spring security 3 中使用自定义数据库来设置权限 http://blog.csdn.net/remote_roamer/article/details/5713777
Spring Security3 页面 权限标签 http://hehch.iteye.com/blog/1409959
Spring Security默认的403页面 http://www.it161.com/article/javaDetail?articleid=140113232712,两种方法:
1. <access-denied-handler error-page=”/403.jsp”/>,这里的error-page好像必须是/符号开头,否则报错的。
2. hanlder方法 http://naozao.com/topic/view/133.html,自定义自己的方法,可以显示更多的信息。
Spring Security 常用的几个自定义filter http://www.quanlei.com/2011/01/2029.html
在spring security获取当前对象 http://zm2011.iteye.com/blog/1319577

SecurityContext 和 Authentication 对象
下面开始讨论几个 Spring Security 里面的核心对象。org.springframework.security.core.context.SecurityContext接口表示的是当前应用的安全上下文。通过此接口可以获取和设置当前的认证对象。org.springframework.security.core.Authentication接口用来表示此认证对象。通过认证对象的方法可以判断当前用户是否已经通过认证,以及获取当前认证用户的相关信息,包括用户名、密码和权限等。要使用此认证对象,首先需要获取到 SecurityContext 对象。通过 org.springframework.security.core.context.SecurityContextHolder 类提供的静态方法 getContext() 就可以获取。再通过 SecurityContext对象的 getAuthentication()就可以得到认证对象。通过认证对象的 getPrincipal() 方法就可以获得当前的认证主体,通常是 UserDetails 接口的实现。联系到上一节介绍的 UserDetailsService,典型的认证过程就是当用户输入了用户名和密码之后,UserDetailsService通过用户名找到对应的 UserDetails 对象,接着比较密码是否匹配。如果不匹配,则返回出错信息;如果匹配的话,说明用户认证成功,就创建一个实现了 Authentication接口的对象,如 org.springframework.security. authentication.UsernamePasswordAuthenticationToken 类的对象。再通过 SecurityContext的 setAuthentication() 方法来设置此认证对象。

5.2.1. SecurityContextHolder, SecurityContext 和 Authentication对象
http://www.fengfly.com/document/springsecurity3/technical-overview.html
最基础的对象就是SecurityContextHolder。 我们把当前应用程序的当前安全环境的细节存储到它里边了, 它也包含了应用当前使用的主体细节。 默认情况下,SecurityContextHolder使用ThreadLocal存储这些信息, 这意味着,安全环境在同一个线程执行的方法一直是有效的, 即使这个安全环境没有作为一个方法参数传递到那些方法里。 这种情况下使用ThreadLocal是非常安全的, 只要记得在处理完当前主体的请求以后,把这个线程清除就行了。 当然,Spring Security自动帮你管理这一切了, 你就不用担心什么了。
有些程序并不适合使用ThreadLocal, 因为它们处理线程的特殊方法。比如,swing客户端也许希望 JVM里的所有线程都使用同一个安全环境。 SecurityContextHolder可以使用一个策略进行配置 在启动时,指定你想让上下文怎样被保存。对于一个单独的应用系统,你可以使用 SecurityContextHolder.MODE_GLOBAL策略。 其他程序可能想让一个线程创建的线程也使用相同的安全主体。 这时可以使用 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL。 想要修改默认的SecurityContextHolder.MODE_THREADLOCAL模式,可以使用两种方法。 第一个是设置系统属性。另一个是调用 SecurityContextHolder的静态方法。大多数程序不需要修改默认值, 但是如果你需要做修改,先看一下 SecurityContextHolder的JavaDoc中的详细信息。
5.2.1.1. 获得当前用户的信息
我们把安全主体和系统交互的信息都保存在 SecurityContextHolder中了。 Spring Security使用一个Authentication对应来表现这些信息。 虽然你通常不需要自己创建一个Authentication对象, 但是常见的情况是,用户查询 Authentication对象。你可以使用下面的代码 – 在你程序中的任何位置 – 来获得已认证用户的名字, 比如:

调用getContext()返回的对象是一个 SecurityContext接口的实例。 这个对象是保存在thread-local中的。如我们下面看到的,大多数Spring Security的验证机制 都返回一个UserDetails的实例 作为主体。
5.2.2. UserDetailsService
从上面的代码片段中还可以看出另一件事,就是你可以从Authentication对象中获得安全主体。 这个安全主体就是一个对象。 大多数情况下,可以强制转换成UserDetails对象。 UserDetails是一个Spring Security的核心接口。 它代表一个主体,是扩展的,而且是为特定程序服务的。 想一下UserDetails章节,在你自己的用户数据库和如何把Spring Security需要的数据放到SecurityContextHolder里。 为了让你自己的用户数据库起作用,我们常常把UserDetails转换成你系统提供的类,这样你就可以直接调用业务相关的方法了(比如getEmail(), getEmployeeNumber()等等)。
现在,你可能想知道,我应该什么时候提供这个UserDetails对象呢? 我怎么做呢? 我想你说这个东西是声明式的,我不需要写任何代码,怎么办? 简单的回答是,这里有一个特殊的接口,叫UserDetailsService。 这个接口里的唯一一个方法,接收String类型的用户名参数,返回UserDetails:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
这是获得从Spring Security中获得用户信息的最常用方法,你会看到它在框架中一直被用到。 当需要获得一个用户的信息的时候。
当成功通过验证时,UserDetails会被用来 建立Authentication对象,保存在SecurityContextHolder里。 (更多的信息可以参考下面的#tech-intro-authentication-mgr)。 好消息是我们提供了好几个UserDetailsService实现,其中一个使用了 内存中的map(InMemoryDaoImpl)另一个而是用了JDBC (JdbcDaoImpl)。 虽然,大多数用户倾向于写自己的,使用这些实现常常放到已有的数据访问对象(DAO)上,表示它们的雇员,客户或其他企业应用中的用户。 记住这个优势,无论你用什么UserDetailsService返回的数据都可以通过SecurityContextHolder获得,就像上面的代码片段讲的一样。
5.2.3. GrantedAuthority
除了主体,另一个Authentication提供的重要方法是getAuthorities()。 这个方法提供了GrantedAuthority对象数组。 毫无疑问,GrantedAuthority是赋予到主体的权限。 这些权限通常使用角色表示,比如ROLE_ADMINISTRATOR 或 ROLE_HR_SUPERVISOR。 这些角色会在后面,对web验证,方法验证和领域对象验证进行配置。 Spring Security的其他部分用来拦截这些权限,期望他们被表现出现。 GrantedAuthority对象通常使用UserDetailsService读取的。
通常情况下,GrantedAuthority对象是应用程序范围下的授权。 它们不会特意分配给一个特定的领域对象。 因此,你不能设置一个GrantedAuthority,让它有权限展示编号54的Employee对象,因为如果有成千上网的这种授权,你会很快用光内存(或者,至少,导致程序花费大量时间去验证一个用户)。 当然,Spring Security被明确设计成处理常见的需求,但是你最好别因为这个目的使用项目领域模型安全功能。
5.2.4. 小结
简单回顾一下,Spring Security主要是由一下几部分组成的:
SecurityContextHolder,提供几种访问SecurityContext的方式。
SecurityContext,保存Authentication信息,和请求对应的安全信息。
HttpSessionContextIntegrationFilter,为了在不同请求使用,把SecurityContext保存到 HttpSession里。
Authentication,展示Spring Security特定的主体。
GrantedAuthority,反应,在应用程序范围你,赋予主体的权限。
UserDetails,通过你的应用DAO,提供必要的信息,构建Authentication对象。
UserDetailsService,创建一个 UserDetails,传递一个 String类型的用户名(或者证书ID或其他)。

文章说明和配置都是参考Spring Security 3.1.4 版本开发解读

源码:
pom.xml
============================

web.xml
=========================================

数据库
===============================

省略掉了其他applicationContext的xml配置文件,只给security的配置
applicationContext-security.xml
==================================================

<custom-filterref=”myFilter”before=”FILTER_SECURITY_INTERCEPTOR”/>这里的FILTER_SECURITY_INTERCEPTOR是SpringSecurity默认的Filter,
我们自定义的Filter必须在它之前,过滤客服请求。用一个继承AbstractSecurityInterceptor实现类来完成这个工作。

一个错误转向过滤器和四个security的实现类
============================================================

编写自定义过滤器,必须继承 org.springframework.security.access.intercept.AbstractSecurityInterceptor 和 实现 javax.servlet.Filter 接口。重写 doFilter 方法并新增属性 FilterInvocationSecurityMetadataSource 对象的 getter 和 setter 方法,用于 Spring 注入。

如之前的配置,我们需要 Spring 帮我们注入 securityMetadataSource、authenticationManager、accessDecisionManager 三个类对象。那他们分别是做什么的呢?现在听我慢慢道来。
securityMetadataSource 这个类型的接口,提供了根据访问资源获取角色集合的接口,也就是说此类维护着,资源和角色的关系并提供外界使用。 securityMetadataSource 必须是 FilterInvocationSecurityMetadataSource 接口实现类。看看我写的自定义实现类:
在 loadResourceDefine 方法中,初始化了资源。将资源和权限以 Map 的形式做了映射。一个地址会对应一组权限。然后实现了接口方法 public Collection<ConfigAttribute> getAttributes(Object object) ,可以通过此方法获取权限集合。这个接口的调用在 AbstractSecurityInterceptor 的 beforeInvocation 方法中。

读取资源信息的service,
实现类中,我们可以通过 loadUserByUsername 方法,根据用户名找到该用户的基本信息和角色信息。并创建 UserDetails 实现类对象返回。我们在这里设置了角色集合对象 array 并将其赋值给了User 对象。
==================================================

当用户需要访问某个资源是,我们就可以通过这两个对象在 accessDecisionManager 引用的 AccessDecisionManager 接口实现类中,进行比较了。

两个简单的工具类:PasswordUtil,SpringSecurityUtil

 

一个查询资源的service, 资源源数据定义, 将所有的资源和权限的对应关系建立起来
==================================

controller
=============================

login.jsp
============================

测试页面:index.jsp
======================================