5. Java Configuration Java配置
在Spring 3.1中,对 Java Configuration (Java配置)的一般支持被添加到Spring框架中。自Spring Security 3.2以来,就有了Spring Security对Java配置的支持,用户可以轻松配置Spring Security,而无需使用任何XML。
如果您熟悉第6章“安全命名空间配置”,那么您应该会发现它和安全Java配置支持之间有很多相似之处。
Spring Security提供了 lots of sample applications (许多示例应用程序),演示了Spring Security Java配置的使用。
5.1 Web Security Java Configuration(网络安全Java配置)
第一步是创建我们的Spring安全Java配置。该配置创建了一个名为springSecurityFilterChain的Servlet过滤器,它负责应用程序中的所有安全性(保护应用程序URL、验证提交的用户名和密码、重定向到登录表单等)。您可以在下面找到Spring安全Java配置的最基本的例子:
1、这里真没有配置多少内容,但它确实很有用。您可以找到以下功能的摘要:
2、在你的应用程序中对每个URL进行验证
3、为你生成一个登陆表单
4、允许使用用户名Usernameuser和密码Passwordpassword使用验证表单进行验证。
5、允许用户登出
6、CSRF attackCSPF攻击防范
7、Session FixationSession保护
8、安全 Header 集成
HTTP Strict Transport Security对安全要求严格的HTTP传输安全
X-Content-Type-OptionsX-Content-Type-Options集成
缓存控制(稍后可以允许你缓存静态资源)
X-XSS-ProtectionX-XSS-Protection集成
X-Frame-Options 集成防止点击劫持Clickjacking
9、和以下 Servlet API 方法集成
HttpServletRequest#getRemoteUser()
HttpServletRequest.html#getUserPrincipal()
HttpServletRequest.html#isUserInRole(java.lang.String)
HttpServletRequest.html#login(java.lang.String, java.lang.String)
HttpServletRequest.html#logout()
5.1.1 AbstractSecurityWebApplicationInitializer(抽象安全网络应用程序初始化器)
下一步是将springSecurityFilterChain注册到war中。这可以在Servlet 3.0+环境中通过支持Spring的WebApplicationInitializer在Java配置中完成。不足为奇的是,Spring Security提供了一个基类AbstractSecurityWebApplicationInitializer(抽象安全性应用程序初始化器),它将确保为您注册springSecurityFilterChain。我们使用AbstractSecurityWebApplicationInitializer(抽象安全性应用程序初始化器)的方式会有所不同,这取决于我们是否已经在使用Spring,或者Spring安全性是我们的应用程序中唯一的Spring组件。
第5.1.2节,“AbstractSecurityWebApplicationInitializer without Existing Spring”-如果您还没有使用Spring,请使用这些说明。
第5.1.3节,“AbstractSecurityWebApplicationInitializer with Spring MVC”-如果您已经在使用Spring,请使用这些说明。
5.1.2 AbstractSecurityWebApplicationInitializer without Existing Spring(没有现有Spring的抽象安全性应用程序初始化器)
如果您没有使用Spring或Spring MVC,您将需要将网络安全参数(WebSecurityConfig)传入超类,以确保配置被采用。你可以在下面找到一个例子:
安全性应用程序初始化器(SecurityWebApplicationInitializer)将执行以下操作:
为应用程序中的每个网址自动注册到springSecurityFilterChain过滤器。
添加一个加载网络安全配置(WebSecurityConfig)的上下文加载器(ContextLoaderListener)。
5.1.3 AbstractSecurityWebApplicationInitializer with Spring MVC(有springMVC的抽象安全性应用程序初始化器 )
如果我们在应用程序的其他地方使用Spring,我们可能已经有了一个加载Spring配置的WebApplicationInitializer(web应用初始化器)。如果我们使用以前的配置,我们会得到一个错误。相反,我们应该在现有的应用上下文(ApplicationContext)中注册Spring Security。例如,如果我们使用的是Spring MVC,我们的SecurityWebApplicationInitializer(安全web应用初始化器)看起来就像下面这样:
这只会为应用程序中的每个网址注册springSecurityFilterChain过滤器。之后,我们将确保WebSecurityConfig(web安全配置)加载到我们现有的应用程序初始化器中。例如,如果我们使用Spring MVC,它将被添加到getRootConfigClasses():
5.2 HttpSecurity(http安全)
到目前为止,我们的网络安全配置只包含如何验证用户身份的信息。Spring Security如何知道我们想要要求所有用户都经过身份验证?Spring Security如何知道我们想要支持基于表单的身份验证?原因是WebSecurityConfigurerAdapter(web安全配置适配器)在configure(HttpseSecurity http)方法中提供了一个默认配置,如下所示:
上面的默认配置:
确保我们应用中的所有请求都需要用户被认证
允许用户进行基于表单的认证
允许用户使用HTTP基于验证进行认证
你可以看到这个配置和下面的XML命名配置相似:
java配置使用and()方法相当于XML标签的关闭。这允许我们继续配置父对象。如果你阅读代码,它也是有意义的。我想配置授权请求,配置表单登录和配置HTTP基本身份验证。
然而,Java配置有不同的默认URL和参数。创建自定义登录页面时请记住这一点。结果是,我们的网址更加RESTful。此外,我们使用有助于防止信息泄露的Spring Security并不十分明显。例如5.3所示:
5.3 Java Configuration and Form Login(java配置和表单登录)
当提示您登录时,您可能想知道登录表单是从哪里来的,因为我们没有提到任何HTML文件或JSP。由于Spring Security的默认配置没有为登录页面明确设置URL,Spring Security会根据已启用的功能自动生成一个,并使用处理提交的登录的URL的标准值、用户登录后将被发送到的默认目标URL等等。
虽然自动生成的登录页面便于快速启动和运行,但大多数应用程序都希望提供自己的登录页面。为此,我们可以更新我们的配置,如下所示:
1、指定登录页的路径
2、我们必须允许所有用户访问我们的登录页(例如为验证的用户),这个formLogin().permitAll()方法允许基于表单登录的所有的URL的所有用户的访问。
下面是我们当前配置的一个用JSPs实现的登录页面示例:
下面的登录页面代表我们当前的配置。如果某些默认值不能满足我们的需求,我们可以轻松地更新我们的配置。
1、一个POST请求到/login用来验证用户
2、如果参数有error, 验证尝试失败
3、如果请求蚕食logout存在则登出
4、登录名参数必须被命名为username
5、密码参数必须被命名为password
6、我们必须阅读第18.4.3节“包含CSRF令牌”(Section 18.4.3, “Include the CSRF Token”)以了解更多信息,请阅读第18章“跨站点请求伪造(CSRF)”(Chapter 18,Cross Site Request Forgery (CSRF))一节的参考资料
5.4 Authorize Requests(请求授权)
我们的例子中要求用户进行身份验证并且在我们应用程序的每个URL这样做。我么你可以通过给http.authorizeRequests()添加多个子节点来指定多个定制需求到我们的URL。例如:
1、http.authorizeRequests()方法有多个子节点,每个macher按照他们的声明顺序执行。
2、我们指定任何用户都可以通过访问的多个URL模式。任何用户都可以访问URL以"/resources/", equals "/signup", 或者 "/about"开头的URL。
3、以 "/admin/" 开头的URL只能由拥有 "ROLE_ADMIN"角色的用户访问。请注意我们使用 hasRole 方法,没有使用 "ROLE_" 前缀。
4、任何以"/db/" 开头的URL需要用户同时具有 "ROLE_ADMIN" 和 "ROLE_DBA"。和上面一样我们的 hasRole 方法也没有使用 "ROLE_" 前缀。
5、尚未匹配的任何URL要求用户进行身份验证。
5.5 Handling Logouts(登出处理)
当使用WebSecurityConfigurerAdapter, 注销功能会自动应用。默认是访问URL`/logout`将注销登陆的用户:
使HTTP Session 无效
清楚所有已经配置的 RememberMe 认证
清除SecurityContextHolder
重定向到 /login?logout
1、提供注销支持,使用WebSecurityConfigurerAdapter会自动被应用。
2、设置触发注销操作的URL (默认是/logout). 如果CSRF内启用(默认是启用的)的话这个请求的方式被限定为POST。 请查阅相关信息JavaDoc相关信息.
3、注销之后跳转的URL。默认是/login?logout。具体查看JavaDoc文档.
4、让你设置定制的 LogoutSuccessHandler。如果指定了这个选项那么logoutSuccessUrl()的设置会被忽略。请查阅JavaDoc文档.
5、指定是否在注销时让HttpSession无效。 默认设置为true。 在内部配置SecurityContextLogoutHandler,有关更多信息, 请查阅JavaDoc文档.
6、添加一个LogoutHandler,默认情况下SecurityContextLogoutHandler会被添加为最后一个LogoutHandler。
7、允许指定在注销成功时将移除的cookie。这是一个显示的添加一个CookieClearingLogoutHandler的快捷方式。
注销也可以通过XML命名空间进行配置,请参阅Spring Security XML命名空间相关文档获取更多细节logout element。
5.5.1 LogoutHandler(登出处理器)
通常,LogoutHandle的r实现类能够参与到注销处理中。它们将被调用来执行必要的清理。因此,他们不应该抛出异常。提供了各种实现方式:
PersistentTokenBasedRememberMeServices(基于持久令牌的记忆服务)
TokenBasedRememberMeServices(基于令牌的记忆服务)
CookieClearingLogoutHandler(Cookie清除注销处理程序)
CsrfLogoutHandler(Csrf注销处理程序)
SecurityContextLogoutHandler(安全上下文注销处理程序)
详情请参见第17.4节“记住我的界面和实现”。(Section 17.4, “Remember-Me Interfaces and Implementations”)
除了直接提供LogoutHandler实现,fluent应用编程接口还提供了快捷方式,这些快捷方式在封面下提供了各自的LogoutHandler实现。例如,deleteCookies()允许指定注销成功时要删除的一个或多个cookies的名称。与添加CookieClearingLogoutHandler相比,这是一个快捷方式。
5.5.2 LogoutSuccessHandler(登出成功处理器)
LogoutSuccessHandler(登出成功处理器)被LogoutFilter(登出处理器)在成功注销后调用,用来进行重定向或者转发相应的目的地。注意这个接口与LogoutHandler(登出处理器)几乎一样,但是可以抛出异常。
下面是提供的一些实现:
SimpleUrlLogoutSuccessHandler(简单URL登出成功处理器)
HttpStatusReturningLogoutSuccessHandler(http状态返回登出成功处理器)
和前面提到的一样,你不需要直接指定SimpleUrlLogoutSuccessHandler(简单URL登出成功处理器)。而使用流式API接口通过设置logoutSuccessUrl(登出成功URL)快捷方式进行设置SimpleUrlLogoutSuccessHandler简单URL登出成功处理器)。注销成功 后将重定向到设置的URL地址。默认的地址是 /login?logout。
在REST API场景中HttpStatusReturningLogoutSuccessHandlerhttp状态返回登出成功处理器)会进行一些有趣的改变。LogoutSuccessHandler(登出成功处理器)允许你设置一个返回给客户端的HTTP状态码(默认返回200)来替换重定向到URL这个动作。
5.5.3 Further Logout-Related References(进一步登出相关参考)
Logout Handling(登出处理)
Testing Logout(登出测试)
HttpServletRequest.logout()(HTTPServletRequest登出)
Section 17.4, “Remember-Me Interfaces and Implementations”(Remember-Me接口和实现)
Logging Outin section CSRF Caveats (登出CSRF说明)
SectionSingle Logout(CAS protocol)(单点登出)
Documentation for thelogout elementin the Spring Security XML Namespace section(Spring安全XML命名空间部分中注销元素的文档)
5.6 Authentication(认证)
到目前为止,我们只看了最基本的身份验证配置。让我们看几个稍微高级一点的配置身份验证的选项。
5.6.1 In-Memory Authentication(内存中的身份验证)
我们已经看到了一个单用户配置到内存验证的示例,下面是配置多个用户的例子:
5.6.2 JDBC Authentication(jdbc认证)
你可以找一些更新来支持JDBC的验证。下面的例子假设你已经在应用程序中定义好了DataSource, jdbc-javaconfig 示例提供了一个完整的基于JDBC的验证。
5.6.3 LDAP Authentication(LDAP认证)
你可以找一些更新来支持LDAP的身份验证, ldap-javaconfig 提供了一个完成的使用基于LDAP的身份验证的示例。
上面的例子使用了下面的LDIF和一个嵌入的Apache DS LDAP实例。
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=admin,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Rod Johnson
sn: Johnson
uid: admin
userPassword: password
dn: uid=user,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dianne Emu
sn: Emu
uid: user
userPassword: password
dn: cn=user,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: user
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
uniqueMember: uid=user,ou=people,dc=springframework,dc=org
dn: cn=admin,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: admin
uniqueMember: uid=admin,ou=people,dc=springframework,dc=org
5.6.4 AuthenticationProvider(认证提供商)
您可以通过一个自定义的AuthenticationProvider为bean定义自定义身份验证。 例如, 下面这个例子假设自定义身份验证SpringAuthenticationProvider实现了AuthenticationProvider:
仅当尚未填充AuthenticationManagerBuilder时,才使用此选项。
5.6.5 UserDetailsService(用户详细信息服务)
你可以通过一个自定义的UserDetailsService(用户详细信息服务)为bean定义自定义身份验证。 例如,下面这个例子假设自定义身份验证SpringDataUserDetailsService实现了UserDetailsService:
仅当尚未填充AuthenticationManagerBuilder(身份验证管理器生成器)并且未定义AuthenticationProviderBean(身份验证提供商)时,才使用此选项。
你也可以通过让passwordencoder为bean自定义密码如何编码。 例如,如果你使用BCrypt,你可以添加一个bean定义如下图所示:
5.6.6 LDAP Authentication(LDAP身份认证)
5.7 Multiple HttpSecurity(多个HttpSecurity)
我们可以配置多个http安全实例,就像我们可以有多个< http >块一样。关键是多次扩展WebSecurityConfigurerAdapter。例如,下面是一个为以/api/开头的网址进行不同配置的示例。
配置正常的验证。
1、创建一个WebSecurityConfigurerAdapter,包含一个@Order注解,用来指定个哪一个WebSecurityConfigurerAdapter更优先。
2、http.antMatcher指出,这个HttpSecurity只应用到以/api/开头的URL上。
3、创建另外一个WebSecurityConfigurerAdapter实例。用于不以/api/开头的URL,这个配置的顺序在ApiWebSecurityConfigurationAdapter之后,因为他没有指定@Order值为1(没有指定@Order默认会被放到最后).
5.8 Method Security(方法安全性)
从版本2.0开始,Spring Security已经大大改进了对为服务层方法增加安全性的支持。它支持JSR-250注释安全性以及框架的原始@Secured注释。从3.0开始,您还可以使用新的基于表达式(expression-based annotations.)的注释。您可以对单个bean应用安全性,使用intercept-methods元素来修饰bean声明,或者使用AspectJ风格的切入点来保护整个服务层中的多个bean。
5.8.1 EnableGlobalMethodSecurity(启用全局方法安全性)
我们可以在任何@Configuration实例上使用@EnableGlobalMethodSecurity注释来启用基于注释的安全性。例如,下面将启用Spring Security的@Secured注释。
向方法(在类或接口上)添加注释会相应地限制对该方法的访问。Spring Security的原生注释支持为该方法定义了一组属性。这些将被传递给AccessDecisionManager(访问决策管理器),以便它做出实际的决策:
使用如下代码启用JSR-250注解的支持
这些都是基于标准的,允许应用简单的基于角色的约束,但不具备Spring Security的原生注释。要使用新的基于表达式的语法,您应该使用
等效的Java代码是
5.8.2 GlobalMethodSecurityConfiguration(全局方法安全性配置)
有时您可能需要执行比@EnableGlobalMethodSecurity注释允许的操作更复杂的操作。对于这些实例,您可以扩展GlobalMethodsecurityConfiguration(全局方法安全性配置),确保您的子类中存在@EnableGlobalMethodSecurity注释。例如,如果您想提供一个自定义的MethodSecurityExpressionHandler(方法安全表达式处理程序),您可以使用以下配置:
关于可以被重写的方法的更多信息,请参考GlobalMethodSecurityConfiguration(全局方法安全性配置)的java文档。
5.9 Post Processing Configured Objects(后置处理配置的对象)
Spring Security的Java配置没有公开它配置的每个对象的每个属性。这简化了大多数用户的配置。毕竟,如果每个属性都公开了,用户可以使用标准的bean配置。
虽然有充分的理由不直接公开每个属性,但是用户可能仍然需要更高级的配置选项。为了解决这个问题,Spring Security引入了ObjectPostProcessor(后置对象处理器)的概念,它可以用来修改或替换许多由Java配置创建的对象实例。例如,如果您想在FilterSecurityInterceptor(安全过滤拦截器)上配置FilterSecurityPublishauthorizationSuccess(安全过滤发布授权成功)属性,可以使用以下方法:
5.10 Custom DSLs(自定义DSLs)
您可以在Spring Security中提供自己的定制DSL。例如,你可能有这样的东西:
这实际上是像HttpSecurity.authorizeRequests()这样的方法是来实现的。
自定义DSL可以这样使用:
代码按以下顺序调用:
1、调用“配置”的配置方法中的代码
2、调用了“MyCustomDsl的初始化方法”中的代码
3、调用了“MyCustomDsl的配置方法”中的代码
如果需要,您可以使用SpringFactories让WebSecurityConfiguerAdapter默认添加MyCustomDsl。例如,您可以在类路径上创建一个名为META-INF/spring.factories的资源,其内容如下:
org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyCustomDsl
希望禁用默认值的用户可以明确地这样做。