如何拥有单独的身份验证源? (一个用于 Oauth2,一个用于基于表单的登录)
How to have separate authentication sources? (one for Oauth2 and one for form-based login)
我正在编写一个具有链接到数据库的身份验证的小型应用程序,此身份验证将由 Oauth2 方面管理(类 由@EnableAuthorizationServer 和@EnableResourceServer 注释)。在同一应用程序中还有另一个用于管理页面的身份验证,该页面将链接到另一个不同的数据库,并将使用正常的基于表单的身份验证。
我为此特定目的编写了以下 Web 安全配置 class:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig{
@Configuration
@Order(5)
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/admin_logout"))
.invalidateHttpSession(true).logoutSuccessUrl("/admin/login.html");
http.authorizeRequests()
.antMatchers("/admin/login.html").permitAll().antMatchers("/admin/protected.html")
.hasRole("ADMIN")
.and().formLogin().loginPage("/admin/login.html")
.loginProcessingUrl("/admin_login").defaultSuccessUrl("/admin/protected.html");
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//Data source for form based auth
auth.inMemoryAuthentication().withUser("adminuser").password("adminpassword").roles("ADMIN");
}
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//Data source for Oauth
auth.inMemoryAuthentication().withUser("myuser").password("mypassword").roles("USER").and().withUser("test")
.password("testpassword").roles("USER");
}
}
其他相关组件是:
授权服务器配置:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter{
@Autowired
AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenEnhancer(tokenEnhancer())
.tokenStore(tokenStore());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.secret("secret")
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "write")
.resourceIds("resource").accessTokenValiditySeconds(60);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
{
oauthServer.checkTokenAccess("isAuthenticated()");
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
资源服务器配置:
@Configuration
@EnableResourceServer
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER-1)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter{
@Autowired
TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("resource").tokenStore(tokenStore);
}
@Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
}
您也可以在此处查看代码:https://github.com/cenobyte321/spring-oauth2-tokenenhancer-test/tree/webspeciallogin(分支:webspeciallogin)
问题是 AdminSecurityConfig class 中的所有内容都被忽略了,我可以在不登录的情况下进入 protected.html 页面,并且未创建指定的登录和注销处理 url。
另一方面,基于 Oauth2 的登录没有问题。我还没有弄清楚如何在 Oauth2 中指定一个 AuthenticationManagerBuilder,大多数在线资源建议使用 Oauth 适当读取的 configureGlobal 注入方法,这就是为什么它像上面的代码那样设置。
如何在一个启用 Oauth2 的应用程序中配置两个相互独立的身份验证源?
此致。
您能否通过使用基于角色的授权以不同的方式解决问题?例如:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
....
.antMatchers("/user/manage/**").access("hasRole('SYS_ADMIN_ROLE')")
.antMatchers("/audit/**").access("hasRole('SYS_ADMIN_ROLE')")
.antMatchers("/upload**").access("hasRole('SYS_ADMIN_ROLE')")
另一种方法是自定义用户详细信息服务,该服务知道如何在适当的数据库中查找用户 ID:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
LimitLoginAuthenticationProvider provider = (LimitLoginAuthenticationProvider)authenticationProvider;
provider.setPasswordEncoder(passwordEncoder);
auth.userDetailsService(customUserDetailsService()).passwordEncoder(passwordEncoder);
auth.authenticationProvider(authenticationProvider);
}
你需要两件事:
- 确保
AdminSecurityConfig
的优先级高于 ResourceServerConfiguration
。虽然 @EnableResourceServer
注释的文档说它将使用 hard-coded Order 3 注册 WebSecurityConfigurerAdapter
,但它实际上在 ResourceServerOrderProcessor
中被 -10 的顺序覆盖。因此,请确保您的 AdminSecurityConfig
订单低于 -10。
确保将 AdminSecurityConfig
中 HttpSecurity
的配置限制为与管理服务器关联的 URL 的请求匹配器,如下所示:
http.requestMatchers().antMatchers("/admin/**", "/admin_login", "/admin_logout")
.and()
.authorizeRequests()
.antMatchers("/admin/protected.html").hasRole("ADMIN")
.antMatchers("/admin/login.html").permitAll()
.and()
.formLogin().loginPage("/admin/login.html")
.loginProcessingUrl("/admin_login")
.defaultSuccessUrl("/admin/protected.html")
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/admin_logout"))
.invalidateHttpSession(true).logoutSuccessUrl("/admin/login.html")
;
注意嵌入代码的第一行带有http.requestMatchers().antMatchers("/admin/**", "/admin_login", "/admin_logout")
。
请参阅 Dave Syer(Spring 安全作者之一) 的类似问题以供参考。
我在 github.
上为您的示例项目做了一个 pull request 修复
我正在编写一个具有链接到数据库的身份验证的小型应用程序,此身份验证将由 Oauth2 方面管理(类 由@EnableAuthorizationServer 和@EnableResourceServer 注释)。在同一应用程序中还有另一个用于管理页面的身份验证,该页面将链接到另一个不同的数据库,并将使用正常的基于表单的身份验证。
我为此特定目的编写了以下 Web 安全配置 class:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig{
@Configuration
@Order(5)
public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/admin_logout"))
.invalidateHttpSession(true).logoutSuccessUrl("/admin/login.html");
http.authorizeRequests()
.antMatchers("/admin/login.html").permitAll().antMatchers("/admin/protected.html")
.hasRole("ADMIN")
.and().formLogin().loginPage("/admin/login.html")
.loginProcessingUrl("/admin_login").defaultSuccessUrl("/admin/protected.html");
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//Data source for form based auth
auth.inMemoryAuthentication().withUser("adminuser").password("adminpassword").roles("ADMIN");
}
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//Data source for Oauth
auth.inMemoryAuthentication().withUser("myuser").password("mypassword").roles("USER").and().withUser("test")
.password("testpassword").roles("USER");
}
}
其他相关组件是:
授权服务器配置:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter{
@Autowired
AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenEnhancer(tokenEnhancer())
.tokenStore(tokenStore());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.secret("secret")
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "write")
.resourceIds("resource").accessTokenValiditySeconds(60);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception
{
oauthServer.checkTokenAccess("isAuthenticated()");
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
资源服务器配置:
@Configuration
@EnableResourceServer
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER-1)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter{
@Autowired
TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("resource").tokenStore(tokenStore);
}
@Override
public void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/**").authenticated();
}
}
您也可以在此处查看代码:https://github.com/cenobyte321/spring-oauth2-tokenenhancer-test/tree/webspeciallogin(分支:webspeciallogin)
问题是 AdminSecurityConfig class 中的所有内容都被忽略了,我可以在不登录的情况下进入 protected.html 页面,并且未创建指定的登录和注销处理 url。
另一方面,基于 Oauth2 的登录没有问题。我还没有弄清楚如何在 Oauth2 中指定一个 AuthenticationManagerBuilder,大多数在线资源建议使用 Oauth 适当读取的 configureGlobal 注入方法,这就是为什么它像上面的代码那样设置。
如何在一个启用 Oauth2 的应用程序中配置两个相互独立的身份验证源?
此致。
您能否通过使用基于角色的授权以不同的方式解决问题?例如:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
....
.antMatchers("/user/manage/**").access("hasRole('SYS_ADMIN_ROLE')")
.antMatchers("/audit/**").access("hasRole('SYS_ADMIN_ROLE')")
.antMatchers("/upload**").access("hasRole('SYS_ADMIN_ROLE')")
另一种方法是自定义用户详细信息服务,该服务知道如何在适当的数据库中查找用户 ID:
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
LimitLoginAuthenticationProvider provider = (LimitLoginAuthenticationProvider)authenticationProvider;
provider.setPasswordEncoder(passwordEncoder);
auth.userDetailsService(customUserDetailsService()).passwordEncoder(passwordEncoder);
auth.authenticationProvider(authenticationProvider);
}
你需要两件事:
- 确保
AdminSecurityConfig
的优先级高于ResourceServerConfiguration
。虽然@EnableResourceServer
注释的文档说它将使用 hard-coded Order 3 注册WebSecurityConfigurerAdapter
,但它实际上在ResourceServerOrderProcessor
中被 -10 的顺序覆盖。因此,请确保您的AdminSecurityConfig
订单低于 -10。 确保将
AdminSecurityConfig
中HttpSecurity
的配置限制为与管理服务器关联的 URL 的请求匹配器,如下所示:http.requestMatchers().antMatchers("/admin/**", "/admin_login", "/admin_logout") .and() .authorizeRequests() .antMatchers("/admin/protected.html").hasRole("ADMIN") .antMatchers("/admin/login.html").permitAll() .and() .formLogin().loginPage("/admin/login.html") .loginProcessingUrl("/admin_login") .defaultSuccessUrl("/admin/protected.html") .and() .logout().logoutRequestMatcher(new AntPathRequestMatcher("/admin_logout")) .invalidateHttpSession(true).logoutSuccessUrl("/admin/login.html") ;
注意嵌入代码的第一行带有
http.requestMatchers().antMatchers("/admin/**", "/admin_login", "/admin_logout")
。
请参阅 Dave Syer(Spring 安全作者之一)
我在 github.
上为您的示例项目做了一个 pull request 修复