Как проверить JWT с внешнего интерфейса с помощью утилиты JWT на серверной части? (Реакция Пружинный ботинок)

#reactjs #spring-boot #spring-security #jwt

Вопрос:

Итак, у меня есть компонент React, который я хочу загрузить только в том случае, если JWT он действителен в локальном хранилище (это означает, что срок действия токена не истек и это правильный токен). В настоящее время я могу проверить только одну из этих вещей, если срок ее действия истек или нет.

Это componentDidMount функция в этом компоненте:

 componentDidMount() {
    this._isMounted = true;

    // validate token
    const token = localStorage.getItem("token");
    AuthService.validateToken()
      .then((res) => {
        console.log("step 1");
        if (res == undefined || res == null || !token) {
          this.props.history.push("/login");
        }
      })
      .then(() => {
        console.log("step 2");

        TicketService.getTickets().then((res) => {
          if (this._isMounted) {
            this.setState({ tickets: res.data });
          }
        });
      });
  }
 

AuthService.validateToken() Выглядит это примерно так:

   // IS token expired?
  validateToken() {
    console.log(" about to try validating");
    return AxiosInstance.get("authenticate")
      .then("did it")
      .catch((err) => console.log(err));
  }
}
 

И AxiosInstance это как раз то, что нужно. Я также поделюсь кодом со своим экземпляром Axios:

 const AxiosInstance = axios.create({
  baseURL: API_BASE_URL,
  headers: { Authorization: `Bearer ${getTokenFromLocalStorage()}` },
});

function getTokenFromLocalStorage() {
  const token = localStorage.getItem("token");
  console.log("the token is -> "   token);
  if (token === null) {
    return undefined;
  }
  return token;
}
 

Наконец, конечная точка выглядит так:

     // returns the username
    @GetMapping("/authenticate")
    public ResponseEntity<?> validateHeaderToken() throws Exception {

        String username = null;
        try {
            System.out.println("Attempting to validate token");
            String token = request.getHeader("Authorization");
            token = token.split("Bearer ")[1];
            username = jwtTokenUtil.extractUsername(token);

            // TODO : this should eventually use the .validateToken method once I figure out
            // how to pass in UserDetails
            if (!jwtTokenUtil.isTokenExpired(token)) {
                return ResponseEntity.ok(username);
            } else {
                throw new Exception("Expired or Invalid Token");
            }

        } catch (Exception e) {
            return ResponseEntity.status(400).build();
        }

    }
 

So as you can see, I am only checking if the token is expired in the function above ^^. I want to use my JWT Utility to use the validateToken that already exists. This is my JWT Utility:

   public String extractUsername(String token) {
        // we set the username as the subject when we created the token so we can
        // extract the username from the claim like this
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        // extract the claim that is attached to the token, if any
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    public Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        // create token using the username as subject
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        // Expiration is 10 hours from creation
        // subject is username
        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis()   1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) amp;amp; !isTokenExpired(token));
    }
 

Итак, все полезные функции есть в моей утилите JWT, но validateToken() требуется UserDetails И token . Где и как я могу получить UserDetails передачу в validateToken функцию??

ИЗМЕНИТЬ: данные пользователя уже загружены, когда я обновляю свою страницу, я вижу данные пользователя, напечатанные для тех, кто вошел в систему, но не уверен, как получить к ним доступ из других мест.

ИЗМЕНИТЬ: MyUserDetailsService выглядит так:

 @Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private EmployeeServices employeeServices;

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

        System.out.println("S is -> "   s);
        Employee employee = employeeServices.getEmployeeByUsername(s);
        System.out.println(employee);
        // if (employee == null) {
        // throw new UsernameNotFoundException("Username was not found. Could not log
        // in.");
        // }

        return new User(employee.getUsername(), employee.getPassword(), new ArrayList<>());
    }
}
 

И когда я обновляю свою страницу, это вызывается каждый раз. Я могу видеть S is -> распечатку на своем терминале при каждом обновлении страницы. Это то место, где я должен хранить данные пользователя в файле cookie?

Комментарии:

1. Spring имеет встроенную поддержку jwt в течение 3 лет, почему вы создаете индивидуальную безопасность? Изготовленная на заказ самодельная безопасность-плохая практика. Зачем использовать фреймворк безопасности, если вы собираетесь игнорировать то, что предлагает фреймворк. Кроме того, хранение JWT в локальном хранилище является очень плохой практикой. Пожалуйста, ознакомьтесь с OWASP

2. Я новичок в этом и следовал учебнику. Теперь я планирую преобразовать вещи в лучшую практику. Не могли бы вы, пожалуйста, объяснить или, может быть, поделиться ссылкой, которую я могу просмотреть, которая объяснит, какие части необходимо изменить, чтобы использовать встроенную систему безопасности JWT?

3. Воспользуйтесь Google, найдите документацию по безопасности spring и прочитайте главу JWT.

4. если вы следуете учебнику, вы должны опубликовать его в своем вопросе. В этом учебнике вы узнаете о плохих методах, и я предлагаю вам прочитать официальную документацию, если вы хотите изучить безопасность spring и не доверяете плохо написанным учебникам, которые предоставляют неверную информацию.

Ответ №1:

Похоже, что вы хотите сравнить данные в файле cookie с данными в UserDetails нем .

Предположительно, у вас есть UserDetails сохраненный в файле cookie сеанса или, по крайней мере, идентификатор пользователя. Не зная точно, как формируются ваши сеансовые файлы cookie, я не могу дать точный ответ, но вот в чем суть.

Внутри вашего validateHeaderToken метода, где у вас есть доступ к объекту входящего запроса, вы должны либо:

  1. Извлеките данные пользователя из файлов cookie во входящем запросе или
  2. Извлеките идентификатор из файлов cookie во входящем запросе и найдите соответствующие данные пользователя в своей базе данных.

затем

  1. Передайте данные пользователя вместе с токеном своему validateToken методу.

Статья MDN о файлах cookie в заголовках: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie

Комментарии:

1. Я распечатал свое печенье, и оно было пустым. У меня их не было. Я обновил свой вопрос, не могли бы вы, пожалуйста, просмотреть его?

2. Ну, каким-то образом ваши данные пользователя видны на странице. Как они туда добираются? Мне не совсем понятно, почему вы хотите сравнить то, что находится в действительном токене, с именем пользователя, вошедшего в систему, поскольку это противоречит цели токена на предъявителя, но вы сказали, что хотите их сравнить, поэтому вам нужно каким-то образом добавить данные пользователя в запрос аутентификации.