簡體   English   中英

Spring 使用 JWT 令牌的安全性正在加載 spring 用戶詳細信息 ZA8CFDE6331BD59EB2AC966F8911C4B on every request

[英]Spring Security with JWT token is loading spring UserDetails object on every request

我正在使用 JWT 令牌和 spring 引導來學習 spring 安全性。 我已經正確實施它並且工作正常。 但我對 JwtRequestFilter 的工作原理有一個疑問。 我瀏覽了幾個網站來了解 spring 引導的 spring 安全性,並發現同樣的事情。 所以讓我 go 主要懷疑。 我在下面添加 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);
}

}

As highlighed to validate token we have to provide spring UserDetails object and we are getting spring UserDetails object from jwtUserDetailsService. 所以這個過濾器將調用的每個請求都會執行令牌驗證,我們必須在每個請求上調用 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 object 中添加用戶權限,為什么我們需要對每個請求再次執行此操作。 或者那些只有 scope 的請求。 I have worked on spring mvc and once we add privileges in spring UserDetails object it will be there until I am hitting logout means It will be there in spring security context until we remove it. 在 spring 引導中是否相同? 如果我在 spring UserDetails object 中添加一次角色和權限詳細信息,為什么我們需要再次添加它?

所以這個過濾器將調用的每個請求都會執行令牌驗證,我們必須在每個請求上調用 jwtUserDetailsService。

這不可能是正確的,因為您有一個條件if (SecurityContextHolder.getContext().getAuthentication() == null)

所以第一次驗證令牌時,您查詢您的用戶詳細信息服務,獲取所有授權並將它們設置為安全上下文(您已經在這樣做: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); )。

此外,使用 JWT 身份驗證,您通常甚至不需要訪問任何用戶詳細信息服務,因為理想情況下,所有授權都應包含在令牌本身中。 因此,您唯一需要做的就是驗證令牌的簽名。

一旦用戶登錄他的身份驗證建立,因此您不需要再次進行 db 調用,在每個請求用戶登錄后,應僅使用身份驗證期間在令牌中設置的角色檢查授權,您需要驗證令牌不會在每個請求中被篡改,而不是通過從 db 調用加載用戶詳細信息來創建用戶詳細信息

UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);

您還可以在 JWT 聲明中對用戶的用戶名和角色進行編碼,並通過解析來自 JWT 的聲明來創建 UserDetails object。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM