Рубрики
Без рубрики

Давайте создадим приложение Java с полным стеком Spring boot & React: Серверный REST API / 3 – Защита REST API с помощью Spring Security и JWT (b)

Приложение Java Full Stack Spring boot & React: Серверный REST API / 2 – Защита REST API с помощью Spring Security и JWT (b)

Автор оригинала: Koffi KOMLAN.

Привет,

Мы добавили некоторые функции безопасности в наш REST API в предыдущем посте благодаря Spring Security и JWT . В этом уроке мы проверим токен и защитим конечные точки API.

Обзор

Вот что мы будем делать в этом посте:

  • Проверка токена JWT
  • Как предоставить/запретить доступ к методу хранилища данных Spring для отдыха

Проверка токена JWT

Чтобы перехватить и проверить токен, поступающий от клиента, мы должны создать класс фильтра запросов, расширяющий веб-фильтр Spring OncePerRequestFilter класс. Для любого входящего запроса этот класс фильтра будет выполнен. Он проверяет, имеет ли запрос действительный токен JWT. Если у него есть действительный токен JWT, он устанавливает аутентификацию в контексте, чтобы указать, что текущий пользователь аутентифицирован.

Создайте класс, AuthRequestFilter.java в безопасность.фильтр пакет.

package com.codeurinfo.easytransapi.security.filter;

import com.codeurinfo.easytransapi.security.UserDetailsImpl;
import com.codeurinfo.easytransapi.security.UserDetailsServiceImpl;
import com.codeurinfo.easytransapi.security.util.JwtUtil;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

@Component
public class AuthRequestFilter extends OncePerRequestFilter {

  @Autowired
  private UserDetailsServiceImpl userDetailsService;

  @Autowired
  private JwtUtil jwtUtil;

  @Override
  protected void doFilterInternal(
    HttpServletRequest request,
    HttpServletResponse response,
    FilterChain chain
  )
    throws ServletException, IOException {
    final String authorizationHeader = request.getHeader("Authorization");

    String username = null;
    String jwt = null;

    if (
      authorizationHeader != null && authorizationHeader.startsWith("Bearer ")
    ) {
      // get the jwt token
      jwt = authorizationHeader.substring(7);// authorizationHeader is like "Bearer jwttoken", so 7 caracters before jwttoken
      username = jwtUtil.extractUsername(jwt);// get username for the given token
    }

    if (
      username != null &&
      SecurityContextHolder.getContext().getAuthentication() == null
    ) {
      UserDetailsImpl userDetails =
        (UserDetailsImpl) this.userDetailsService.loadUserByUsername(username);

      if (jwtUtil.validateToken(jwt, userDetails)) {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
          userDetails,
          null,
          userDetails.getAuthorities()
        );
        usernamePasswordAuthenticationToken.setDetails(
          new WebAuthenticationDetailsSource().buildDetails(request)
        );
        SecurityContextHolder
          .getContext()
          .setAuthentication(usernamePasswordAuthenticationToken);
      }
    }
    chain.doFilter(request, response);
  }
}

Затем давайте добавим этот фильтр в SecurityConfig.java класс в сконфигурируйте метод, подобный этому:

@Override
  protected void configure(HttpSecurity http) throws Exception {
    http
      .csrf()
      .disable()
      .authorizeRequests()
      .antMatchers("/api/auth/**").permitAll() // Grant access to every /api/auth based url
      .anyRequest().authenticated() // All other based url wil be allowed if authenticated
      .and()
      .sessionManagement()
      .sessionCreationPolicy(SessionCreationPolicy.STATELESS);//Force server not to never create an HttpSession
    http.addFilterBefore(
      authRequestFilter,
      UsernamePasswordAuthenticationFilter.class
    );
  }

Напоминание: Вы должны сначала объявить Фильтр запроса аутентификации в качестве переменной в SecurityConfig.java класс

@Autowired
  private AuthRequestFilter authRequestFilter;

На данный момент наш REST API почти защищен. Давайте запустим и протестируем его в Postman.

1 . Сделайте простой запрос на получение

http:
Сделайте простой запрос на получение

Как вы можете видеть, у нас ошибка отказа в доступе.

2 . Сделайте запрос на запись для входа в систему

http:

