如何在 Spring Boot 中使用 RESTful 和基本身份验证

Posted

技术标签:

【中文标题】如何在 Spring Boot 中使用 RESTful 和基本身份验证【英文标题】:How to use RESTful with Basic Authentication in Spring Boot 【发布时间】:2019-05-29 20:28:05 【问题描述】:

您好,我正在使用带有基本身份验证的 RESTful,此代码是 RestController 的一部分:

@GetMapping("/jpa/users/username/goals")
public List<Goal> getAllGoals(@PathVariable String username) 
    userId = getUserIdFromUsername(username);
    return goalJpaRepository.findByUserId(userId); 


public Long getUserIdFromUsername(String username) 
    User user = userJpaRepository.findByUsername(username);
    userId = user.getId(); 
    return userId;

我有一个问题,例如我正在使用 Postman 来检索特定用户的目标,如下所示:

http://localhost:8080/jpa/users/john/goals 带有 GET 请求

然后我使用用户名 john 的基本身份验证和此用户名的密码,我收到了 john 的目标。

之后,如果我对此链接 http://localhost:8080/jpa/users/tom/goals 发出 GET 请求,我会收到 tom 的目标,但此时我使用 john 登录,所以 john 可以看到他的目标,也可以看到 tom 的目标目标。

问题是如何访问 RestController 中的登录用户名,因为我想做这样的事情:

if (loginUsername == username) 
    return goalJpaRepository.findByUserId(userId);
 

return "Access denied!";

所以我想知道是否可以从 HTTP Header 访问登录用户名?

谢谢!


更新 - 是的,框架是 Spring Boot,我也在使用带有 Dao 身份验证的 Spring Security,因为我想从 mysql 数据库中获取用户。无论如何,我不是 Spring Security 方面的专家。

现在我了解了如何在我的控制器方法中使用 Principal,但我不知道如何在这种特定情况下使用 Spring Security。我应该如何实施?例如,用户 john 应该只看到和修改他的目标。

Spring 安全配置:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.dgs.restful.webservices.goaltrackerservice.user.MyUserDetailsService;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter 

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Bean
    public DaoAuthenticationProvider authenticationProvider() 
        DaoAuthenticationProvider authProvider
          = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(bCryptPasswordEncoder());
        return authProvider;
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
            .antMatchers("/allusers").permitAll()
                .anyRequest().authenticated()
                .and()
            // .formLogin().and()
            .httpBasic();
        

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.authenticationProvider(authenticationProvider());
    

【问题讨论】:

你在用弹簧吗?特别是spring-security? 旁注,loginUsername 的数据类型是什么?如果它的 String,那么你的比较是错误的。 这取决于您使用的框架(如果您正在使用)。您说的是RestController,所以我猜您正在使用Spring(您绝对应该在您的问题中添加适当的标签)。如果是这样,请查看 Spring 安全性,它允许您配置如何保护您的应用程序。 【参考方案1】:

