Spring Security с токеном JWT загружает объект Spring UserDetails при каждом запросе - PullRequest
1 голос
/ 20 сентября 2019

Я изучаю пружинную безопасность с токеном JWT и загрузкой пружины.Я реализовал это правильно, и он работает нормально.Но у меня есть одно сомнение в том, как работает JwtRequestFilter.Я просмотрел несколько веб-сайтов, чтобы понять, как работает весенняя защита с помощью весенней загрузки, и нашел то же самое.Итак, позвольте мне перейти к основному сомнению.Я добавляю файл 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);
}

}

Как выделено для проверки токена, мы должны предоставить объект Spring UserDetails и получить объект Spring UserDetails.от 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;
}
}

Таким образом, при каждом запросе эти запросы выполняются.Есть ли лучший способ сделать это?Потому что как только я весной добавляю пользовательские привилегии для объекта UserDetails, нам нужно делать это снова при каждом запросе.Или те, которые имеют объем запроса только.Я работал над Spring mvc, и как только мы добавим привилегии в весной объект UserDetails, он будет там, пока я не нажму выход из системы, значит, он будет там в контексте безопасности Spring, пока мы не удалим его.Будет ли это так же в весенней загрузке?Если я добавляю детали роли и привилегий один раз весной, объект UserDetails, зачем нам его снова добавлять?

Ответы [ 2 ]

0 голосов
/ 22 сентября 2019

После того, как пользователь вошел в систему с использованием своей аутентификации, поэтому вам не нужно делать db call снова, после входа в систему при каждом запросе пользователь должен проверяться на авторизацию только с ролями, установленными в токене во время аутентификации, вам нужнодля проверки токена не вмешиваются в каждый запрос, вместо создания пользовательских деталей путем загрузки сведений о пользователе из вызова db

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

вы также можете кодироватьимя пользователя и роли пользователя в утверждениях JWT и создание объекта UserDetails путем анализа этих утверждений из JWT.

0 голосов
/ 20 сентября 2019

Таким образом, каждый запрос, который вызовет этот фильтр, будет выполнять проверку токена, и мы должны вызывать jwtUserDetailsService для каждого запроса.

Это не может быть правильно, так как у вас есть условие if (SecurityContextHolder.getContext().getAuthentication() == null).

Таким образом, когда токен был впервые проверен, вы запрашиваете службу пользовательских данных, извлекаете все разрешения и устанавливаете их в контексте безопасности (вы уже делаете это: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);).

Кроме того, с помощью аутентификации JWT вам обычно даже не требуется доступ к какой-либо службе сведений о пользователе, поскольку в идеале все гранты должны содержаться в самом токене.Поэтому единственное, что вам нужно сделать, это проверить подпись токена.

...