我在哪里从 Spring Security 中获取“用户名”值以传递给 UserDetailsS​​ervice 接口的 loadUserByUsername(String username) 方法

Posted

技术标签:

【中文标题】我在哪里从 Spring Security 中获取“用户名”值以传递给 UserDetailsS​​ervice 接口的 loadUserByUsername(String username) 方法【英文标题】:where do I get the "username" value from in spring security to pass to the loadUserByUsername(String username) method of UserDetailsService interface 【发布时间】:2021-05-29 20:43:23 【问题描述】:

我正在尝试通过根据用户名和密码对用户进行身份验证来从数据库中获取用户。我正在使用基本身份验证来执行此操作。 我在其余 api 的授权标头中发送用户名和密码

在我的控制器中,getUser() 方法调用 UserService 类的 getuser() 方法

 @GetMapping("/user/self")
public ResponseEntity<UserDto> getUser() 
    UserDto UserDto = userService.getUser();
    return new ResponseEntity<>(UserDto, HttpStatus.OK);


@PutMapping("/user/self")
public ResponseEntity<User> updateUser(@Valid @RequestBody Map<String, String> userMap, Principal principal) 
    String username = principal.getName();
    String firstname = userMap.get("firstName");
    String lastName = userMap.get("lastName");
    String password = BCrypt.hashpw(userMap.get("password"), BCrypt.gensalt(10));
    User user = userService.getUserByUserName(username);
    user.setFirstName(firstname);
    user.setLastName(lastName);
    user.setPassword(password);
    userService.save(user);
    return new ResponseEntity<>(user, HttpStatus.NO_CONTENT);

UserService 类实现 UserDetailsS​​ervice 并覆盖需要将用户名作为参数传递的 loadUserByUsername 方法。我的问题是:如何从我的控制器调用的 UserService 类中将用户名传递给 loadUserByUsername() 方法。用户名值在哪里? 我的理解是 - 身份验证对象包含当用户键入他们的凭据并发送他们的请求时传递给身份验证对象的用户凭据,我如何检索这个用户名值

 @Service
     public class UserService implements UserDetailsService 

@Autowired
UserRepository userRepository;

public UserDto save(User user) 
    String hashedPassword = BCrypt.hashpw(user.getPassword(), BCrypt.gensalt(10));
    user.setPassword(hashedPassword);
    userRepository.save(user);
    UserDto userDto = new UserDto();
    userDto.setId(user.getId());
    userDto.setFirstName(user.getFirstName());
    userDto.setLastName(user.getLastName());
    userDto.setUserName(user.getUserName());
    userDto.setAccountUpdatedAt(user.getAccountUpdatedAt());
    userDto.setAccountCreatedAt(user.getAccountCreatedAt());
    return userDto;


@Override
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException 
    User user = userRepository.findByUserName(userName);
    if (user == null) 
        throw new UsernameNotFoundException(userName + "was not found");
    
    return new UserPrincipal(user);

这是我的存储库代码:

 @Repository
    public interface UserRepository extends CrudRepository<User, Long> 

User findByUserName(String userName);
   

这是我的验证码:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

@Autowired
DataSource dataSource;
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
UserService userService;


@Bean
public PasswordEncoder passwordEncoder() 
    return new BCryptPasswordEncoder();


public void configure(AuthenticationManagerBuilder auth) throws Exception 
    auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder());




@Override
protected void configure(HttpSecurity http) throws Exception 
    http.cors().and().csrf().disable();
    http.authorizeRequests().antMatchers("/v1/user").permitAll()
            .antMatchers("/v1/user/self").authenticated().and().httpBasic()
            .authenticationEntryPoint(authenticationEntryPoint);
 

【问题讨论】:

如果使用基本认证,在用户登录后,客户端会得到一个会话cookie。在每个请求中,此会话 cookie 将在每个请求中返回到服务器。使用会话 cookie 中的 id,服务器将使用用户信息填充 Principal。如果您想检索该信息,可以通过多种方式在此处阅读它们baeldung.com/get-user-in-spring-security 【参考方案1】:

如果您处理 JPA,那么在您的情况下,您必须使用 userDetailsService 而不是 jdbcauthentication,因此您的安全类将如下所示:

@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    private  UserService userService;
    
    public SecurityConfig(UserService userService)
        this.userService = userService;
    

    @Bean
    public PasswordEncoder passwordEncoder()
        return new BCryptPasswordEncoder(10); // Number of rounds 
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(this.userService).passwordEncoder(passwordEncoder());
    

然后您可以在UserService 类中自定义身份验证以满足业务需求,如下示例:

@Service
public class UserService implements UserDetailsService 

    private final UserRepository userRepository;
    
    public UserService(UserRepository userRepository)
        this.userRepository = userRepository;
    

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 

        Optional<User> user = userRepository.findByUsername(username);

        if(user.isPresent())
            log.info("cretaed under User service : " + user.get());
            return user.get();
        

        throw new UsernameNotFoundException("empty or invalud user");
    

此外,不要忘记在你的存储库中创建findByUsername 方法,也不要忘记在你的模块类中实现org.springframework.security.core.userdetails.UserDetails

@Repository
public interface UserRepository extends JpaRepository<User, Long> 

    Optional<User> findByUsername(String name);

【讨论】:

感谢您抽出宝贵时间回答我的问题。我需要澄清一件事。 `` @shahi 您必须将用户名和密码与标头中的每个请求作为httpBasic 身份验证一起传递,并在每个端点的配置类中指定您的角色 您可以删除整个configure(AuthenticationManagerBuilder auth),而只需将用户服务声明为@BeanUserDetailServicePasswordEncoder 将在 spring 自动插入 @Toerktumlare 是的,我知道,但出于演示目的,我决定为他简化一些事情,如果您有其他选择,请随时编辑它 @MahmoudOdeh 谢谢!这样做`@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception auth.userDetailsS​​ervice(this.userService).passwordEncoder(passwordEncoder()); `帮助。而不是使用 jdbcauthentication()。 @Toerktumlare 谢谢!我能够从 Principal 对象中检索当前经过身份验证的用户 @GetMapping("/user/self") public ResponseEntity getUser(Principal principal) String username = principal.getName(); UserDto UserDto = userService.getUser(用户名);

以上是关于我在哪里从 Spring Security 中获取“用户名”值以传递给 UserDetailsS​​ervice 接口的 loadUserByUsername(String username) 方法的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security:从数据库表中获取当前用户的字段

spring security从数据库获取用户ID

Zuul spring security 并在请求中添加附加参数

如何在 Spring Security 中获取 userInformation?

CORS 干扰 Spring Security oauth2

在 spring-security 的身份验证中无法获取自定义信息