假设您使用 Spring 作为 Java 框架,您应该使用 Spring 安全性来配置基本身份验证。网上有很多教程(https://www.baeldung.com/spring-security-basic-authentication,

然后,Spring Security 将提供整个应用程序可用的安全上下文 (SecurityContextHolder.getContext()),您可以从中检索连接的用户信息(用户名,...)。

例如,要检索已连接用户的用户名,您应该这样做:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String loginUsername = authentication.getName();

或者,正如@gervais.b 所述,Spring 可以在您的控制器方法中注入Principal(或Authentication)。

正如@Glains 所说,一个更好的选择是使用@PreAuthorize@PostAuthorize 注释,它允许您基于Spring 表达式语言定义简单的规则。

【讨论】:

感谢马特的帮助,我刚刚更新了我的问题。我正在使用 Spring Boot,并且我正在使用带有 Dao 身份验证的 Spring Security,因为我想从数据库中获取用户。现在我明白了如何在控制器方法中使用Principal来实现我想要的,但是我不知道如何使用Spring Security,我应该在spring security配置文件中添加什么? Spring Security 可以帮助您制定基于角色的一般规则,例如端点“/users/*”只能由连接的用户(或具有角色 X 的用户)访问。正如@Glains 在他的回答中提到的那样,它还允许您使用注释定义简单的规则,例如使用 Spring EL 表达式的@PreAuthorize【参考方案2】:

请注意,您目前没有做任何安全措施。

正如@Matt 所说,“这取决于您使用的框架”。但我猜你正在使用弹簧。然后你应该看看 spring-securuty 模块文档。

基本上,您可以将经过身份验证的用户注入您的方法参数:

   @GetMapping("/jpa/users/username/goals")
   public List<Goal> getAllGoals(@PathVariable String username, Principal principal) 
     if ( username.equals(principal.getName()) ) 
       userId = getUserIdFromUsername(username);
       return goalJpaRepository.findByUserId(userId); 
      else 
       throw new SomeExceptionThatWillBeMapped();
     
    

但是 spring-security 和许多框架提供了更好的模式来管理安全性。

【讨论】:

感谢您的回复!我刚刚添加了这个,它工作得很好。我已经使用带有 dao 身份验证的 spring security,但我不知道如何在这种特定情况下使用 spring security。如果您想看一下,我刚刚更新了问题。 您应该考虑为此创建一个新问题并接受此问题的答案之一。【参考方案3】:

您也可以使用 @PreAuthorize 解决这个问题,这是 Spring Security Framework 提供的一个注解,它使用 Spring 表达式语言。

@PreAuthorize("principal.name == #username")
@GetMapping("/jpa/users/username/goals")
public List<Goal> getAllGoals(@PathVariable String username) 
    return goalJpaRepository.findByUserId(userId); 

在幕后 Spring 将使用已经提到的SecurityContextHolder 来获取当前经过身份验证的主体。如果表达式解析为假,则返回响应码403

请注意,您必须启用全局方法安全性:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration 


【讨论】:

【参考方案4】:

要回答您关于“Dao 身份验证”的新问题,答案是提供自定义UserDetailsService

从您附加到问题的配置看来,您已经有一个MyUserDetailsService

有很多文章解释了如何使用自定义DetailsService。这似乎符合您的要求:https://www.baeldung.com/spring-security-authentication-with-a-database

编辑:关于如何确保只有 John 可以看到 John 的项目。

基本上,您可以做的唯一事情是确保只有 John 可以看到他的目标,将目标限制在 John 拥有的目标上。但是有很多方法可以做到这一点。

    正如您在最初的问题中所建议的,您可以只为特定用户选择目标。 spring-security 的强大之处在于它可以注入Principal,但也可以注入其他身份验证对象。

    您还可以使用SecurityContextHolder 使DAO/存储库端的过滤器更加隐式。当您的系统更加以用户为中心或类似于多租户系统时,这种方法很好并且看起来更好。

    使用一些特定的 @Annotations 或 Aspects 也是一种解决方案,但在这种情况下可能不太明显。

【讨论】:

谢谢,但问题不在于“Dao Authentication”,我已经实现了这个......这是老问题,当我使用 Postman 时,我使用 john 和我可以检索 john 的目标以及任何其他用户的目标,当我使用 john 登录时,我想只允许检索 john 的目标......你说我可以将经过身份验证的用户注入到我的方法参数中,我做了这个并且完美运行...您还说 spring-security 比这更好,我想知道如何在这种情况下使用 spring-security。 我说spring-security是一个很好的安全框架。而且,既然你确定春天,这是事实上的选择。我将编辑此答案以解释您的替代方案。

以上是关于如何在 Spring Boot 中使用 RESTful 和基本身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring Boot 中使用 Spring Security 启用 CORS

如何在 Spring Boot 中使用 Spring Security 配置 CORS? [复制]

如何在Spring Boot 中使用 HandlerMethodArgumentResolver

spring boot 如何使用多线程

如何在 spring-boot 中替换最新的 Jetty 9?

如何在 Spring Boot 中手动新建实例中使用 @Autowired