首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >支持并发全堆栈MVC (会话)身份验证以及(0.2.3+)中的无状态JWT身份验证

支持并发全堆栈MVC (会话)身份验证以及(0.2.3+)中的无状态JWT身份验证
EN

Stack Overflow用户
提问于 2022-04-05 01:08:40
回答 1查看 99关注 0票数 0

在创建授权服务器时,我有一个相对简单的AS。它支持:

  • 用户注册(WebMVC)
  • FormLogin (WebMVC)
  • 忘记密码(WebMVC)
  • 管理RegisteredClient (WebMVC) --人们可以在那里管理他们的API客户端,这些客户端通过交换访问令牌来访问其他资源服务器。

我还有一些API @RestController端点;但是,我注意到我不能向它们发出JWT身份验证请求,因为身份验证过程并不适用于这些请求,因为我得到了一个基于会话的.formlogin()风格的显示登录页面内容的样式,它使用的是200 OK而不是我所期望的-- 401或403或200 OK,但是使用了RESTful应用程序/json结构化答案。

如何同时支持基于会话的WebMVC流以及依赖于JWT身份验证的REST控制器端点?

代码语言:javascript
复制
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的过滤器链配置:

代码语言:javascript
复制
@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端点示例:

代码语言:javascript
复制
@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的承载值,而不是身份验证主体。这里发生了一些奇怪的事情--任何帮助都会很感激!

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-06 13:15:07

请考虑配置3条安全筛选链。第一个用于auth,第二个用于具有承载令牌的呼叫,最后一个用于所有其他MVC请求:

代码语言:javascript
复制
   @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();
   }

至于你的第二个问题,我认为每个过滤器链都有自己的过滤器集。由于登录也是作为过滤器实现的,因此应该将其添加到相应的筛选链中。

更新:似乎您有错误的导入订单注释:导入org.junit.jupiter.api.Order;

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71745323

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档