我可以混合使用基本身份验证和 JWT 令牌身份验证来保护单个 Spring 启动项目的 API 吗?

Can I mix both basic authentication and JWT token authentication to protect APIs of a single Spring Boot project?

我是 Spring 安全方面的新手,我正在开发一个 Spring 引导项目,该项目使用基本身份验证来保护一些 API。我从现有的教程代码(Udemy 课程)开始尝试使其适应我自己的用例。

在这个项目中,我有这个 SecurityConfiguration 用于配置基本身份验证。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
    private static String REALM = "REAME";
    
    private static final String[] USER_MATCHER = { "/api/utenti/cerca/**"};
    private static final String[] ADMIN_MATCHER = { "/api/utenti/inserisci/**", "/api/utenti/elimina/**" };

    @Override
    protected void configure(HttpSecurity http) throws Exception
    {
            http.csrf().disable()
                .authorizeRequests()
                .antMatchers(USER_MATCHER).hasAnyRole("USER")
                .antMatchers(ADMIN_MATCHER).hasAnyRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthEntryPoint()).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    
    @Bean
    public AuthEntryPoint getBasicAuthEntryPoint()
    {
        return new AuthEntryPoint();
    }

    /* To allow Pre-flight [OPTIONS] request from browser */
    @Override
    public void configure(WebSecurity web) 
    {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    };

    @Bean
    @Override
    public UserDetailsService userDetailsService()
    {
        UserBuilder users = User.builder();

        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();

        manager.createUser(users
                .username("ReadUser")
                .password(new BCryptPasswordEncoder().encode("BimBumBam_2018"))
                .roles("USER").build());

        manager.createUser(users
                .username("Admin")
                .password(new BCryptPasswordEncoder().encode("MagicaBula_2018"))
                .roles("USER", "ADMIN").build());

        return manager;
    }
}

据我了解:

这里id定义了普通用户可以访问的API列表和admin用户可以访问的API列表:

private static final String[] USER_MATCHER = { "/api/utenti/cerca/**"};
private static final String[] ADMIN_MATCHER = { "/api/utenti/inserisci/**", "/api/utenti/elimina/**" };

进入前面的configure()方法基本上是说明APIURL与[=110=匹配] 可由具有 USER 角色的登录用户访问,而 API 具有 URL 匹配 ADMIN_MATCHER 可由具有 ADMIN 角色的登录用户访问。这个解释正确吗?

最后,UserDetailsS​​ervice bean 简单地定义了两个用户:一个属于 USER“组”,另一个同时属于USERADMIN“组”。

因此,如果我理解得很好,第一个将很容易访问具有 enpoint URL /api/utenti/cerca/[=83= 的 API ]** 而第二个也将能够访问具有端点 URLs /api/utenti/inserisci/** 和 /api/utenti/elimina/**

我的推理是否正确?

现在我怀疑:进入这个项目的控制器 class 我定义了这个方法:

@RestController
@RequestMapping("api/users")
@Log
public class UserController {
    
    @Autowired
    UserService userService;
    
    //@Autowired
    //private BCryptPasswordEncoder passwordEncoder;
    
    //@Autowired
    //private ResourceBundleMessageSource errMessage;
    
    @GetMapping(value = "/test", produces = "application/json")
    public ResponseEntity<String> getTest() throws NotFoundException  {
        
        log.info(String.format("****** getTest() START *******"));
        
        return new ResponseEntity<String>("TEST", HttpStatus.OK);
        
    }
    
    ..............................................................................................................
    ..............................................................................................................
    ..............................................................................................................
}

如您所见,此方法处理对 localhost:8019/api/users/test 端点的 GET 请求。

此端点 URL 不在前两个与受保护端点相关的列表中(它不在 USER_MATCHER 列表中,也不在ADMIN_MATCHER 列表。所以我预计只是这个端点没有受到保护并且每个人都可以访问。但是使用 PostMan 执行之前的请求,我收到此错误消息:

HTTP Status 401 : Full authentication is required to access this resource

所以基本上在我看来,如果这个端点不属于任何受保护的端点列表,它无论如何都会受到某种方式的保护(在我看来,至少用户必须经过身份验证(事实上,同时尝试以前的用户我可以获得预期的输出,所以这应该意味着端点不受用户规则的保护,但它受到保护 againts 未经过身份验证的访问)。

为什么?也许它取决于之前的 configure() 方法设置,特别是这一行?

.anyRequest().authenticated()

万一有可能以某种方式禁用来实现这样的事情:

  • 如果被叫端点属于前两个列表之一(USER_MATCHERADMIN_MATCHER) --> 用户必须经过身份验证并且需要具有正确的角色。

  • 如果被叫端点不属于前面的列表之一 --> 每个人都可以访问,也不是经过身份验证的用户。

这种方法有意义,还是我失去了什么?

借此机会我还想问你另一个信息:你认为可以配置这个特定项目的安全性 Spring 以使用基本身份验证和其他一些特定的方式来保护一些特定的端点使用 JWT 身份验证的端点。

Sone 进一步注释以解释最后一个问题的原因。该项目是一个微服务,目前被另一个微服务(用于生成 JWT 令牌)用于获取用户信息。 (另一个微服务调用此项目的 API 以接收用户信息,以便它可以生成将在我的应用程序中使用的 JWT 令牌。这两个微服务之间的通信必须使用基本身份验证)。

由于该项目包含所有实体 classes 用于映射与我的数据库中的用户相关的表,我的想法是将该项目也用于通用用户管理,因此它可以包括类似的功能: 添加全新用户、修改现有用户信息、获取所有用户列表、搜索特定用户等。

这些新的 API 将受到 JWT 令牌的保护,因为每个 API 都可以从在系统上具有不同权限的特定用户类型调用。

所以我问的是在这种情况下我是否可以毫无问题地添加 2 种不同类型的身份验证(API 的基本身份验证检索用户以便其他微服务可以获得此信息)和 JWT所有其他 API 的身份验证。为新的用户管理微服务创建一个全新的项目有意义还是更好?

So, if I well understood, the first one will be aple only to access to the API having enpoint URL /api/utenti/cerca/** while the second one will be able to access also to the APIs having endpoint URLs /api/utenti/inserisci/** and /api/utenti/elimina/**

是的。

Why? Maybe it depende by the previous configure() method settings, in particular this line?

是的,使用 .anyRequest().authenticated() 时,任何未匹配的请求都必须进行身份验证。

If a called endpoint not belong to one of the previous lists --> everybody can access, also not authenticated user.

您可以通过 anyRequest().permitAll() 来实现。但这并不是那么安全,因为您允许访问所有其他端点,相反您应该继续使用 anyRequest().authenticated() 并允许手动访问特定端点,如下所示:

http
                .authorizeRequests()
                .antMatchers(USER_MATCHER).hasAnyRole("USER")
                .antMatchers(ADMIN_MATCHER).hasAnyRole("ADMIN")
                .antMatchers("/api/users/test").permitAll()
                .anyRequest().authenticated()
                ...