当前位置: 首页 > 知识库问答 >
问题:

使用JWT令牌的Spring Security在每个请求上都加载Spring User细节对象

夏季萌
2023-03-14

我正在用JWT令牌和Spring靴学习Spring安全性。我已经正确实现了它,它工作正常。但我对JwtRequestFilter的工作原理有一个疑问。我浏览了几个网站,以了解Spring靴的Spring安全性,并发现了同样的事情。因此,让我来谈谈主要的疑问。我正在添加JwtRequestFilter文件。

JwtRequestFilter.java

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

@Autowired
private JwtUserDetailsService jwtUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    final String requestTokenHeader = request.getHeader("Authorization");

    String username = null;
    String jwtToken = null;

    // JWT Token is in the form "Bearer token". Remove Bearer word and get
    // only the Token
    if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
        jwtToken = requestTokenHeader.substring(7);
        try {
            username = jwtTokenUtil.getUsernameFromToken(jwtToken);
        } catch (IllegalArgumentException e) {
            System.out.println("Unable to get JWT Token");
        } catch (ExpiredJwtException e) {
            System.out.println("JWT Token has expired");
        }
    } else {
        logger.warn("JWT Token does not begin with Bearer String");
    }
    // Once we get the token validate it.
    if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
         // This below line is calling on every request
        UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
        // if token is valid configure Spring Security to manually set
        // authentication
        if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
            UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
            userDetails, null, userDetails.getAuthorities());
            usernamePasswordAuthenticationToken
            .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            // After setting the Authentication in the context, we specify
            // that the current user is authenticated. So it passes the
            // Spring Security Configurations successfully.
            SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        }
    }
    filterChain.doFilter(request, response);
}

}

作为验证令牌的高潮,我们必须提供SpringUserDetails对象,并且我们正在从jwtUserDetailsService获取spring UserDetails对象。因此,此过滤器将调用的每个请求,然后令牌验证将执行,我们必须在每个请求上调用jwtUserDetailsService。我的疑问是在我的jwtUserDetailsService中,我正在添加一些验证并添加用户权限。因此,在每个请求中,以下步骤都会在jwtUserDetailsService中重复。

  1. 使用数据库中的用户名获取用户
  2. 获取用户角色
  3. 从数据库获取用户权限
  4. 为userDetails分配权限

JwtUserDetailsService.java

@Service("jwtUserDetailsService")
@Transactional
public class JwtUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Autowired
private IUserService service;

@Autowired
private MessageSource messages;

@Autowired
private RoleRepository roleRepository;

@Override
public UserDetails loadUserByUsername(String email)
  throws UsernameNotFoundException {

    User user = userRepository.findByEmail(email);
    if (user == null) {
        return new org.springframework.security.core.userdetails.User(
          " ", " ", true, true, true, true, 
          getAuthorities(Arrays.asList(
            roleRepository.findByName("ROLE_USER"))));
    }

    return new org.springframework.security.core.userdetails.User(
      user.getEmail(), user.getPassword(), user.isEnabled(), true, true, 
      true, getAuthorities(user.getRoles()));
}

private Collection<? extends GrantedAuthority> getAuthorities(
  Collection<Role> roles) {

    return getGrantedAuthorities(getPrivileges(roles));
}

private List<String> getPrivileges(Collection<Role> roles) {

    List<String> privileges = new ArrayList<>();
    List<Privilege> collection = new ArrayList<>();
    for (Role role : roles) {
        collection.addAll(role.getPrivileges());
    }
    for (Privilege item : collection) {
        privileges.add(item.getName());
    }
    return privileges;
}

private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
    List<GrantedAuthority> authorities = new ArrayList<>();
    for (String privilege : privileges) {
        authorities.add(new SimpleGrantedAuthority(privilege));
    }
    return authorities;
}
}

因此,对于每个请求,这些查询都在执行。有没有更好的办法?因为一旦我在spring UserDetails对象中添加了用户权限,为什么我们需要在每个请求中再次这样做。或仅具有请求范围的。我在spring mvc上工作过,一旦我们在spring UserDetails对象中添加了权限,它就会一直存在,直到我点击“注销”按钮。这意味着它会一直存在于Spring Security上下文中,直到我们删除它。Spring靴也一样吗?如果我在spring UserDetails对象中添加了一次角色和权限详细信息,为什么我们需要再次添加它?