Не забудьте использовать те же учетные данные, которые вы настроили для пользователей при предварительной загрузке DatabaseLoader.java Если указано действительное имя пользователя/пароль, возвращается токен JWT.

Если указано действительное имя пользователя/пароль, возвращается токен JWT.

3 . Попробуйте еще раз выполнить простой запрос на получение

http:
  • Перед выполнением запроса GET в Postman скопируйте токен JWT, возвращенный на шаге 2
  • Откройте вкладку “Заголовок”, добавьте ключ : Авторизация , введите значение :
  • :

С этого момента вы должны добавить ключ авторизации и значение на вкладке Заголовок перед выполнением любого другого запроса, кроме входа в систему

4 . Сделайте запрос на публикацию, чтобы сохранить маршрут

http:
  • Откройте вкладку Заголовок и ключ авторизации и значение, как описано в шаге 3
  • Откройте вкладку Тело, чтобы добавить свои данные полезной нагрузки
{ 
  "name": "Rex-Adidogome", 
    "start": "Grand marche - Rex", 
    "terminus": "Adidogome"
}
  • Затем выполните, чтобы получить результат, как показано ниже:
Затем выполните, чтобы получить результат, как показано ниже:

Поздравляю! Вы настроили Spring Security и JWT для защиты API REST, чтобы только прошедшие проверку пользователи могли получать доступ к конечным точкам для запросов.

Но… в реальном мире каждый вошедший в систему пользователь должен иметь возможность доступа к некоторым конечным точкам, в то время как другие-нет. Давайте укрепим нашу безопасность API.

Безопасность пружины: безопасность на уровне метода

Помните, мы добавили @Enableglobalmethod безопасность y(защищено,) аннотация к SecurityConfig.java класс. Эта аннотация обеспечивает безопасность на уровне метода.

В RouteRepository.java класс, предположим, мы хотим, чтобы только пользователи ROLE_ADMIN имели возможность доступа к методу findRoutesByName .

Добавить @Предварительная авторизация (“hasRole(‘ROLE_ADMIN’)”) аннотация к этому методу для предоставления доступа пользователям ROLE_ADMIN. Обновленный код RouteRepository.java:

package com.codeurinfo.easytransapi.repository; import com.codeurinfo.easytransapi.model.Route;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.data.rest.core.annotation.RestResource;
import org.springframework.security.access.prepost.PreAuthorize; @RepositoryRestResource
public interface RouteRepository extends JpaRepository { @PreAuthorize("hasRole('ROLE_ADMIN')") @RestResource(path = "routesByName") Optional findRoutesByName(@Param("name") String name);
}

Давайте еще раз протестируем API.

1 . Убедитесь, что вы предварительно загрузили двух разных пользователей, одного с именем ROLE_ADMIN, а другого нет.

2 . Сделайте запрос POST для входа в систему с учетными данными администратора, затем скопируйте маркер

3 . Попробуйте отправить запрос на сохранение маршрута, получить запрос на поиск маршрута и т. Д. Используя маркер авторизации на вкладке Заголовок, вы должны получить успешные ответы.

Попробуйте отправить запрос на сохранение маршрута, получить запрос на поиск маршрута и т. Д. Используя маркер авторизации на вкладке Заголовок, вы должны получить успешные ответы.

4 . Теперь войдите в систему с помощью учетных данных простого пользователя (у которого не было роли ROLE_ADMIN) и скопируйте токен

5 . Используя маркер авторизации на вкладке Заголовок, все методы POST и GET должны быть успешными, за исключением запроса GET для маршрута поиска!

http://localhost:9000/api/routes/search/routesByName?name=Rex-Adidogome
Используя маркер авторизации на вкладке Заголовок, все методы POST и GET должны быть успешными, за исключением запроса GET для маршрута поиска!

Мы получили запрещенную ошибку, в которой говорится, что вошедшему в систему пользователю не разрешается запрашивать этот метод.

В следующем посте мы добавим новые функции в наше приложение Easy Trans. Надеюсь, вы узнали что-то новое. Если это так, не забудьте нажать кнопку “Мне нравится” и подписаться на этот блог, чтобы быть в курсе новых сообщений.

Оригинал: “https://www.codementor.io/@koffikomlan/let-s-build-java-full-stack-spring-boot-react-app-backend-rest-api-3-securing-the-rest-api-with-spring-security-jwt-b-1k448kc13g”