在創建授權服務器時,我有一個比較簡單的 AS。它支持:
- 用戶注冊 (WebMVC)
- 表單登錄 (WebMVC)
- 忘記密碼 (WebMVC)
- 管理 RegisteredClient (WebMVC) - 人們可以管理他們的 API 客戶端的地方,這些客戶端交換訪問令牌以訪問其他資源服務器。
我也有一些 API @RestController 端點;但是,我觀察到我無法向他們發出經過 JWT 身份驗證的請求,因為身份驗證程序不適用于這些請求,因為我正在獲取以 200 呈現的登錄頁面內容的基于會話的 .formlogin() 樣式顯示好的,而不是我所期望的—— 401 或 403 或 200 好的,但帶有 RESTful 應用程式/json 結構化答案。
如何同時支持基于會話的 WebMVC 流以及依賴 JWT 身份驗證的 REST 控制器端點?
import org.junit.jupiter.api.Order;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class DefaultSecurityConfiguration {
public static final int FILTER_PRECEDENCE = -5;
@Bean
@Order(FILTER_PRECEDENCE)
SecurityFilterChain defaultSecurityFilterChain(final HttpSecurity http) throws Exception {
return http.authorizeRequests(authorizeRequests ->
authorizeRequests
.mvcMatchers("/favicon.ico", "/favicon-16x16.png", "/favicon-32x32.png", "/mstile-150x150.png", "/apple-touch-icon.png", "/", "/assets/**", "/login/**", "/webjars/**", "/register", "/register-form", "/actuator/health", "/reset-password", "/reset-password-2", "/error")
.permitAll()
).formLogin(oauth2 ->
oauth2
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?error=1")
.permitAll()
)
.build();
}
}
這是我的 AuthorizationServerConfiguration 的過濾器鏈配置:
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(final HttpSecurity http) throws Exception {
final OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();
final RequestMatcher endpointsMatcher = authorizationServerConfigurer
.getEndpointsMatcher();
http
.requestMatcher(endpointsMatcher)
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.apply(authorizationServerConfigurer);
return http.formLogin(Customizer.withDefaults()).build();
}
而且,對于咯咯笑,這是我希望作業的示例 REST 端點:
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping("/oauth/account")
public class AccountController {
public static final String ACCOUNT_ID_IS_REQUIRED = "Account Id is required.";
private final UserDetailRepository userDetailRepository;
private final AccountService accountService;
/**
* Return List of all account and clients of logged in user
*
* @param authentication
* @return
*/
@GetMapping
public List<AccountResponseDTO> findAllUserAccounts(final Authentication authentication) {
final User user = this.userDetailRepository.findByUsername(authentication.getName()).get();
return this.accountService.findAccountsByUserId(user.getId());
}
除了簡單之外,我還很好奇為什么在自定義同意服務器的示例中,在兩個不同的安全過濾器中重復宣告了 .formLogin() 。我一直在玩這個,我對安全過濾器覆寫有點難過。
如果我在 .formLogin() 中存在差異,我會觀察到默認值或間歇性故障。如果我在更高價值的 @Order 上洗掉 .formLogin ,則登錄不起作用,但我得到了一個有效的 REST 端點。如果我在這兩個地方都有 .formLogin() 我有一個很好的 WebMVC 方面;但是,我的 REST 控制器身份驗證僅回傳 JWT 的承載值,而不是身份驗證主體。這里發生了奇怪的事情 - 任何幫助將不勝感激!
uj5u.com熱心網友回復:
考慮配置 3 個安全過濾器鏈。第一個用于身份驗證,第二個用于具有承載令牌的呼叫,最后一個用于所有其他 MVC 請求:
@Bean
@Order(1)
@SuppressWarnings("unused")
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
return http
.requestMatcher(endpointsMatcher)
.authorizeRequests(authorizeRequests ->
authorizeRequests.anyRequest().authenticated()
)
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.apply(authorizationServerConfigurer)
.oidc(oidc -> oidc
.clientRegistrationEndpoint(Customizer.withDefaults())
)
.and()
.formLogin(Customizer.withDefaults()).build();
}
@Bean
@Order(2)
@SuppressWarnings("unused")
public SecurityFilterChain resourceServerOauthFilterChain(HttpSecurity http) throws Exception {
http
.requestMatcher(request -> {
String headerValue = request.getHeader("Authorization");
return headerValue != null && headerValue.startsWith("Bearer");
})
.authorizeRequests()
.anyRequest().authenticated()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.oauth2ResourceServer().jwt(Customizer.withDefaults());
return http.build();
}
@Bean
@Order(3)
@SuppressWarnings("unused")
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
return http.authorizeRequests(authorizeRequests ->
authorizeRequests
.mvcMatchers("/favicon.ico", "/favicon-16x16.png", "/favicon-32x32.png", "/mstile-150x150.png", "/apple-touch-icon.png", "/", "/assets/**", "/login/**", "/webjars/**", "/register", "/register-form", "/actuator/health", "/reset-password", "/reset-password-2", "/error")
.permitAll().anyRequest().authenticated()
)
.formLogin(oauth2 ->
oauth2
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/")
.failureUrl("/login?error=1")
.permitAll()
)
.build();
}
至于你的第二個問題,我認為每個過濾器鏈都有自己的過濾器集。由于登錄也是作為過濾器實作的,因此應該將其添加到相應的過濾器鏈中。
更新:似乎您對 Order 注釋的匯入不正確: import org.junit.jupiter.api.Order;
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/468182.html
