Создадим классы-обертки для входящего логина и пароля и для исходящего jwt
@Data
public class AuthRequest {
private String username;
private String password;
}
@Data
public class AuthResponse {
private final String jwt;
}
Создадим класс REST-контроллера, добавим метод для создания токена. Логика работы метода следующая:
Производится аутентификация по пришедшему логину и паролю;
Если аутентификация прошла успешно, то получаем UserDetails из БД по username;
Генерируем токен с помощью данных UserDetails;
Возвращаем токен как объект AuthResponse.
@org.springframework.web.bind.annotation.RestController
public class RestController {
@Autowired
private AuthenticationManager authManager;
@Autowired
private JwtUtil jwiUtil;
@Autowired
MyUserDetailsService userDetailsService;
@PostMapping(value = "/auth")
public ResponseEntity<?> createAuthToken(@RequestBody AuthRequest request) throws BadCredentialsException {
try {
authManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
);
} catch (BadCredentialsException ex) {
ex.printStackTrace();
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername());
final String jwt = jwiUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthResponse(jwt));
}
}
Настраиваем конфигурационный класс SecurityConfig. Добавляем bean для AuthenticationManager, а также доступ к URL. Для того, чтобы сервер не генерировал новую сессию, устанавливаем sessionCreationPolicy.
Для начала попробуем сделать запрос с некорректным логином и паролем
На стороне сервера было выброшено исключение
Теперь попробуем ввести логин и пароль существующего пользователя. В ответ мы получим jwt, который будем использовать для последующих запросов.
Аутентификация с помощью jwt
Добавим в класс-контроллер endpoint, для доступа к которому требуется аутентификация. Входной аргумент типа Principal хранит информацию об аутентифицированном пользователе. Получаем username пользователя и извлекаем из БД информацию о нем.
Для того чтобы провести аутентификацию с помощью jwt, создадим отдельный фильтр, который потом встроим в filter chain, которая используется в Spring Security.
Логика работы нашего фильтра следующая:
Считываем заголовок GET-запроса с ключом "Authorization";
Проверяем, есть ли в начале заголовка слово "Bearer ";
Извлекаем из jwt значение username;
Извлекаем из БД пользователя по полученному username и осуществляем валидацию токена;
Если jwt провел валидацию, то аутентифицируем пользователя;
Отредактируем конфигурационный класс SecurityConfig, добавим использование фильтра в цепочке фильтров (мы указываем, что наш фильтр должен быть встроен в цепочку ДО фильтра UsernamePasswordAuthenticationFilter, который является стандартным фильтром для аутентификации).