spring-security Sping Boot -无需数据库调用的JWT身份验证

jei2mxaa  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(201)

是否可以实现简单的JWT身份验证(不考虑使标记无效-我将在缓存中执行此操作),而不调用数据库将用户加载到安全上下文中?我看到我当前的实现在每次调用api时都会命中数据库(将用户加载到安全上下文中)。下面您可以看到JwtAuthenticationFilter扩展OncePerRequestFilter的部分实现:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    try {
        String jwt = getJwtFromRequest(request);
        if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
            Long userId = tokenProvider.getUserIdFromJWT(jwt);
            UserDetails userDetails = customUserDetailsService.loadUserById(userId);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    } catch (Exception ex) {
        logger.error("Could not set user authentication in security context", ex);
    }
    filterChain.doFilter(request, response);
}

下面是对数据库的调用,我希望避免这种调用(对于每个对api的经过身份验证的调用):

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    // This method is used by JWTAuthenticationFilter
    @Transactional
    public UserDetails loadUserById(Long id) {
        User user = userRepository.findById(id).orElseThrow(
            () -> new UsernameNotFoundException("User not found with id : " + id)
        );
        return UserPrincipal.create(user);
    }
}

我找到了一些解决问题的方法来构建UserPrincipal对象(它实现了UserDetails接口),仅具有用户ID用户名授予的权限,而没有例如密码,我无法从JWT标记本身读取这些密码(剩下的我可以),但我不确定它是否安全,是否被认为是一个良好的实践解决方案,(UserDetails类需要密码字段,我认为存储它是不明智的)。我需要UserPrincipal示例(实作UserDetails界面)以支援作为UsernamePasswordAuthenticationToken的参数,如您在第一段中所见。

cgfeq70w

cgfeq70w1#

一种方法可以如下具有两个滤波器。

  • 仅为登录请求/终结点提供服务的CustomAuthenticationFilter。您可以在此筛选器中执行以下操作,
  • 调用数据库并验证凭据,然后检索用户的角色
  • 生成JWT标记,您可以将用户ID或电子邮件沿着角色存储在JWT标记的主题中。由于我们要添加特定于用户的详细信息,因此建议对JWT标记进行加密。
  • 为所有其他请求/终结点提供服务的CustomAuthroizationFilter。您可以在此筛选器中执行以下操作,
  • 验证JWT标记
  • 从JWT标记的主题中检索用户ID或电子邮件沿着用户的角色。
  • 构建Spring身份验证(UsernamePasswordAuthenticationToken),并像您所做的那样在SecurityContextHolder中对其进行设置。

这样,您将只在登录请求期间调用db,而不是为所有其他api端点调用。

相关问题