【问题标题】:Can I mix both basic authentication and JWT token authentication to protect APIs of a single Spring Boot project?我可以混合使用基本身份验证和 JWT 令牌身份验证来保护单个 Spring Boot 项目的 API 吗?
【发布时间】:2022-01-03 22:38:20
【问题描述】:

我是 Spring Security 的新手,我正在开发一个 Spring Boot 项目,该项目使用基本身份验证来保护一些 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列表和管理员用户可以访问的API列表:

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

在前面的 configure() 方法中,基本上是声明与 USER_MATCHER 匹配的 API URL 可由具有角色 USER 的登录用户访问> 而具有 URL 匹配 ADMIN_MATCHER 的 API 可由具有角色 ADMIN 的登录用户访问。这种解释正确吗?

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

所以,如果我很好理解的话,第一个只能访问具有 enpoint URL /api/utenti/cerca/** 的 API,而第二个将能够访问也适用于具有端点 URL /api/utenti/inserisci/** 和 /api/utenti/elimina/**

的 API

我的推理正确吗?

现在我的疑问是:在这个项目的控制器类中我定义了这个方法:

@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

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

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

.anyRequest().authenticated()

如果有可能以某种方式禁用以实现类似这样的功能:

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

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

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

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

Sone 进一步说明了为什么最后一个问题。该项目是一个微服务,目前被另一个微服务(用于生成 JWT 令牌)用于获取用户信息。 (其他微服务调用该项目的 API 以接收用户信息,以便生成将在我的应用程序中使用的 JWT 令牌。这两个微服务之间的通信必须使用基本身份验证)。

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

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

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

【问题讨论】:

  • 最好为你的第二个问题开一个新问题

标签: java spring spring-boot spring-security spring-security-rest


【解决方案1】:

所以,如果我很好理解的话,第一个只能访问具有端点 URL /api/utenti/cerca/** 的 API,而第二个也可以访问具有端点 URL 的 API /api/utenti/inserisci/** 和 /api/utenti/elimina/**

是的。

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2018-06-21
    • 2021-01-25
    • 2014-07-21
    • 1970-01-01
    • 2020-02-04
    • 2018-02-27
    • 2017-11-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多