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
相关文章
相关标签/搜索