spring security 4.0 教程 步步深入 5
2016-11-03 11:28
369 查看
5. Java 配置
在Spring 3.1中向Spring框架添加了对Java配置的常规支持。 自Spring Security 3.2以来,一直有Spring Security Java配置支持,使用户能够轻松地配置Spring Security而不使用任何XML。 如果你熟悉第6章,安全命名空间配置,那么你应该发现它和安全Java配置支持之间有几个相似之处。Spring Security提供了许多以-j结尾的示例应用程序,演示了使用Spring Security Java配置。
5.1 初识 Java 配置
第一步是创建我们的Spring Security Java配置。 配置创建一个名为springSecurityFilterChain的Servlet过滤器,它负责应用程序中的所有安全性(保护应用程序URL,验证提交的用户名和密码,重定向到登录表单等)。 你可以在下面找到Spring Security Java配置的最基本的例子:@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("cmx").password("123").roles("USER"); } }
并没有太多的配置,但它做了很多。您可以在下面找到功能的摘要:
需要对应用的每个url进行身份验证
生成登录表单
允许具有用户名为cmx和密码123的用户通过表单的身份验证访问url
允许用户注销
CSRF 攻击 预防
会话 保护
安全头集成
用于 HTTP严格传输安全 的安全请求
X-Content-Type-Options 集成
缓存控制(以后可以通过应用程序覆盖,允许缓存您的静态资源)
X-XSS-Protection 集成
X-Frame-Options 集成以帮助防止 Clickjacking
与以下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
下一步是使用war注册springSecurityFilterChain。 这可以在Java配置中使用Spring的WebApplicationInitializer支持在Servlet 3.0+环境中完成。不奇怪,Spring Security提供了一个基类AbstractSecurityWebApplicationInitializer,它将确保springSecurityFilterChain为您注册。 我们使用AbstractSecurityWebApplicationInitializer的方式取决于我们是否已经使用Spring,或者如果Spring Security是我们应用程序中唯一的Spring组件。5.1.2 AbstractSecurityWebApplicationInitializer without Existing Spring
如果你不使用Spring或Spring MVC,你需要将WebSecurityConfig传入超类,以确保配置被选中。 你可以找到一个例子:import org.springframework.security.web.context.*; public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { public SecurityWebApplicationInitializer() { super(WebSecurityConfig.class); } }
SecurityWebApplicationInitializer将做以下一些事情:
为应用程序中的每个URL自动注册springSecurityFilterChain Filter
添加一个加载 WebSecurityConfig 的ContextLoaderListener.
5.1.3 AbstractSecurityWebApplicationInitializer with Spring MVC
如果我们在应用程序的其他地方使用Spring,我们可能已经有一个WebApplicationInitializer加载我们的Spring配置。 如果我们使用以前的配置,我们会出错。 相反,我们应该使用现有的
ApplicationContext注册Spring Security。 例如,如果我们使用Spring MVC,我们的
SecurityWebApplicationInitializer将类似于以下内容:
import org.springframework.security.web.context.*; public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer { }
这将只是为您的应用程序中的每个URL注册
springSecurityFilterChain过滤器。 之后,我们将确保
WebSecurityConfig加载在我们现有的
ApplicationInitializer中。 例如,如果我们使用Spring MVC,它将被添加到
getRootConfigClasses()
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[] { WebSecurityConfig.class }; } // ... other overrides ... }
5.2 HttpSecurity
到目前为止,我们的WebSecurityConfig只包含有关如何验证我们的用户的信息。 Spring Security知道我们想要求所有用户进行身份验证? Spring Security知道我们要支持基于表单的身份验证? 其原因是
WebSecurityConfigurerAdapter在
configure(HttpSecurity http)方法中提供了一个默认配置,如下所示:
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and() .httpBasic(); }
上面的默认配置:
确保对我们的应用程序的任何请求需要用户进行身份验证
允许用户使用基于表单的登录进行身份验证
允许用户使用HTTP Basic进行身份验证
您会注意到,此配置与XML命名空间配置非常相似:
<http> <intercept-url pattern="/**" access="authenticated"/> <form-login /> <http-basic /> </http>
关闭XML标签的Java配置等同于使用和()方法表示,这允许我们继续配置父代。 如果你读的代码,这也有意义。 我想配置授权的请求并配置表单登录和配置HTTP基本认证。 但是,Java配置具有不同的默认URL和参数。 创建自定义登录页面时请记住这一点。 结果是我们的URL更加RESTful。 此外,它不是很明显,我们使用Spring Security有助于防止信息泄漏。 例如:
5.3 Java配置和表单登录
您可能想知道当提示登录时登录表单来自哪里,因为我们没有提到任何HTML文件或JSP。 由于Spring Security的默认配置没有为登录页面显式设置URL,Spring Security会根据启用的功能自动生成一个URL,并使用处理提交的登录的URL的标准值,用户将使用的默认目标URL 发送到登录后等等。 虽然自动生成的登录页面方便快速启动和运行,但大多数应用程序想要提供自己的登录页面。 为此,我们可以更新我们的配置,如下所示:protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") //1 .permitAll(); //2 }
更改配置以指定login页面
我们必须授予所有用户(即未经身份验证的用户)对我们登录页面的访问权限。 formLogin().permitAll()方法允许向所有用户授予与基于表单的登录相关联的所有URL的访问权限
使用JSP实现的用于当前配置的示例登录页面如下所示:
下面的登录页面代表我们当前的配置。如果某些默认设置不能满足我们的需要,我们可以轻松修改我们的配置。
<c:url value="/login" var="loginUrl"/> <form action="${loginUrl}" method="post"> //1 <c:if test="${param.error != null}"> //2 <p> Invalid username and password. </p> </c:if> <c:if test="${param.logout != null}"> //3 <p> You have been logged out. </p> </c:if> <p> <label for="username">Username</label> <input type="text" id="username" name="username"/> //4 </p> <p> <label for="password">Password</label> <input type="password" id="password" name="password"/> //5 </p> <input type="hidden" //6 name="${_csrf.parameterName}" value="${_csrf.token}"/> <button type="submit" class="btn">Log in</button> </form>
尝试对 /login 发送post请求以验证用户
如果查询参数 param.error 存在,则验证失败
如果查询参数 param.logout 存在,则注销成功
用户名必须以 username 为http请求参数
密码必须以 password 为http请求参数
这部分我们必须在第18.4.3节“包括CSRF令牌”中了解更多,请参阅第18章,跨站点请求伪造(CSRF)部分的参考
5.4 验证请求
我们的示例只需要对用户进行身份验证,并且对我们的应用程序中的每个网址都这样做。我们可以通过向我们的http.authorizeRequests()方法添加多个子节点来指定我们的URL的自定义要求。例如:
protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() //1 .antMatchers("/resources/**", "/signup", "/about").permitAll() //2 .antMatchers("/admin/**").hasRole("ADMIN") //3 .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") //4 .anyRequest().authenticated() //5 .and() // ... .formLogin(); }
http.authorizeRequests()方法有多个子节点,每个匹配器都按它们声明的顺序考虑
我们指定了多个用户可以访问的网址格式。具体来说,如果网址以”/resources/”开头,等于”/signup”或等于”/about”,则任何用户都可以访问请求。
以”/admin/”开头的任何网址将被重新分配给具有角色”ROLE_ADMIN”的用户。你会注意到,由于我们调用了hasRole方法,我们不需要指定”ROLE_”前缀
以”/db/”开头的任何URL都要求用户同时拥有”ROLE_ADMIN”和”ROLE_DBA”。 你会注意到,由于我们使用hasRole表达式,我们不需要指定”ROLE_”前缀
任何尚未匹配的URL只需要对用户进行身份验证
5.5 注销登录
使用WebSecurityConfigurerAdapter时,会自动应用注销功能。 默认情况下,访问URL
/logout将通过以下方式记录用户:
使HTTP session无效
清除配置的任何RememberMe身份验证
清除
SecurityContextHolder
跳转至
/login?logout
与配置登录功能类似,您还可以使用各种选项进一步自定义您的注销要求:
protected void configure(HttpSecurity http) throws Exception { http .logout() //1 .logoutUrl("/my/logout") //2 .logoutSuccessUrl("/my/index") //3 .logoutSuccessHandler(logoutSuccessHandler) //4 .invalidateHttpSession(true) //5 .addLogoutHandler(logoutHandler) //6 .deleteCookies(cookieNamesToClear) //7 .and() ... }
提供注销支持。这在使用
WebSecurityConfigurerAdapter时自动应用。
触发注销的URL(默认为
/logout)。如果启用CSRF保护(默认),则请求也必须是POST。有关信息,请参阅JavaDoc
发生注销后重定向到的URL。默认值为
/login?logout。有关信息,请参考JavaDoc。
让我们指定一个自定义
LogoutSuccessHandler。如果指定此参数,将忽略logoutSuccessUrl()。有关信息,请参阅JavaDoc。
指定在注销时是否使
HttpSession无效。 默认情况下是这样。 在封底下配置
SecurityContextLogoutHandler。 有关信息,请参阅JavaDoc。
添加
LogoutHandler。默认情况下,
SecurityContextLogoutHandler作为最后一个
LogoutHandler添加。
允许指定在注销成功时要删除的Cookie的名称。这是一个显式添加
CookieClearingLogoutHandler的快捷方式。
当然,注销也可以使用XML命名空间符号进行配置。有关更多详细信息,请参阅
Spring Security XML命名空间部分中的注销元素的文档。
通常,为了定制注销功能,您可以添加
LogoutHandler和
/或
LogoutSuccessHandler实现。对于许多常见的情况,当使用流利的API时,这些处理程序应用在封面下
5.5.1 LogoutHandler
通常,LogoutHandler实现指示能够参与注销处理的类。它们将被调用来执行必要的清理。因此,他们不应该抛出异常。提供了各种实现:
PersistentTokenBasedRememberMeServices
TokenBasedRememberMeServices
CookieClearingLogoutHandler
CsrfLogoutHandler
SecurityContextLogoutHandler
有关详细信息,请参见第17.4节“Remember-Me接口和实现”。
并非直接提供
LogoutHandler实现,流式API还提供了快捷方式,提供了相应的
LogoutHandler实现。 例如。
deleteCookies()允许指定在注销成功时要删除的一个或多个cookie的名称。 与添加
CookieClearingLogoutHandler相比,这是一个快捷方式。
5.5.2 登录成功句柄
LogoutSuccessHandler在LogoutFilter成功注销后被调用,以处理例如。 重定向或转发到适当的目的地。 注意,该接口几乎与LogoutHandler相同,但可能引发异常。提供以下实现:
SimpleUrlLogoutSuccessHandler
HttpStatusReturningLogoutSuccessHandler
如上所述,您不需要直接指定
SimpleUrlLogoutSuccessHandler。相反,流利的API通过设置
logoutSuccessUrl()提供了一个快捷方式。 这将在下面设置
SimpleUrlLogoutSuccessHandler。提供的URL将在发生注销后重定向到。 默认值为
/login?logout。
HttpStatusReturningLogoutSuccessHandler在REST API类型场景中很有趣。 成功注销后,此
LogoutSuccessHandler不会重定向到URL,而是允许您提供要返回的纯HTTP状态代码。 如果未配置,默认情况下将返回状态代码200。
5.6 认证
到目前为止,我们只看了一下最基本的认证配置。让我们来看看一些稍微更高级的配置认证选项。5.6.1 内存认证
我们已经看到了为单个用户配置内存身份验证的示例。以下是配置多个用户的示例:@Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER").and() .withUser("admin").password("password").roles("USER", "ADMIN"); }
5.6.2 JDBC 认证
您可以找到对基于JDBC的身份验证的更新。下面的示例假设您已经在应用程序中定义了一个DataSource。 jdbc-javaconfig示例提供了使用基于JDBC的身份验证的完整示例。
@Autowired private DataSource dataSource; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .jdbcAuthentication() .dataSource(dataSource) .withDefaultSchema() .withUser("user").password("password").roles("USER").and() .withUser("admin").password("password").roles("USER", "ADMIN"); }
5.6.3 LDAP 认证
您可以找到更新以支持基于LDAP的身份验证。 ldap-javaconfig示例提供了使用基于LDAP的身份验证的完整示例。@Autowired private DataSource dataSource; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth .ldapAuthentication() .userDnPatterns("uid={0},ou=people") .groupSearchBase("ou=groups"); }
上面的示例使用以下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时使用
@Bean public SpringAuthenticationProvider springAuthenticationProvider() { return new SpringAuthenticationProvider(); }
5.6.5 UserDetailsService
您可以通过将自定义UserDetailsService作为bean公开来定义自定义认证。例如,以下将自定义认证,假设
SpringDataUserDetailsService实现
UserDetailsService:
这仅在未填充
AuthenticationManagerBuilder且未定义
AuthenticationProviderBean时使用。
@Bean public SpringDataUserDetailsService springDataUserDetailsService() { return new SpringDataUserDetailsService(); }
您还可以通过将
PasswordEncoder公开为bean来自定义密码的编码方式。例如,如果使用bcrypt,您可以添加一个bean定义,如下所示:
@Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
5.7 多HttpSecurity实例
我们可以配置多个HttpSecurity实例,就像我们可以有多个
<http>块。 关键是要多次扩展
WebSecurityConfigurationAdapter。 例如,以下是具有以
/api/开头的URL的不同配置的示例。
@EnableWebSecurity public class MultiHttpSecurityConfig { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) { //1 auth .inMemoryAuthentication() .withUser("user").password("password").roles("USER").and() .withUser("admin").password("password").roles("USER", "ADMIN"); } @Configuration @Order(1) //2 public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { protected void configure(HttpSecurity http) throws Exception { http .antMatcher("/api/**") //3 .authorizeRequests() .anyRequest().hasRole("ADMIN") .and() .httpBasic(); } } @Configuration //4 public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .formLogin(); } } }
配置认证正常
创建包含
@Order的
WebSecurityConfigurerAdapter的实例,以指定应首先考虑哪个
WebSecurityConfigurerAdapter。
http.antMatcher声明这个HttpSecurity将只适用于以
/api/
创建另一个
WebSecurityConfigurerAdapter实例。 如果URL不以
/api/开头,则将使用此配置。 这个配置在
ApiWebSecurityConfigurationAdapter之后被考虑,因为它在1之后有一个
@Order值(没有
@Order默认值为last)。
5.8 方法Security
从2.0版本开始,Spring Security已经大大提高了对您的服务层方法添加安全性的支持。 它提供对JSR-250注释安全性以及框架的原始@Secured注释的支持。 从3.0,你也可以使用新的基于表达式的注释。 您可以将安全性应用于单个bean,使用
intercept-methods元素装饰bean声明,也可以使用AspectJ样式切入点在整个服务层上保护多个bean。
5.8.1 EnableGlobalMethodSecurity
我们可以使用任何@Configuration实例上的
@EnableGlobalMethodSecurity注释启用基于注释的安全性。例如,以下将启用Spring Security的
@Secured注释。
@EnableGlobalMethodSecurity(securedEnabled = true) public class MethodSecurityConfig { // ... }
向方法(在类或接口上)添加注释将相应地限制对该方法的访问。 Spring Security的原生注释支持为方法定义了一组属性。 这些将被传递给
AccessDecisionManager,以便做出实际的决定:
public interface BankService { @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account readAccount(Long id); @Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account[] findAccounts(); @Secured("ROLE_TELLER") public Account post(Account account, double amount); }
可以启用对JSR-250注释的支持
@EnableGlobalMethodSecurity(jsr250Enabled = true) public class MethodSecurityConfig { // ... }
这些是基于标准的,允许应用简单的基于角色的约束,但没有Spring Security的本机注释。要使用新的基于表达式的语法,您将使用
@EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig { // ... }
和等效的Java代码
public interface BankService { @PreAuthorize("isAnonymous()") public Account readAccount(Long id); @PreAuthorize("isAnonymous()") public Account[] findAccounts(); @PreAuthorize("hasAuthority('ROLE_TELLER')") public Account post(Account account, double amount); }
5.8.2 GlobalMethodSecurityConfiguration
有时,您可能需要执行比使用@EnableGlobalMethodSecurity注释允许的操作更复杂的操作。 对于这些实例,您可以扩展
GlobalMethodSecurityConfiguration,确保
@EnableGlobalMethodSecurity注释存在于您的子类上。 例如,如果要提供自定义的
MethodSecurityExpressionHandler,可以使用以下配置:
@EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected MethodSecurityExpressionHandler createExpressionHandler() { // ... create and return custom MethodSecurityExpressionHandler ... return expressionHandler; } }
有关可以覆盖的方法的其他信息,请参阅
GlobalMethodSecurityConfigurationJavadoc。
5.9 Post Processing Configured Objects
Spring Security的Java配置不公开它配置的每个对象的每个属性。 这简化了大多数用户的配置。 总之,如果每个属性都暴露出来,用户可以使用标准bean配置。 虽然有很好的理由不直接暴露每个属性,用户可能仍需要更高级的配置选项。 为了解决这个问题Spring Security引入了一个ObjectPostProcessor的概念,它可以用来修改或替换由Java配置创建的许多Object实例。 例如,如果要在
FilterSecurityInterceptor上配置
filterSecurityPublishAuthorizationSuccess属性,您可以使用以下命令:
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { public <O extends FilterSecurityInterceptor> O postProcess( O fsi) { fsi.setPublishAuthorizationSuccess(true); return fsi; } }); }
相关文章推荐
- spring security 4.0 教程 步步深入 5
- spring security 4.0 教程 步步深入 6
- spring security 4.0 步步深入 1-4
- Fedora Core 4.0 安装教程
- 深入理解C# 3.0的五项主要改进_C#教程
- PetShop 4.0 分析、基于C#的接口基础教程
- C# 4.0语言将出现重大改变,带来一段Code Preview _C#教程
- SDL入门教程(四):3、SDL的软、硬件渲染的深入试验和分析
- VBScript教程 第十一课深入VBScript
- Spring Security-2.0入门教程(基础篇)
- Packet Tracer 5.0软件使用教程深入详解
- GTK+ 2.0 教程--深入探索信号处理函数
- WPF入门教程系列(二) 深入剖析WPF Binding的使用方法
- JAVA AJAX教程第五章―DOM和JAVASCRIPT深入(上)
- 最新的Spring Security-2.0入门教程
- Web Part 教程--深入理解 Web Part Manager 和 Zone
- 最新图解DEDE4.0采集教程
- Cisco Access Control Server 4.0简明中文图文教程
- 【Python开发web】(2) -- 跟着教程步步来 -- QuickWiki
- JAVA AJAX教程第五章—DOM和JAVASCRIPT深入