共有2个答案

翟黎明
2023-03-14

一旦用户登录,他的身份验证就建立了,所以您不需要再次执行db调用,在每个请求中登录后,应该只检查用户的授权,在身份验证期间,您需要验证令牌在每个请求中没有被篡改,而不是通过从db调用加载用户详细信息来创建用户详细信息

=this.jwtUserDetailsService.loadUserByUsername(username);

您还可以在JWT声明中编码用户的用户名和角色,并通过从JWT解析这些声明来创建UserDetails对象。

万俟穆冉
2023-03-14

因此,该过滤器调用的每个请求都会执行令牌验证,我们必须对每个请求调用jwtUserDetailsService。

这不可能是正确的,因为您有一个条件< code > if(securitycontextholder . get context()。getAuthentication()= = null)。

因此,第一次验证令牌时,您查询您的用户详细信息服务,获取所有授权并将它们设置为安全上下文(您已经在这样做了:SecurityContextHolder.getContext(). setAuthentication(usernamePasswordAuthentiationToken);)。

此外,使用JWT auth,您通常甚至不需要访问任何用户详细信息服务,因为理想情况下,所有授权都应该包含在令牌本身中。因此,您唯一需要做的就是验证令牌的签名。

 类似资料:
  • 问题内容: 是否可以使用client_credentials或每个请求的密码授予类型来生成多个有效的访问令牌? 使用以上授权类型生成令牌仅在每个请求当前令牌过期时才提供一个新令牌。 我可以使用密码授予类型来生成刷新令牌,然后生成多个访问令牌,但是这样做会使以前的所有访问令牌无效。 知道如何更改以允许对/ oauth / token端点的每个请求生成访问令牌,并确保以前的所有令牌都不会无效吗? 以下

  • 问题内容: 是否可以使用每个请求的client_credentials或密码授予类型来生成多个有效的访问令牌? 使用以上授权类型生成令牌仅在每个请求当前令牌过期时才提供一个新令牌。 我可以使用密码授予类型来生成刷新令牌,然后生成多个访问令牌,但是这样做会使以前的所有访问令牌无效。 知道如何更改以允许对/ oauth / token端点的每个请求生成访问令牌,并确保以前的所有令牌都不会无效吗? 以下

  • null 实现基于Spring Boot with Spring Security(OAuth2)。我有以下工作2LA流程: RP可以使用和向AS发送访问令牌请求。 AS使用访问令牌响应RP。 RP能够使用所述接入令牌向RS发出授权请求。 RS能够使用AS上的endpoint验证访问令牌。 问题:在上面的步骤1中,我需要对我的AS进行哪些更改,以便它接受基于JWT的访问令牌请求? RP OAuth

  • 问题内容: 我一直在寻找一种方法,可以在每个请求时重新加载我们的Spring Security UserDetails对象,并且无法在任何地方找到示例。 有人知道该怎么做吗? 基本上,我们希望为每个请求重新加载用户权限,因为该用户权限可能会从Web请求更改为Web请求。 例如,一个已登录并随后被授予新权限的用户(并通过电子邮件被通知他们具有新权限),我知道该用户实际获得该新权限的唯一方法是先注销然

  • 问题内容: 因此,我四处阅读,对于拥有CSRF令牌感到非常困惑,但是我应该为每个请求还是每小时生成一个新令牌? 但是,我们最好每小时生成一个令牌,然后我需要两个会话:令牌,到期, 我将如何处理该表格?只需将echo $ _SESSION [‘token’]放在隐藏值表单上,然后在提交时进行比较? 问题答案: 如果您按照表单请求进行操作-那么基本上就可以消除CSRF攻击的发生,并且可以解决另一个常见

  • 我的方法正确吗?在正文中发送凭据>接收响应(成功)>登录成功>将令牌存储在会话存储中并将其设置在头中>会话过期时注销 存储jwt