Spring Security
Table of contents
-
第 6 章 控制用户信息让我们来研究一些与用户信息相关的功能,包括为用户密码加密,缓存用户信息,获得系统当前登陆的用户,获得登陆用户的所有权限。6.1. MD5加密任何一个正式的企业应用中,都不会在数据库中使用明文来保存密码的,我们在之前的章节中都是为了方便起见没有对数据库中的用户密码进行加密,这在实际应用中是极为幼稚的做法。可以想象一下,只要有人进入数据库就可以看到所有人的密码,这是一件多么恐怖的事情,为此我们至少要对密码进行加密,这样即使数据库被攻破,也可以保证用户密码的安全。最常用的方法是使用MD5算法对密码进行摘要加密,这是一种单项加密手段,无法通过加密后的结果反推回原来的密码明文。为了使用MD5对密码加密,我们需要修改一下配置文件。 <authentication-provider> <password-encoder hash="md5"/> <jdbc-user-service data-source-ref="dataSource"/> </authentication-provider> 上述代码中新增的黄色部分,将启用md5算法。这时我们在数据库中保存的密码已经不再是明文了,它看起来像是一堆杂乱无章的乱码。 INSERT INTO USERS VALUES('admin','21232f297a57a5a743894a0e4a801fc3',TRUE) INSERT INTO USERS VALUES('user','ee11cbb19052e40b07aac0ca060c23ee',TRUE) 可以看到密码部分已经面目全非了,即使有人攻破了数据库,拿到这种“乱码”也无法登陆系统窃取客户的信息。这些配置对普通客户不会造成任何影响,他们只需要输入自己的密码,Spring Security会自动加以演算,将生成的结果与数据库中保存的信息进行比对,以此来判断用户是否可以登陆。这样,我们只添加了一行配置,就为系统带来了密码加密的功能。6.2. 盐值加密实际上,上面的实例在现实使用中还存在着一个不小的问题。虽然md5算法是不可逆的,但是因为它对同一个字符串计算的结果是唯一的,所以一些人可能会使用“字典攻击”的方式来攻破md5加密的系统[5]。这虽然属于暴力解密,却十分有效,因为大多数系统的用户密码都不回很长。实际上,大多数系统都是用admin作为默认的管理
-
第 5 章 使用数据库管理资源国内对权限系统的基本要求是将用户权限和被保护资源都放在数据库里进行管理,在这点上Spring Security并没有给出官方的解决方案,为此我们需要对Spring Security进行扩展。5.1. 数据库表结构这次我们使用五张表,user用户表,role角色表,resc资源表相互独立,它们通过各自之间的连接表实现多对多关系。 -- 资源 create table resc( id bigint, name varchar(50), res_type varchar(50), res_string varchar(200), descn varchar(200) ); alter table resc add constraint pk_resc primary key(id); alter table resc alter column id bigint generated by default as identity(start with 1); -- 角色 create table role( id bigint, name varchar(50), descn varchar(200) ); alter table role add constraint pk_role primary key(id); alter table role alter column id bigint generated by default as identity(start with 1); -- 用户 create table user( id bigint, username varchar(50), password varchar(50), status integer, descn varchar(200) ); alter table user add constraint pk_user primary key(id); alter table user alter column id bigint generated by default as identity(start with 1); -- 资源角色连接表 create table resc_role( resc_id bigint, role_id bigint );
-
第 4 章 自定义登陆页面Spring Security虽然默认提供了一个登陆页面,但是这个页面实在太简陋了,只有在快速演示时才有可能它做系统的登陆页面,实际开发时无论是从美观还是实用性角度考虑,我们都必须实现自定义的登录页面。4.1. 实现自定义登陆页面自己实现一个login.jsp,放在src/main/webapp/目录下。 + ch04/ + src/ + main/ + resources/ * applicationContext.xml + webapp/ + WEB-INF/ * web.xml * admin.jsp * index.jsp * login.jsp + test/ + resources/ * pom.xml 4.2. 修改配置文件在xml中的http标签中添加一个form-login标签。 <http auto-config='true'> <intercept-url pattern="/login.jsp" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern="/**" access="ROLE_USER" /> <form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?error=true" default-target-url="/" /> </http> 让没登陆的用户也可以访问login.jsp。[2]这是因为配置文件中的“/**”配置,要求用户访问任意一个系统资源时,必须拥有ROLE_USER角色,/login.jsp也不例外,如果我们不为/login.jsp单独配置访问权限,会造成用户连登陆的权限都没有,这是不正确的。 login-page表示用户登陆时显示我们自定义的login.jsp。这时我们访问系统显示的登陆页面将是我们上面创建的login.jsp。 authentication-failure-url表示用户登陆失败时,跳转到哪个页面。当用户输入的登录名和密码不正确时,系统将再次跳转到/login.jsp,并添加一个error
-
第 7 章 图解过滤器图 7.1. auto-config='true'时的过滤器列表7.1. HttpSessionContextIntegrationFilter图 7.2. org.springframework.security.context.HttpSessionContextIntegrationFilter位于过滤器顶端,第一个起作用的过滤器。用途一,在执行其他过滤器之前,率先判断用户的session中是否已经存在一个SecurityContext了。如果存在,就把SecurityContext拿出来,放到SecurityContextHolder中,供Spring Security的其他部分使用。如果不存在,就创建一个SecurityContext出来,还是放到SecurityContextHolder中,供Spring Security的其他部分使用。用途二,在所有过滤器执行完毕后,清空SecurityContextHolder,因为SecurityContextHolder是基于ThreadLocal的,如果在操作完成后清空ThreadLocal,会受到服务器的线程池机制的影响。7.2. LogoutFilter图 7.3. org.springframework.security.ui.logout.LogoutFilter只处理注销请求,默认为/j_spring_security_logout。用途是在用户发送注销请求时,销毁用户session,清空SecurityContextHolder,然后重定向到注销成功页面。可以与rememberMe之类的机制结合,在注销的同时清空用户cookie。7.3. AuthenticationProcessingFilter图 7.4. org.springframework.security.ui.webapp.AuthenticationProcessingFilter处理form登陆的过滤器,与form登陆有关的所有操作都是在此进行的。默认情况下只处理/j_spring_security_check请求,这个请求应该是用户使用form登陆后的提交地址,form所需的其他参数可以参考:???。此过滤器执行的基本操作时,通过用户名和密码判断用户是否有效,如果登录成功就跳转到成功页面(可能是登陆之前访问
-
第 1 章 一个简单的HelloWorldSpring Security中可以使用Acegi-1.x时代的普通配置方式,也可以使用从2.0时代才出现的命名空间配置方式,实际上这两者实现的功能是完全一致的,只是新的命名空间配置方式可以把原来需要几百行的配置压缩成短短的几十行。我们的教程中都会使用命名空间的方式进行配置,凡事务求最简。1.1. 配置过滤器为了在项目中使用Spring Security控制权限,首先要在web.xml中配置过滤器,这样我们就可以控制对这个项目的每个请求了。 <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 所有的用户在访问项目之前,都要先通过Spring Security的检测,这从第一时间把没有授权的请求排除在系统之外,保证系统资源的安全。关于过滤器配置的更多讲解可以参考http://www.family168.com/tutorial/jsp/html/jsp-ch-07.html#jsp-ch-07-03-01。1.2. 使用命名空间在applicationContext.xml中使用Spring Security提供的命名空间进行配置。 <?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springfram
-
第 3 章 自定义数据库表结构Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也无法满足所有企业内部对用户信息和权限信息管理的要求。基本上每个企业内部都有一套自己的用户信息管理结构,同时也会有一套对应的权限信息体系,如何让Spring Security在这些已有的数据结构之上运行呢?3.1. 自定义表结构假设我们实际使用的表结构如下所示: -- 角色 create table role( id bigint, name varchar(50), descn varchar(200) ); alter table role add constraint pk_role primary key(id); alter table role alter column id bigint generated by default as identity(start with 1); -- 用户 create table user( id bigint, username varchar(50), password varchar(50), status integer, descn varchar(200) ); alter table user add constraint pk_user primary key(id); alter table user alter column id bigint generated by default as identity(start with 1); -- 用户角色连接表 create table user_role( user_id bigint, role_id bigint ); alter table user_role add constraint pk_user_role primary key(user_id, role_id); alter table user_role add constraint fk_user_role_user foreign key(user_id) references user(id); alter table user_role add constraint fk_user_role_role foreign key(role_id) r
-
第 8 章 管理会话多个用户不能使用同一个账号同时登陆系统。8.1. 添加监听器在web.xml中添加一个监听器,这个监听器会在session创建和销毁的时候通知Spring Security。 <listener> <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class> </listener> 这种监听session生命周期的监听器主要用来收集在线用户的信息,比如统计在线用户数之类的事。有关如何自己编写listener统计在线用户数,可以参考:http://family168.com/tutorial/jsp/html/jsp-ch-08.html8.2. 添加过滤器在xml中添加控制同步session的过滤器。 <http auto-config='true'> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern="/**" access="ROLE_USER" /> <concurrent-session-control/> </http> 因为Spring Security的作者不认为控制会话是一个大家都经常使用的功能,所以concurrent-session-control没有包含在默认生成的过滤器链中,在我们需要使用它的时候,需要自己把它添加到http元素中。这个concurrent-session-control对应的过滤器类是org.springframework.security.concurrent.ConcurrentSessionFilter,它的排序代码是100,它会被放在过滤器链的最顶端,在所有过滤器使用之前起作用。8.3. 控制策略8.3.1. 后登陆的将先登录的踢出系统默认情况下,后登陆的用户会把先登录的用户踢出系统。想测试一下的话,先打开firefox使用user/user登陆系统,然后再打开ie使用user/user登陆系统。这时ie下的user用户会登陆成功,进入登陆成功页面。而firefox下的用户如何刷新页面,就会显示如下信息: This session has been e
-
第 10 章 basic认证basic认证是另一个常用的认证方式,与表单认证不同的是,basic认证常用于无状态客户端的验证,比如HttpInvoker或者Web Service的认证,这种场景的特点是客户端每次访问应用时,都在请求头部携带认证信息,一般就是用户名和密码,因为basic认证会传递明文,所以最好使用https传输数据。10.1. 配置basic验证如果在http中配置了auto-config="true"我们就不用再添加任何配置了,默认配置中已经包含了Basic认证功能。但是这同时也会激活form-login,因此我们将演示仅有basic验证的场景,为此需要去掉配置文件中的auto-config="true"。 <http auto-config="true"> <http-basic /> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern="/" access="ROLE_USER" /> </http> 删除了auto-config="true"之后,还要记得添加http-basic标签,这样我们的系统将仅仅使用basic认证方式来实现用户登录。现在我们访问系统时,不会再进入之前的登录页面,而是会显示浏览器原生的登录对话框。图 10.1. basic登录登录成功之后,我们可以在HTTP请求头部看到basic验证所需的属性Authorization。图 10.2. HTTP请求头最后需要注意的是,因为basic认证不使用session,所以无法与rememberMe功用。10.2. 编程实现basic客户端下面我们来示范一下如何使用basic认证。假设我们在basic.jsp中需要远程调用http://localhost:8080/ch10/admin.jsp的内容。这时为了能够通过Spring Security的权限检测,我们需要在请求的头部加上basic所需的认证信息。 String username = "admin"; String password = "admin"; byte[] token = (username + ":" + password).getBytes("utf-8"); String authorizati
-
第 12 章 自动登录如果用户一直使用同一台电脑上网,那么他可能希望不要每次上网都要进行登录这道程序,他们希望系统可以记住自己一段时间,这样用户就可以无需登录直接登录系统,使用其中的功能。rememberMe就给我们提供了这样一种便捷途径。12.1. 默认策略在配置文件中使用auto-config="true"就会自动启用rememberMe,之后,只要用户在登录时选中checkbox就可以实现下次无需登录直接进入系统的功能。图 12.1. 选中rememberMe默认有效时间是两周,启用rememberMe之后的两周内,用户都可以直接跳过系统,直接进入系统。实际上,Spring Security中的rememberMe是依赖cookie实现的,当用户在登录时选择使用rememberMe,系统就会在登录成功后将为用户生成一个唯一标识,并将这个标识保存进cookie中,我们可以通过浏览器查看用户电脑中的cookie。图 12.2. rememberMe cookie从上图中,我们可以看到Spring Security生成的cookie名称是SPRING_SECURITY_REMEMBER_ME_COOKIE,它的内容是一串加密的字符串,当用户再次访问系统时,Spring Security将从这个cookie读取用户信息,并加以验证。如果可以证实cookie有效,就会自动将用户登录到系统中,并为用户授予对应的权限。12.2. 持久化策略rememberMe的默认策略会将username和过期时间保存到客户主机上的cookie中,虽然这些信息都已经进行过加密处理,不过我们还可以使用安全级别更高的持久化策略。在持久化策略中,客户主机cookie中保存的不再用username,而是由系统自动生成的序列号,在验证时系统会将客户cookie中保存的序列号与数据库中保存的序列号进行比对,以确认客户请求的有效性,之后在比对成功后才会从数据库中取出对应的客户信息,继续进行认证和授权等工作。这样即使客户本地的cookie遭到破解,攻击者也只能获得一个序列号,而不是用户的登录账号。如果希望使用持久化策略,我们需要先在数据库中创建rememberMe所需的表。 create table persistent_logins ( username varchar(64) not null, se
-
第 13 章 匿名登录匿名登录,即用户尚未登录系统,系统会为所有未登录的用户分配一个匿名用户,这个用户也拥有自己的权限,不过他是不能访问任何被保护资源的。设置一个匿名用户的好处是,我们在进行权限判断时,可以保证SecurityContext中永远是存在着一个权限主体的,启用了匿名登录功能之后,我们所需要做的工作就是从SecurityContext中取出权限主体,然后对其拥有的权限进行校验,不需要每次去检验这个权限主体是否为空了。这样做的好处是我们永远认为请求的主体是拥有权限的,即便他没有登录,系统也会自动为他赋予未登录系统角色的权限,这样后面所有的安全组件都只需要在当前权限主体上进行处理,不用一次一次的判断当前权限主体是否存在。这就更容易保证系统中操作的一致性。13.1. 配置文件在配置文件中使用auto-config="true"就会启用匿名登录功能。在启用匿名登录之后,如果我们希望允许未登录就可以访问一些资源,可以在进行如下配置。 <http auto-config='true'> <intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern="/**" access="ROLE_USER" /> </http> 在access中指定IS_AUTHENTICATED_ANONYMOUSLY后,系统就知道此资源可以被匿名用户访问了。当未登录时访问系统的“/”,就会被自动赋以匿名用户的身份。我们可以使用taglib获得用户的权限主体信息。 <div> username : <sec:authentication property="name"/> | authorities: <sec:authentication property="authorities" var="authorities" scope="page"/> <c:forEach items="${authorities}" var="authority"> ${authority.authority} </c:forEach> </div> 当用户访问系统时,就会
-
第 14 章 防御会话伪造14.1. 攻击场景session fixation会话伪造攻击是一个蛮婉转的过程。比如,当我要是使用session fixation攻击你的时候,首先访问这个网站,网站会创建一个会话,这时我可以把附有jsessionid的url发送给你。 http://unsafe/index.jsp;jsessionid=1pjztz08i2u4i 你使用这个网址访问网站,结果你和我就会公用同一个jsessionid了,结果就是在服务器中,我们两人使用的是同一个session。这时我只要祈求你在session过期之前登陆系统,然后我就可以使用jsessionid直接进入你的后台了,然后可以使用你在系统中的授权做任何事情。简单来说,我创建了一个session,然后把jsessionid发给你,你傻乎乎的就使用我的session进行了登陆,结果等于帮我的session进行了授权操作,结果就是我可以使用一开始创建的session进入系统做任何事情了。与会话伪造的详细信息可以参考http://en.wikipedia.org/wiki/Session_fixation。14.2. 解决会话伪造解决session fix的问题其实很简单,只要在用户登录成功之后,销毁用户的当前session,并重新生成一个session就可以了。Spring Security默认就会启用session-fixation-protection,这会在登录时销毁用户的当前session,然后为用户创建一个新session,并将原有session中的所有属性都复制到新session中。如果希望禁用session-fixation-protection,可以在http中将session-fixation-protection设置为none。 <http auto-config='true' session-fixation-protection="none"> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" /> <intercept-url pattern="/**" access="ROLE_USER" /> </http> session-fixation-protection的值共有三个可供选择,none,migra
-
第 2 章 使用数据库管理用户权限上一章节中,我们把用户信息和权限信息放到了xml文件中,这是为了演示如何使用最小的配置就可以使用Spring Security,而实际开发中,用户信息和权限信息通常是被保存在数据库中的,为此Spring Security提供了通过数据库获得用户权限信息的方式。2.1. 修改配置文件为了从数据库中获取用户权限信息,我们所需要的仅仅是修改配置文件中的authentication-provider部分。将上一章配置文件中的user-service替换为jdbc-user-service,替换内容如下所示: <authentication-provider> <user-service> <user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" /> <user name="user" password="user" authorities="ROLE_USER" /> </user-service> </authentication-provider> 将上述红色部分替换为下面黄色部分。 <authentication-provider> <jdbc-user-service data-source-ref="dataSource"/> </authentication-provider> 现在只要再为jdbc-user-service提供一个dataSource就可以让Spring Security使用数据库中的权限信息了。在此我们使用spring创建一个演示用的dataSource实现,这个dataSource会连接到hsqldb数据库,从中获取用户权限信息。[1] <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <beans:property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <beans:property name="url" value="jdbc:hsqldb:res:/hsqldb/test"/> <beans:prope
-
第 9 章 单点登录所谓单点登录,SSO(Single Sign On),就是把N个应用的登录系统整合在一起,这样一来无论用户登录了任何一个应用,都可以直接以登录过的身份访问其他应用,不用每次访问其他系统再去登陆一遍了。Spring Security没有实现自己的SSO,而是整合了耶鲁大学单点登陆(JA-SIG),这是当前使用很广泛的一种SSO实现,它是基于中央认证服务CAS(Center Authentication Service)的结构实现的,可以访问它们的官方网站获得更详细的信息http://www.jasig.org/cas。在了解过这些基础知识之后,我们可以开始研究如何使用Spring Security实现单点登录了。9.1. 配置JA-SIG从JA-SIG的官方网站下载cas-server,本文写作时的最新稳定版为3.3.2。http://www.ja-sig.org/downloads/cas/cas-server-3.3.2-release.zip。将下载得到的cas-server-3.3.2-release.zip文件解压后,可以得到一大堆的目录和文件,我们这里需要的是modules目录下的cas-server-webapp-3.3.2.war。把cas-server-webapp-3.3.2.war放到ch09\server目录下,然后执行run.bat就可启动CAS中央认证服务器。我们已在pom.xml中配置好了启用SSL所需的配置,包括使用的server.jks和对应密码,之后我们可以通过https://localhost:9443/cas/login访问CAS中央认证服务器。图 9.1. 登陆页面默认情况下,只要输入相同的用户名和密码就可以登陆系统,比如我们使用user/user进行登陆。图 9.2. 登陆成功这就证明中央认证服务器已经跑起来了。下一步我们来配置Spring Security,让它通过中央认证服务器进行登录。9.2. 配置Spring Security9.2.1. 添加依赖首先要添加对cas的插件和cas客户端的依赖库。因为我们使用了maven2,所以只需要在pom.xml中添加一个依赖即可。 <dependency> <groupId>org.springframework.security</groupId> <art
-
第 11 章 标签库Spring Security提供的标签库,主要的用途是为了在视图层直接访问用户信息,再者就是为了对显示的内容进行权限管理。11.1. 配置taglib如果需要使用taglib,首先要把spring-security-taglibs-2.0.4.jar放到项目的classpath下,这在文档附带的实例中已经配置好了依赖。剩下 的只要在jsp上添加taglib的定义就可以使用标签库了。 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags"%> 11.2. authenticaitonauthentication的功能是从SecurityContext中获得一些权限相关的信息。可以用它获得当前登陆的用户名: <sec:authentication property="name"/> 获得当前用户所有的权限,把权限列表放到authorities变量里,然后循环输出权限信息: <sec:authentication property="authorities" var="authorities" scope="page"/> <c:forEach items="${authorities}" var="authority"> ${authority.authority} </c:forEach> 11.3. authorizeauthorize用来判断当前用户的权限,然后根据指定的条件判断是否显示内部的内容。 <sec:authorize ifAllGranted="ROLE_ADMIN,ROLE_USER"> admin and user </sec:authorize> <sec:authorize ifAnyGranted="ROLE_ADMIN,ROLE_USER"> admin or user </sec:authorize> <sec:authorize ifNotGranted="ROLE_ADMIN"> not admin </sec:authorize> ifAllGranted,只有当前用户同时拥有ROLE_ADMIN和ROLE_USER两个权限时,才能显示标签内部内容。 ifAnyGranted,如果当前用户拥有ROLE_ADMIN或RO
-
第 15 章 预先认证预先认证是指用户在进入系统给钱,就已经通过某种机制进行过身份认证,请求中已经附带了身份认证的信息,这时我们只需要从获得这些身份认证信息,并对用户进行授权即可。CAS, X509等都属于这种情况。Spring Security中专门为这种系统外预先认证的情况提供了工具类,这一章我们来看一下如何使用Pre-Auth处理使用容器Realm认证的用户。15.1. 为jetty配置Realm首先在pom.xml中配置jetty所需的Realm。 <userRealms> <userRealm implementation="org.mortbay.jetty.security.HashUserRealm"> <name>Preauth Realm</name> <config>realm.properties</config> </userRealm> </userRealms> 用户,密码,以及权限信息都保存在realm.properties文件中。 admin: admin,ROLE_ADMIN,ROLE_USER user: user,ROLE_USER test: test 我们配置了三个用户,分别是admin, user和test,其中admin拥有ROLE_ADMIN和ROLE_USER权限,user拥有ROLE_USER权限,而test没有任何权限。下一步在src/webapp/WEB-INF/web.xml中配置登录所需的安全权限。 <login-config> <auth-method>BASIC</auth-method> <realm-name>Preauth Realm</realm-name> </login-config> <security-role> <role-name>ROLE_USER</role-name> </security-role> <security-role> <role-name>ROLE_ADMIN</role-name> </security-role> <security-constraint> <web-resource-collection> <web-resource-name>All areas</web-resource-name> <url-pattern>/*</url-patte
-
第 18 章 digest认证digest认证比form-login和http-basic更安全的一种认证方式,尤其适用于不能使用https协议的场景。它与http-basic一样,都是不基于session的无状态认证方式。18.1. 配置digest验证因为digest不包含在命名空间中,所以我们需要配置额外的过滤器和验证入口。 <beans:bean id="digestProcessingFilter" class="org.springframework.security.ui.digestauth.DigestProcessingFilter"> <custom-filter position="BASIC_PROCESSING_FILTER" /> <beans:property name="authenticationEntryPoint" ref="digestProcessingFilterEntryPoint"/> <beans:property name="userDetailsService" ref="org.springframework.security.userdetails.memory.InMemoryDaoImpl"/> </beans:bean> <beans:bean id="digestProcessingFilterEntryPoint" class="org.springframework.security.ui.digestauth.DigestProcessingFilterEntryPoint"> <beans:property name="realmName" value="springsecurity"/> <beans:property name="key" value="changeIt"/> </beans:bean> 然后记得删除auto-config="true",去除默认的form-login和http-basic认证,并添加对验证入口的引用。 <http auto-config="true" entry-point-ref="digestProcessingFilterEntryPoint"> <intercept-url pattern="/admin.jsp" access="ROLE_AD
-
第 19 章 保护方法调用这里有三种方式可以选择:19.1. 控制全局范围的方法权限使用global-method-security和protect-point标签来管理全局范围的方法权限。为了在spring中使用AOP,我们要为项目添加几个依赖库。 <dependency> <groupId>cglib</groupId> <artifactId>cglib-nodep</artifactId> <version>2.1_3</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.4</version> </dependency> 首先来看看我们将要保护的java类。 package com.family168.springsecuritybook.ch12; public class MessageServiceImpl implements MessageService { public String adminMessage() { return "admin message"; } public String adminDate() { return "admin " + System.currentTimeMillis(); } public String userMessage() { return "user message"; } public String userDate() { return "user " + System.currentTimeMillis(); } } 这里使用的是spring-2.0中的aop语法,对MessageService中所有以admin开头的方法进行权限控制,限制这些方法只能由ROLE_ADMIN调用。 <global-method-security> <protect-pointcut expr
-
第 22 章 拦截器无论是Filter,MethodIntercepter,ACL都要用到AOP,实际上都是拦截器的概念,其中要用到AbstractSecurityInterceptor总拦截器,AfterInvocationManager后置拦截,authenticationManager验证管理器,可能还要用上RunAsManager。关于RunAsManager,官方文档给出的解释是,在HttpInvoker或者Web Service的情况下,当前用户的一些身份要转换成其他身份,这时就是用RunAsManager,默认将以RUN_AS_开头的权限名,改变成 ROLE_RUN_AS_开头的权限名,然后重新赋予当前认证主体是用。现在的问题是不清楚具体使用在什么场景。
-
第 23 章 Spring Security中的ACLACL即访问控制列表(Access Controller List),它是用来做细粒度权限控制所用的一种权限模型。对ACL最简单的描述就是两个业务员,每个人只能查看操作自己签的合同,而不能看到对方的合同信息。下面我们会介绍Spring Security中是如何实现ACL的。23.1. 准备数据库和aclServiceACL所需的四张表,表结构见附录:附录 E, 数据库表结构。然后我们需要配置aclService,它负责与数据库进行交互。23.1.1. 为acl配置cache默认使用ehcache,spring security提供了一些默认的实现类。 <bean id="aclCache" class="org.springframework.security.acls.jdbc.EhCacheBasedAclCache"> <constructor-arg ref="aclEhCache"/> </bean> <bean id="aclEhCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheManager" ref="cacheManager"/> <property name="cacheName" value="aclCache"/> </bean> 在ehcache.xml中配置对应的aclCache缓存策略。 <cache name="aclCache" maxElementsInMemory="1000" eternal="false" timeToIdleSeconds="600" timeToLiveSeconds="3600" overflowToDisk="true" /> 23.1.2. 配置lookupStrategy简单来说,lookupStrategy的作用就是从数据库中读取信息,把这些信息提供给aclService使用,所以我们要为它配置一个dataSource,配置中还可以看到一个aclCache,这就是上面我们配置的缓存,它会把资源最大限度的利用起来。 <bean id="lookupStrategy" class="org.springframewor
-
第 24 章 最简控制台所谓的最简,实际上就是尽量利用现有资源,实现一个可管理的权限后台。我们将使用Spring Security提供的filter实现URL级的权限控制,使用Spring Security提供的UserDetailsManager实现用户管理,其中会包含用户密码加密和用户信息缓存。麻雀虽小,五脏俱全,如果想为自己的系统添加最简的权限后台,这一章将是不二之选。我们将在这个简易的控制台中实现如下功能:浏览用户,新增用户,修改用户,删除用户,修改密码,用户授权。24.1. 平台搭建选择maven2作为主要的构建工具,以便更加方便的管理第三方依赖,这章使用的依赖如下所示: com.family168.springsecuritybook:ch101:war:0.1 +- org.springframework.security:spring-security-taglibs:jar:2.0.4:compile | +- org.springframework.security:spring-security-core:jar:2.0.4:compile | | +- org.springframework:spring-core:jar:2.0.8:compile | | +- org.springframework:spring-context:jar:2.0.8:compile | | | \- aopalliance:aopalliance:jar:1.0:compile | | +- org.springframework:spring-aop:jar:2.0.8:compile | | +- org.springframework:spring-support:jar:2.0.8:runtime | | +- commons-logging:commons-logging:jar:1.1.1:compile | | +- commons-codec:commons-codec:jar:1.3:compile | | \- commons-collections:commons-collections:jar:3.2:compile | +- org.springframework.security:spring-security-acl:jar:
-
第 16 章 切换用户Spring Security提供了一种称为切换用户的机制,可以使管理员免于进过登录的操作,直接切换当前用户,从而改变当前的操作权限。因为按照责权分离的原则,系统内的超级管理员应该只有管理权限,而没有操作权限,所以为了在改变操作后可以测试系统的操作,需要降低权限才可以进入操作界面,这时就可以使用切换用户的功能。16.1. 配置方式在xml中添加SwitchUser的配置。 <beans:bean id="switchUserProcessingFilter" class="org.springframework.security.ui.switchuser.SwitchUserProcessingFilter"> <custom-filter position="SWITCH_USER_FILTER" /> <beans:property name="userDetailsService" ref="org.springframework.security.userdetails.memory.InMemoryDaoImpl" /> <beans:property name="targetUrl" value="/index.jsp"/> </beans:bean> 它需要引用系统中的userDetailsService在切换用户时,根据对应的username获得切换后用户的信息和权限,我们还要使用custom-filter将该过滤器放到过滤器链中,注意必须放在用来验证权限的FilterSecurityInterceptor之后,这样可以控制当前用户是否拥有切换用户的权限。现在,我们可以在系统中使用切换用户这一功能了,我们可以通过/j_spring_security_switch_user?j_username=user切换到j_username指定的用户,这样可以快捷的获得目标用户的信息和权限。当需要返回管理员用户时,只需要通过/j_spring_security_exit_user就可以还原到切换前的状态。16.2. 实例演示现在我们进入实例,通过登录页面进行登录。因为实现了权责分离,admin/admin用户只能访问管理页面admin.jsp,不能访问user.jsp,user/user用户只能访问操作页面user.jsp,不能访问adm
-
第 17 章 信道安全17.1. 设置信道安全为了加强安全级别,我们可以限制默写资源必须通过https协议才能访问。 <http auto-config='true'> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" requires-channel="https"/> <intercept-url pattern="/**" access="ROLE_USER" /> </http> 可以为/admin.jsp单独设置必须使用https才能访问,如果用户使用了http协议访问该网址,系统会强制用户使用https 协议重新进行请求。这里我们可以选使用https, http或者any三种数值,其中any为默认值,表示无论用户使用何种协议都可以访问资源。17.2. 指定http和https的端口因为http和https协议的访问端口不同,Spring Security在处理信道安全时默认会使用80/443和8080/8443对访问的网址进行转换。如果服务器对http和https协议监听的端口进行了修改,则需要修改配置文件让系统了解http和https的端口信息。我们可以使用port-mappings自定义端口映射。 <http auto-config='true'> <intercept-url pattern="/admin.jsp" access="ROLE_ADMIN" requires-channel="https"/> <intercept-url pattern="/**" access="ROLE_USER" /> <port-mappings> <port-mapping http="9000" https="9443"/> </port-mappings> </http> 上述配置文件中,我们定义了9000与9443的映射,现在系统会在强制使用http协议的网址时使用9000作为端口号,在强制使用https协议的网址时使用9443作为端口号,这些端口号会反映在重定向后生成网址中。实例在ch111。
-
第 20 章 权限管理的基本概念20.1. 认证与验证Spring Security作为权限管理框架,其内部机制可分为两大部分,其一是认证授权auhorization,其二是权限校验authentication。认证授权authorization是指,根据用户提供的身份凭证,生成权限实体,并为之授予相应的权限。权限校验authentication是指,用户请求访问被保护资源时,将被保护资源所需的权限和用户权限实体所拥护的权限二者进行比对,如果校验通过则用户可以访问被保护资源,否则拒绝访问。我们之前讲过的form-login,http-basic, digest都属于认证授权authorization部分的概念,用户可以通过这些机制登录到系统中,系统会为用户生成权限主体,并授予相应的权限。与之相对的,FilterSecurityInterceptor,Method保护,taglib,@Secured都属于权限校验authentication,无论是对URL的请求,对方法的调用,页面信息的显示,都要求用户拥有相应的权限才能访问,否则请求即被拒绝。20.2. SecurityContext安全上下文为使所有的组件都可以通过同一方式访问当前的权限实体,Spring Security特别提供了SecurityContext作为安全上下文,可以直接通过SecurityContextHolder获得当前线程中的SecurityContext。 SecurityContext securityContext = SecurityContextHolder.getContext(); 默认情况下,SecurityContext的实现基于ThreadLocal,系统会在每次用户请求时将SecurityContext与当前Thread进行绑定,这在web系统中是很常用的使用方式,服务器维护的线程池允许多个用户同时并发访问系统,而ThreadLocal可以保证隔离不同Thread之间的信息。当时对于单机应用来说,因为只有一个人使用,并不存在并发的情况,所以完全可以让所有Thread都共享同一个SecurityContext,因此Spring Security为我们提供了不同的策略模式,我们可以通过设置系统变量的方式选择希望使用的策略类。 java -Dspring.security.str
-
第 21 章 Voter表决者21.1. Voter表决者实际上并没有翻译的字面含义那么有血有肉,实际上就是一些条件,判断权限的时候,这些条件有三个状态。弃权,通过,禁止。最后通过你在xml里配置的策略来决定到底是不是让你访问这个需要验证的对象。Spring Security提供的策略有三个UnanimousBased.java 只要有一个Voter不能完全通过权限要求,就禁止访问。这个太可怕了,我今天晚上就载在它上面了。就因为我给所有的资源设置了两个角色,但当前的用户只拥有其中一个角色,就导致这个用户因为权限不够,所以无法继续访问资源了。简直无法理喻啊。AffirmativeBased.java只要有一个Voter不能通过权限要求,就禁止访问。这里应该是一个最小通过,就是说至少满足里其中一个条件就可以通过了。ConsensusBased.java只要通过的Voter比禁止的Voter数目多就可以访问了。嘿嘿。最后我当然选择AffirmativeBased.java,这样,我给一个资源配置几个角色,用户只要满足其中一个角色就可以访问啦。这样更正常一些啊。21.2. 默认角色名称都是以ROLE_开头稍微注意一下,默认角色名称都要以ROLE_开头,否则不会被计入权限控制,如果需要修改,就在xml里配个什么前缀的。可以用过配置roleVoter的rolePrefix来改变这个前缀。 <bean id="roleVoter" class="org.springframework.security.vote.RoleVoter"> <property name="rolePrefix" value="AUTH_"/> </bean>