Spring Cloud中如何保證各個微服務之間調用的安全性

一.背景

微服務架構下,咱們的系統根據業務被拆分紅了多個職責單一的微服務。java

每一個服務都有本身的一套API提供給別的服務調用,那麼如何保證安全性呢?git

不是說你想調用就能夠調用,必定要有認證機制,是咱們內部服務發出的請求,才能夠調用咱們的接口。github

須要注意的是咱們這邊講的是微服務之間調用的安全認證,不是統一的在API官網認證,需求不同,API網關處的統一認證是和業務掛鉤的,咱們這邊是爲了防止接口被別人隨便調用。web

二.方案

OAUTH2

Spring Cloud可使用OAUTH2來實現多個微服務的統一認證受權算法

經過向OAUTH2服務進行集中認證和受權,得到access_tokenspring

而這個token是受其餘微服務信任的,在後續的訪問中都把access_token帶過去,從而實現了微服務的統一認證受權。json

JWT

JWT是一種安全標準。基本思路就是用戶提供用戶名和密碼給認證服務器,服務器驗證用戶提交信息信息的合法性;若是驗證成功,會產生並返回一個Token,用戶可使用這個token訪問服務器上受保護的資源。安全

感受這2種好像沒多大區別呀,實際上是有區別的:OAuth2是一種受權框架 ,JWT是一種認證協議bash

不管使用哪一種方式切記用HTTPS來保證數據的安全性。服務器

三.用哪一種

我我的建議用JWT,輕量級,簡單,適合分佈式無狀態的應用

用OAUTH2的話就麻煩點,各類角色,認證類型,客戶端等等一大堆概念

四.怎麼用

首先呢建立一個通用的認證服務,提供認證操做,認證成功後返回一個token

@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地址:github.com/jwtk/jjwt

JWT提供了不少加密的算法,我這邊用的是RSA,目前是用的一套公鑰以及私鑰,這種作法目前來講是很差的,由於萬一祕鑰泄露了,那就談不上安全了,因此後面會採用配置中心的方式來動態管理祕鑰。

類裏主要邏輯是生成token,而後提供一個檢查token是否合法的方法,以及是否過時等等判斷。

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.7.0</version>
</dependency>
複製代碼

統一認證的服務有了,咱們只須要將認證服務註冊到註冊中心便可給別的服務消費。

那麼咱們如何使用剛剛的認證服務來作認證呢,最簡單的辦法就是用Filter來處理

好比說我如今有一個服務fangjia-fsh-house-service,以前是隨便誰都能調用我提供的接口,如今我想加入驗證,只有驗證經過的纔可讓它調用個人接口

那就在fangjia-fsh-house-service中加一個過濾器來判斷是否有權限調用接口,咱們從請求頭中獲取認證的token信息,不須要依賴Cookie

這個過濾器我也建議寫在全局的項目中,由於也是全部服務都要用,代碼請參考:HttpBasicAuthorizeFilter

主要邏輯就是獲取token而後經過JWTUtils來驗證是否合法,不合法給提示,合法則放過

這邊須要注意的地方是解密的祕鑰必須跟加密時是相同的,否則解密必然失敗,就是bug了

//驗證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);
複製代碼

到這步爲止,只要調用方在認證經過以後,經過認證服務返回的token,而後塞到請求頭Authorization中,就能夠調用其餘須要認證的服務了。

這樣看起來貌似很完美,可是用起來不方便呀,每次調用前都須要去認證,而後塞請求頭,如何作到通用呢,不須要具體的開發人員去關心,對使用者透明,下篇文章,咱們繼續探討如何實現方便的調用。

具體代碼能夠參考個人github:

github.com/yinjihuan/s…

更多技術分享請關注微信公衆號:猿天地

image.png
相關文章
相關標籤/搜索