源码:Securing your Microservices
一、使用Spring和OAuth2来保护单个端点
1.建立验证服务
构建依赖项:
2.使用OAuth2.0服务注册客户端应用程序
小笔记:验证与授权,验证是看他是谁,授权是可以让他干些什么,所以授权之前肯定是要先验证的。
3.配置用户
上面两个返回的bean,就是在这里注入的:
4.验证用户
先把配置服务搞一下。
新建User表。
启动顺序:Eureka->配置服务->验证服务(端口号设一下:8901)
需要注意的几点:
返回的结果是:
{
"access_token": "96040f35-8b10-491d-a82b-1c1a835ba632",
"token_type": "bearer",
"refresh_token": "f933f8b1-cace-4a1a-bc75-ff7b58b37d3d",
"expires_in": 43199,
"scope": "webclient"
}
然后拿着token去验证这个用户:
返回的结果:
{
"user": {
"password": null,
"username": "john.carnell",
"authorities": [
{
"authority": "ROLE_USER"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true
},
"authorities": [
"ROLE_USER"
]
}
二、使用Oauth2保护组织服务
1.POM依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
2.配置服务以指向OAuth2验证服务
然后将引导类设为受保护资源:
它会强制执行一个过滤器,该过滤器就是来检查调用的HTTP首部中是否存在OAuth2访问令牌,然后调用刚才填写的回调地址验证它是不是有效的。得知它是有效的之后,还会应用定义好的任何访问控制规则,以控制什么人可以访问什么服务。
3.定义谁可以访问服务
访问规则可以按照标准设置的非常细,这里只讨论两个。
- 只有通过验证的用户才能访问服务URL
- 只有具有特定角色的用户才能访问服务URL
(1)通过验证用户保护服务
组织服务定为8085。先运行一下,如果访问的时候不带令牌的话,会直接收到401错误。
然后我们把令牌带上,发现就可以了。
把Zuul开起来再试试:
没毛病。
(2)通过特定角色保护服务
接下来会锁定组织服务的DELETE调用,只让有ADMIN访问权限的人使用。注意这条以及下面的hasRole(“ADMIN”):
最后两句还是定义了其他端点都需要被授权。
如果我们用普通用户登录的令牌去删除的话,就会收到403
而如果用admin的话,则收到204成功。
数据库里看一下,果然被删了。
4.传播OAuth2访问令牌
像上一章一样,微服务是在调用微服务的,如果我访问了微服务A,带着令牌,微服务A自己又调用了微服务B,如果B也受资源保护的话,那么这个令牌也是要自动带上的。这一小节就是为了解决这个问题。
在开启了Zuul网关的情况下,实现这些要做两件事:
(1)修改Zuul服务网关
在配置服务里对Zuul做以下配置:
zuul.sensitiveHeaders: Cookie,Set-Cookie
这个配置是黑名单的意思,没在这个上面的,都会传播到下一层服务,Authorition不再这个上面,所以会传播。
(2)许可证服务配置以及更改
我们就俩主要服务,而且传播都是许可证到组织的,这个很好理解。
基本的授权就不说了,直接说怎么传播令牌:
三、JSON Web Token 与OAuth2
OAuth2作为验证框架,没有标准,比较搞笑,JWT是后来矫正OAuth2的标准,JWT具有以下特点:
- 小巧-- base64,直接http传递
- 密码签名–JWT令牌由颁发它的验证服务器签名,可以保证其没有被篡改。
- 自包含–由于JWT令牌是密码签名的,所以接受该服务的微服务可以保证令牌的内容是有效的(验证就是为了验证其是否有效嘛~),因此,不需要调用验证服务就可以来确认令牌的内容
- 可扩展–当验证服务生成一个令牌时,它可以在加密之前放入额外的信息。
Spring cloud为JWT提供了开箱即用的支持,就是验证服务和受验证服务的配置方式不同而已。
我们拉取JWT版本的代码:JWT版,记得改配置文件:
1.修改验证服务以颁发JWT令牌
pom依赖:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
然后增加个配置类:
@Configuration
public class JWTTokenStoreConfig {
@Autowired
private ServiceConfig serviceConfig;
@Bean
public TokenStore tokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
@Primary //告訴Spring這個是首選的,
public DefaultTokenServices tokenServices(){
DefaultTokenServices defaultTokenServices=new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
@Bean //在JWT和OAuth2服務器之間充當翻譯
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter converter=new JwtAccessTokenConverter();
converter.setSigningKey(serviceConfig.getJwtSigningKey());//定义用于签署令牌的签名密钥
return converter;
}
@Bean
public TokenEnhancer jwtTokenEnhancer(){
return new JWTTokenEnhancer();
}
}
小笔记:这里是使用的对称加密,密钥在配置服务里配置:
signing.key: "345345fsdfsf5345"
刚才定义了如何创建和签名JWT令牌。现在要将它挂钩到OAuth2服务中。
刚才定义的内容,将在这里注入:
验证:运行Eureka->配置服务->验证服务
返回的token已经是Base64了,找个在线解密的网站解密一下:在线操作
2.在微服务中使用JWT
到目前为止,已经有了验证服务。接下来就是配置许可证和组织服务以使用JWT。要做两件事:
(1)将Spring-security-jwt依赖项添加到pom文件。
(2)创建一个JWTTokenStoreConfig类。几乎和之前的相同。这个没必要再写一遍,只需要把传播搞定就行了。
3.扩展JWT令牌
注意刚才的解密,发现有些字段不是JWT令牌字段,这些就是扩展进来的。
通过向验证服务添加一个Spring OAuth2令牌增强器类,可以很轻松的扩展JWT令牌。
需要做的最后一件事就是告诉OAuth2服务使用这个类,首先为这个类公开一个Bean。
公开之后就受IOC管理了,就可以自动装配进相应的地方了:
4.从JWT令牌中解析自定义字段
这里从Zuul网关开始说起:
pom增加依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
然后就可以使用这个库解密了:
四、关于微服务安全的总结
(1)对所有服务通信使用HTTPS。
(2)所有服务调用都应通过API网关。
(3)将服务划分到公共API和私有API。
这个值得讨论,很多人觉得公网下加密,内网下相互调用就不用加密,但是一旦网络被攻破,那就比较惨了,所以还是两个地方都加密的好,虽然开发的时候麻烦了点儿,但是没了后顾之忧。
(4)通过*不需要的网络端口来限制微服务的攻击面。