【问题标题】:How can I update user info from Scheduled in Spring Boot如何从 Spring Boot 中的 Scheduled 更新用户信息
【发布时间】:2021-12-04 19:21:20
【问题描述】:

当我尝试将用户保存在计划任务中,然后通过 Authentication.getPrincipal() 在控制器中访问它时,尽管底层数据库记录发生更改,但它不会得到更新。

@Scheduled(fixedRate = 10_000)
public void job() {
    User user = userRepo.findById(1).get();
    user.setEmail("someNewEmail@gmail.com");
    userRepo.save(user);
}

我使用 Spring Security,我相信用户的信息会以某种方式缓存在 SecurityContextHolder 中,因此不允许我使用已保存用户的更新值。

此外,如果我执行 SignOut - SignIn 数据会更新,但这不能视为解决方案。

作为一种解决方法,我尝试使用自动装配的 EntityManager 并在获取数据之前使用用户刷新记录,但这假设我应该为需要获取用户的每个请求执行此操作。也不是最好的解决方案

除用户外的其他实体都保存完好

【问题讨论】:

    标签: spring-boot spring-security


    【解决方案1】:

    您必须在过滤器中的每个请求之前更新安全上下文持有者中的用户,如下所示:

    public class TokenValidationFilter extends OncePerRequestFilter {
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException {
            // read user from database using data from request header information
            // set user in SecurityContextHolder
            chain.doFilter(request, response);
        }
    }
    

    那么你必须将此过滤器添加到配置中

    @Configuration
    @EnableWebSecurity
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
        
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable()
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .addFilterBefore(new TokenValidationFilter(), UsernamePasswordAuthenticationFilter.class)
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
    }
    

    这样做不会有问题

    【讨论】:

      【解决方案2】:

      我最终获得了一个带有容器的服务,该容器包含我需要更改其数据的用户。

      @Service
      @Getter
      @Setter
      public class AuthService {
      
          private List<User> users = new CopyOnWriteArrayList<>();
      
      }
      

      我还按照 Mehdi 的建议实施了一个过滤器。

      public class AuthFilter extends GenericFilterBean {
      
          @Autowired
          private AuthService authService;
          @Autowired
          private UserRepository userRepo;
      
          @Override
          public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
              Authentication auth = SecurityContextHolder.getContext().getAuthentication();
              if (auth == null || !auth.isAuthenticated() || !(auth.getPrincipal() instanceof UserDetails)) {
                  chain.doFilter(request, response);
                  return;
              }
              User currUser = ... // getting current user from auth
              if (authService.getUsers().contains(currUser)) {
                  UserDetails details = myUserDetailsService.loadUserByUsername(currUser.getFirstName());
                  Authentication updatedAuth = new UsernamePasswordAuthenticationToken(details, currUser.getPassword(), details.getAuthorities());
                  SecurityContextHolder.getContext().setAuthentication(updatedAuth);
                  authService.getUsers().remove(currUser);
              }
              chain.doFilter(request, response);
          }
      }
      

      很遗憾,spring 没有提供一个包含所有 Authentication 对象的容器以使更改用户更容易。或者我没找到 此外,我认为可以通过在 SecurityContextHolder (或任何具有此信息的弹簧安全类)周围创建某种包装器来在自定义过滤器中创建这样的容器,但实现起来会更困难

      【讨论】:

        猜你喜欢
        • 2021-04-15
        • 2021-01-25
        • 1970-01-01
        • 2020-10-02
        • 1970-01-01
        • 2017-10-11
        • 2016-11-17
        • 2020-07-24
        • 2015-04-04
        相关资源
        最近更新 更多