微服務架構下,咱們的系統根據業務被拆分紅了多個職責單一的微服務。java
每一個服務都有本身的一套API提供給別的服務調用,那麼如何保證安全性呢?git
不是說你想調用就能夠調用,必定要有認證機制,是咱們內部服務發出的請求,才能夠調用咱們的接口。github
須要注意的是咱們這邊講的是微服務之間調用的安全認證,不是統一的在API官網認證,需求不同,API網關處的統一認證是和業務掛鉤的,咱們這邊是爲了防止接口被別人隨便調用。web
Spring Cloud可使用OAUTH2來實現多個微服務的統一認證受權算法
經過向OAUTH2服務進行集中認證和受權,得到access_tokenspring
而這個token是受其餘微服務信任的,在後續的訪問中都把access_token帶過去,從而實現了微服務的統一認證受權。json
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:
更多技術分享請關注微信公衆號:猿天地