Как обеспечить безопасность вызовов между различными микросервисами в Spring Cloud

задняя часть Микросервисы Spring Безопасность

1. Предпосылки

В соответствии с микросервисной архитектурой наша система разделена на несколько микросервисов с единой ответственностью в зависимости от бизнеса.

У каждой службы есть собственный набор API для вызовов других служб, так как же обеспечить безопасность?

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

Следует отметить, что здесь речь идет о безопасной аутентификации вызовов между микросервисами, а не об унифицированной аутентификации на официальном сайте API.Требования разные.Унифицированная аутентификация на API-шлюзе связана с бизнесом. здесь, чтобы предотвратить вызов интерфейса другими.

2. Схема

OAUTH2

Spring Cloud может использовать OAUTH2 для реализации единой аутентификации и авторизации нескольких микросервисов.

Получите access_token через централизованную аутентификацию и авторизацию в сервисе OAUTH2.

Этому токену доверяют другие микросервисы, а access_token перенимается при последующих посещениях, тем самым реализуя унифицированную аутентификацию и авторизацию микросервисов.

JWT

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

Кажется, что между этими двумя нет большой разницы, но на самом деле разница есть: OAuth2 — это фреймворк авторизации, JWT — протокол аутентификации.

Независимо от того, какой метод вы используете, не забудьте использовать HTTPS для обеспечения безопасности данных.

3. Какой использовать

Я лично рекомендую использовать JWT, который легкий, простой и подходит для распределенных приложений без сохранения состояния.

Если вы используете OAUTH2, это будет проблематично, много понятий, таких как различные роли, типы аутентификации, клиенты и т. д.

4. Как использовать

Сначала создайте общую службу проверки подлинности, предоставьте операции проверки подлинности и верните токен после успешной проверки подлинности.

@RestController
@RequestMapping(value="/oauth")
public class AuthController {
	
	@Autowired
	private AuthService authService;
	
	@PostMapping("/token")
	public ResponseData auth(@RequestBody AuthQuery query) throws Exception {
		if (StringUtils.isBlank(query.getAccessKey()) || StringUtils.isBlank(query.getSecretKey())) {
			return ResponseData.failByParam("accessKey and secretKey not null");
		}
		
		User user = authService.auth(query);
		if (user == null) {
			return ResponseData.failByParam("认证失败");
		}
		
		JWTUtils jwt = JWTUtils.getInstance();
		return ResponseData.ok(jwt.getToken(user.getId().toString()));
	}

	@GetMapping("/token")
	public ResponseData oauth(AuthQuery query) throws Exception {
		if (StringUtils.isBlank(query.getAccessKey()) || StringUtils.isBlank(query.getSecretKey())) {
			return ResponseData.failByParam("accessKey and secretKey not null");
		}

		User user = authService.auth(query);
		if (user == null) {
			return ResponseData.failByParam("认证失败");
		}

		JWTUtils jwt = JWTUtils.getInstance();
		return ResponseData.ok(jwt.getToken(user.getId().toString()));
	}
	
}

JWT может добавить зависимости, а затем написать класс инструмента. Рекомендуется написать его в глобальном пакете. Должны использоваться все сервисы. Конкретные коды см. в:JWTUtils

адрес на гитхабе:github.com/jwtk/jjwt

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

Основная логика в классе состоит в том, чтобы сгенерировать токен, а затем предоставить метод для проверки того, является ли токен законным и истек ли срок его действия.

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.7.0</version>
</dependency>

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

Так как же мы сейчас используем службу аутентификации для аутентификации?Самый простой способ — использовать для этого фильтр.

Например у меня сейчас есть сервис fangjia-fsh-house-service.Раньше любой мог позвонить на предоставленный мной интерфейс.Теперь я хочу добавить верификацию.Только прошедшие верификацию могут заставить его звонить на мой интерфейс.

Затем добавьте фильтр к fangjia-fsh-house-service, чтобы определить, есть ли у него разрешение на вызов интерфейса.Мы получаем информацию о токене аутентификации из заголовка запроса, не полагаясь на файлы cookie.

Я также рекомендую написать этот фильтр в глобальном проекте, потому что он также используется всеми сервисами, Код можно найти здесь:HttpBasicAuthorizeFilter

Основная логика заключается в том, чтобы получить токен, а затем проверить, является ли он законным или нет, с помощью JWTUtils.

Здесь следует отметить, что ключ дешифрования должен совпадать с ключом шифрования, иначе дешифрование неизбежно завершится ошибкой.

//验证TOKEN
if (!StringUtils.hasText(auth)) {
	PrintWriter print = httpResponse.getWriter();
	print.write(JsonUtils.toJson(ResponseData.fail("非法请求【缺少Authorization信息】", 
                 ResponseCode.NO_AUTH_CODE.getCode())));
	return;
}
JWTUtils.JWTResult jwt = jwtUtils.checkToken(auth);
if (!jwt.isStatus()) {
	PrintWriter print = httpResponse.getWriter();
	print.write(JsonUtils.toJson(ResponseData.fail(jwt.getMsg(), jwt.getCode())));
	return;
}
chain.doFilter(httpRequest, response);

Пока вызывающий объект проходит аутентификацию, передает токен, возвращенный службой аутентификации, а затем вставляет его в заголовок запроса Authorization, он может вызывать другие службы, требующие аутентификации.

Это выглядит идеально, но неудобно в использовании.Его нужно аутентифицировать перед каждым вызовом, а потом вставляется заголовок запроса.Как сделать его универсальным?Не требует внимания конкретных разработчиков,и он прозрачен для пользователей. В следующей части статьи мы продолжаем исследовать, как реализовать удобные вызовы.

Конкретный код может ссылаться на мой github:

GitHub.com/Йинджи Хуан/Да…

Для большего обмена технологиями, пожалуйста, обратите внимание на общедоступную учетную запись WeChat: Yuantiandi

image.png