多个WebSecurityConfigurerAdapter:一个作为库,在其他用户可以添加自己的安全访问
Multiple WebSecurityConfigurerAdapter: one as a library, in the other users can add their own security access
我正在创建一个 Spring 安全配置,供任何想要创建由 Spring 安全保护的 Stormpath Spring 应用程序的开发人员用作库。
为此,我对 WebSecurityConfigurerAdapter
进行了子class编辑,并在 configure(HttpSecurity)
中定义了 Stormpath 访问控制,并通过 [=18] 定义了 Stormpath AuthenticationProvider
=].这一切都可以在这个摘要class及其具体子class:
中看到
@Order(99)
public abstract class AbstractStormpathWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Removed properties and beans for the sake of keeping focus on the important stuff
/**
* The pre-defined Stormpath access control settings are defined here.
*
* @param http the {@link HttpSecurity} to be modified
* @throws Exception if an error occurs
*/
protected void configure(HttpSecurity http, AuthenticationSuccessHandler successHandler, LogoutHandler logoutHandler)
throws Exception {
if (loginEnabled) {
http
.formLogin()
.loginPage(loginUri)
.defaultSuccessUrl(loginNextUri)
.successHandler(successHandler)
.usernameParameter("login")
.passwordParameter("password");
}
if (logoutEnabled) {
http
.logout()
.invalidateHttpSession(true)
.logoutUrl(logoutUri)
.logoutSuccessUrl(logoutNextUri)
.addLogoutHandler(logoutHandler);
}
if (!csrfProtectionEnabled) {
http.csrf().disable();
} else {
//Let's configure HttpSessionCsrfTokenRepository to play nicely with our Controllers' forms
http.csrf().csrfTokenRepository(stormpathCsrfTokenRepository());
}
}
/**
* Method to specify the {@link AuthenticationProvider} that Spring Security will use when processing authentications.
*
* @param auth the {@link AuthenticationManagerBuilder} to use
* @param authenticationProvider the {@link AuthenticationProvider} to whom Spring Security will delegate authentication attempts
* @throws Exception if an error occurs
*/
protected void configure(AuthenticationManagerBuilder auth, AuthenticationProvider authenticationProvider) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
@Configuration
public class StormpathWebSecurityConfiguration extends AbstractStormpathWebSecurityConfiguration {
//Removed beans for the sake of keeping focus on the important stuff
@Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
@Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
简而言之,我们基本上是在定义我们的登录和注销机制,并集成我们的 CSRF 代码以与 Spring 安全性的代码很好地配合。
到目前为止一切正常。
但这只是 "library",我们希望用户在此基础上构建自己的应用程序。
因此,我们创建了一个示例应用程序来演示用户将如何使用我们的库。
基本上用户会想要创建自己的 WebSecurityConfigurerAdapter
。像这样:
@EnableStormpathWebSecurity
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
/**
* {@inheritDoc}
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
}
如果确实需要,WebApplicationInitializer
如下所示:
public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringSecurityWebAppConfig.class);
context.register(StormpathMethodSecurityConfiguration.class);
sc.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Stormpath Filter
FilterRegistration.Dynamic filter = sc.addFilter("stormpathFilter", new DelegatingFilterProxy());
EnumSet<DispatcherType> types =
EnumSet.of(DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
filter.addMappingForUrlPatterns(types, false, "/*");
//Spring Security Filter
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
}
}
所有这些代码都能正确启动。如果我转到 localhost:8080
,我会看到欢迎屏幕。如果我转到 localhost:8080/login
,我会看到登录屏幕。但是,如果我转到 localhost:8080/restricted
,我应该被重定向到登录页面,因为我们有这一行:http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
。但是我看到的是 Access Denied
页面。
然后,如果我在应用程序的访问控制中添加登录名url,就像这样:
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login")
.and()
.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
它现在将我重定向到登录页面,但是一旦我提交凭据,我就会遇到 CSRF 问题,这意味着我们所有的配置实际上并不是这个过滤器链的一部分。
当我调试它时,似乎每个 WebApplicationInitializer
都有自己的实例和自己的过滤器链。我希望它们以某种方式连接起来,但似乎并没有真正发生......
有人试过这样的东西吗?
顺便说一句:作为解决方法,用户可以使用 public class SpringSecurityWebAppConfig extends StormpathWebSecurityConfiguration
而不是 SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter
。这种方式可行,但我希望用户拥有纯粹的 Spring 安全代码,并从我们的 StormpathWebSecurityConfiguration
扩展偏离该目标。
所有代码都可以看到here。 Spring 的 Stormpath Spring 安全库在 extensions/spring/stormpath-spring-security-webmvc
下。使用该库的示例应用程序位于 examples/spring-security-webmvc
.
下
运行非常简单...您只需要注册到 Stormpath as explained here。然后你可以签出 spring_security_extension_redirect_to_login_not_working
分支并像这样启动示例应用程序:
$ git clone git@github.com:mrioan/stormpath-sdk-java.git
$ git checkout spring_security_extension_redirect_to_login_not_working
$ mvn install -DskipTests=true
$ cd examples/spring-security-webmvc
$ mvn jetty:run
然后你可以去localhost:8080/restricted
看看你是否被重定向到登录页面。
非常感谢任何帮助!
这肯定是关于您的 Antmatchers 顺序的问题,或者没有指定您允许访问 URL 的用户的角色。
你在“/restricted”之上有什么?
是否有东西完全阻挡了低于 URL 的东西?您应该首先指定更具体的 URLS,然后是广义的 URLs.
尝试在 URL 上面正确配置(或者告诉我它是什么,这样我可以帮助你),也许 "fullyAuthenticated" 也可以在父 URL 上应用 "permitAll" ROLE ] 的“/限制”。
根据我的经验,有多个 WebSecurityConfigurer
会在启动时扰乱安全配置。
解决此问题的最佳方法是将您的库配置设置为 SecurityConfigurerAdapters
,可以在适当的地方应用。
public class StormpathHttpSecurityConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> {
//Removed beans for the sake of keeping focus on the important stuff
@Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
}
public class StormpathAuthenticationManagerConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
//Removed beans for the sake of keeping focus on the important stuff
@Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
然后您的用户 apply
在他们自己的配置中:
@EnableStormpathWebSecurity
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/restricted").fullyAuthenticated()
.and()
.apply(new StormPathHttpSecurityConfigurer(...))
;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new StormPathAuthenticationManagerConfigurer(...));
}
}
我正在创建一个 Spring 安全配置,供任何想要创建由 Spring 安全保护的 Stormpath Spring 应用程序的开发人员用作库。
为此,我对 WebSecurityConfigurerAdapter
进行了子class编辑,并在 configure(HttpSecurity)
中定义了 Stormpath 访问控制,并通过 [=18] 定义了 Stormpath AuthenticationProvider
=].这一切都可以在这个摘要class及其具体子class:
@Order(99)
public abstract class AbstractStormpathWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
//Removed properties and beans for the sake of keeping focus on the important stuff
/**
* The pre-defined Stormpath access control settings are defined here.
*
* @param http the {@link HttpSecurity} to be modified
* @throws Exception if an error occurs
*/
protected void configure(HttpSecurity http, AuthenticationSuccessHandler successHandler, LogoutHandler logoutHandler)
throws Exception {
if (loginEnabled) {
http
.formLogin()
.loginPage(loginUri)
.defaultSuccessUrl(loginNextUri)
.successHandler(successHandler)
.usernameParameter("login")
.passwordParameter("password");
}
if (logoutEnabled) {
http
.logout()
.invalidateHttpSession(true)
.logoutUrl(logoutUri)
.logoutSuccessUrl(logoutNextUri)
.addLogoutHandler(logoutHandler);
}
if (!csrfProtectionEnabled) {
http.csrf().disable();
} else {
//Let's configure HttpSessionCsrfTokenRepository to play nicely with our Controllers' forms
http.csrf().csrfTokenRepository(stormpathCsrfTokenRepository());
}
}
/**
* Method to specify the {@link AuthenticationProvider} that Spring Security will use when processing authentications.
*
* @param auth the {@link AuthenticationManagerBuilder} to use
* @param authenticationProvider the {@link AuthenticationProvider} to whom Spring Security will delegate authentication attempts
* @throws Exception if an error occurs
*/
protected void configure(AuthenticationManagerBuilder auth, AuthenticationProvider authenticationProvider) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
@Configuration
public class StormpathWebSecurityConfiguration extends AbstractStormpathWebSecurityConfiguration {
//Removed beans for the sake of keeping focus on the important stuff
@Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
@Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
简而言之,我们基本上是在定义我们的登录和注销机制,并集成我们的 CSRF 代码以与 Spring 安全性的代码很好地配合。
到目前为止一切正常。
但这只是 "library",我们希望用户在此基础上构建自己的应用程序。
因此,我们创建了一个示例应用程序来演示用户将如何使用我们的库。
基本上用户会想要创建自己的 WebSecurityConfigurerAdapter
。像这样:
@EnableStormpathWebSecurity
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
/**
* {@inheritDoc}
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
}
如果确实需要,WebApplicationInitializer
如下所示:
public class WebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(SpringSecurityWebAppConfig.class);
context.register(StormpathMethodSecurityConfiguration.class);
sc.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = sc.addServlet("dispatcher", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Stormpath Filter
FilterRegistration.Dynamic filter = sc.addFilter("stormpathFilter", new DelegatingFilterProxy());
EnumSet<DispatcherType> types =
EnumSet.of(DispatcherType.ERROR, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.REQUEST);
filter.addMappingForUrlPatterns(types, false, "/*");
//Spring Security Filter
FilterRegistration.Dynamic securityFilter = sc.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
}
}
所有这些代码都能正确启动。如果我转到 localhost:8080
,我会看到欢迎屏幕。如果我转到 localhost:8080/login
,我会看到登录屏幕。但是,如果我转到 localhost:8080/restricted
,我应该被重定向到登录页面,因为我们有这一行:http.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
。但是我看到的是 Access Denied
页面。
然后,如果我在应用程序的访问控制中添加登录名url,就像这样:
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login")
.and()
.authorizeRequests().antMatchers("/restricted").fullyAuthenticated();
}
它现在将我重定向到登录页面,但是一旦我提交凭据,我就会遇到 CSRF 问题,这意味着我们所有的配置实际上并不是这个过滤器链的一部分。
当我调试它时,似乎每个 WebApplicationInitializer
都有自己的实例和自己的过滤器链。我希望它们以某种方式连接起来,但似乎并没有真正发生......
有人试过这样的东西吗?
顺便说一句:作为解决方法,用户可以使用 public class SpringSecurityWebAppConfig extends StormpathWebSecurityConfiguration
而不是 SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter
。这种方式可行,但我希望用户拥有纯粹的 Spring 安全代码,并从我们的 StormpathWebSecurityConfiguration
扩展偏离该目标。
所有代码都可以看到here。 Spring 的 Stormpath Spring 安全库在 extensions/spring/stormpath-spring-security-webmvc
下。使用该库的示例应用程序位于 examples/spring-security-webmvc
.
运行非常简单...您只需要注册到 Stormpath as explained here。然后你可以签出 spring_security_extension_redirect_to_login_not_working
分支并像这样启动示例应用程序:
$ git clone git@github.com:mrioan/stormpath-sdk-java.git
$ git checkout spring_security_extension_redirect_to_login_not_working
$ mvn install -DskipTests=true
$ cd examples/spring-security-webmvc
$ mvn jetty:run
然后你可以去localhost:8080/restricted
看看你是否被重定向到登录页面。
非常感谢任何帮助!
这肯定是关于您的 Antmatchers 顺序的问题,或者没有指定您允许访问 URL 的用户的角色。
你在“/restricted”之上有什么?
是否有东西完全阻挡了低于 URL 的东西?您应该首先指定更具体的 URLS,然后是广义的 URLs.
尝试在 URL 上面正确配置(或者告诉我它是什么,这样我可以帮助你),也许 "fullyAuthenticated" 也可以在父 URL 上应用 "permitAll" ROLE ] 的“/限制”。
根据我的经验,有多个 WebSecurityConfigurer
会在启动时扰乱安全配置。
解决此问题的最佳方法是将您的库配置设置为 SecurityConfigurerAdapters
,可以在适当的地方应用。
public class StormpathHttpSecurityConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<DefaultSecurityFilterChain, HttpSecurity> {
//Removed beans for the sake of keeping focus on the important stuff
@Override
protected final void configure(HttpSecurity http) throws Exception {
configure(http, stormpathAuthenticationSuccessHandler(), stormpathLogoutHandler());
}
}
public class StormpathAuthenticationManagerConfigurer
extends AbstractStormpathWebSecurityConfiguration
implements SecurityConfigurer<AuthenticationManager, AuthenticationManagerBuilder> {
//Removed beans for the sake of keeping focus on the important stuff
@Override
protected final void configure(AuthenticationManagerBuilder auth) throws Exception {
configure(auth, super.stormpathAuthenticationProvider);
}
}
然后您的用户 apply
在他们自己的配置中:
@EnableStormpathWebSecurity
@Configuration
@ComponentScan
@PropertySource("classpath:application.properties")
@Order(1)
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/restricted").fullyAuthenticated()
.and()
.apply(new StormPathHttpSecurityConfigurer(...))
;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.apply(new StormPathAuthenticationManagerConfigurer(...));
}
}