我目前正在使用Spring Security开发一个OAuth 2.0登录/用户管理系统。由于我正在使用spring-security-oauth2-authorization-server
模块编写自己的授权服务器(基于像here这样的文档),我还实现了一个用户管理/管理 Jmeter 板。
当然,用户管理的端点在auth服务器上。因此,auth服务器充当授权服务器和 (有点像) 资源服务器。要授权用户使用管理 Jmeter 板,他们当然需要登录,所以首先他们被重定向到授权服务器/authorize
端点,然后重定向到登录菜单。然后正常地遵循授权代码授予流程。但是每一步都是在同一台服务器上完成的 (即,身份验证和访问受保护的管理端点)!
由于以下问题,我请求将我们的认证服务器配置为认证服务器和资源服务器:
授权服务器将securityContext
保存到会话中。会话ID(JSESSIONID
)然后作为cookie留在用户浏览器中。问题是,当用户试图访问授权服务器上的安全端点(如{...}/admin/users
端点)时,仅cookie就足以授权他们向该端点发出请求。这意味着当应该首先请求承载令牌以访问受保护端点时,可以绕过整个授权流。我们希望安全端点仅可使用承载令牌访问,并且仅使用承载令牌,而不是会话 (或两者的组合)。
以下是当前安全配置的缩短版本:
@Bean
@Order(1)
public CorsFilter corsFilter(CorsConfigurationSource corsConfigurationSource) {
logger.info("Creating corsFilter bean");
return new CorsFilter(corsConfigurationSource);
}
/**
* Configures the authorization server endpoints.
*/
@Bean
@Order(2)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, RegisteredClientRepository clientRepository) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.registeredClientRepository(clientRepository) // autowired from ClientConfig.java
.oidc(Customizer.withDefaults());
http.exceptionHandling((exceptions) -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
);
http.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()));
http.csrf(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
@Order(3)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.securityMatcher(new NegatedRequestMatcher(new AntPathRequestMatcher("/admin/**")));
http.authorizeHttpRequests((authorize) ->
authorize
.requestMatchers(new AntPathRequestMatcher("/register")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/recover")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/error/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/css/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/js/**")).permitAll()
.requestMatchers(new AntPathRequestMatcher("/favicon.ico")).permitAll()
.anyRequest().authenticated());
http.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()));
// set custom login form
http.formLogin(form -> {
form.loginPage("/login");
form.permitAll();
});
http.logout(conf -> {
// default logout url
conf.logoutSuccessHandler(logoutSuccessHandler());
});
http.csrf(AbstractHttpConfigurer::disable);
http.cors(AbstractHttpConfigurer::disable);
return http.build();
}
@Bean
@Order(4)
public SecurityFilterChain adminResourceFilterChain(HttpSecurity http) throws Exception {
// handle out custom endpoints in this filter chain
http.authorizeHttpRequests((authorize) ->
authorize
.requestMatchers(new AntPathRequestMatcher("/admin/**")).hasRole("ADMIN")
.anyRequest().authenticated());
http.sessionManagement(conf -> conf.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()));
http.csrf(AbstractHttpConfigurer::disable);
http.cors(AbstractHttpConfigurer::disable);
return http.build();
}
如何配置授权服务器,使管理端点独立于会话的安全上下文而受到保护?
出于披露的目的,我调试了 * 很多 ,基本上尝试了整个basics!我还尝试了一些其他的东西:
根据Spring Security Documentation,可以在安全配置中将.sessionManagement
设置为STATELESS。我曾希望这能解决这个问题,但在资源服务器过滤器链中设置这个会导致另一个问题:如果会话管理标志设置为STATELESS,则无法正确处理登录。在登录表单的POST请求之后,认证服务器重定向到“/",而不是从/authorize
请求重定向到“redirect_url”。 我认为这是因为auth服务器模块依赖于保存到会话的安全上下文来进行某些过滤。*
我也有一些问题与CORS,并认为这可能会导致这一点。考虑到docs说:
CORS必须在Spring Security之前处理,因为飞行前请求不会包含任何Cookie(即:JSESSIONID)。如果请求不包含任何cookie,并且Spring Security是第一个,则请求将确定用户未通过身份验证(因为请求中没有cookie)并拒绝它。
这可以解释前端Vue.js应用程序不能正常工作,但不是通过Postman的调试调用。现在我停用了CORS,不处理这些问题。
2条答案
按热度按时间tpxzln5u1#
你的资源服务器过滤链不应该包含任何关于登录和注销的内容。
资源服务器过滤器链可以配置为“无状态”(没有会话),因为安全上下文是在访问令牌之外(而不是会话)构建的。
登录是OAuth2客户端关注点,而不是资源服务器关注点:这是客户端向资源服务器发送请求,资源服务器获取并存储令牌(资源服务器业务仅是令牌验证和访问控制)。
使用
oauth2Login
的客户端需要会话(以及针对CSRF的保护)(它在authorization_code流期间使用,用于存储令牌和授权传入请求)。如果您的应用程序还托管管理 Jmeter 板的客户端(带有Tymeleaf等模板引擎的Spring MVC控制器),那么您可以为其定义第三个过滤器链(带有会话,CSRF保护和
oauth2Login
)。但是,由于您的 Jmeter 板UI似乎是一个SPA(Vue应用程序),它不能是一个机密客户端,登录应该由中间件处理:
这通常被称为BackendF或Frontend模式(BFF),我写了a tutorial for that。
wgmfuz8q2#
在您的defaultSecurityFilterChain配置中,强制验证需要不记名令牌