文档章节

微服务架构下的安全认证与鉴权

Java大蜗牛
 Java大蜗牛
发布于 07/21 20:54
字数 2220
阅读 15
收藏 0
  1. 微服务下常见认证解决方案;
  2. OAuth认证与授权;
  3. JWT认证介绍;
  4. Spring Cloud的OAuth2实现;

单体应用转变为分布式应用

单体应用转变为分布式应用在架构方式上存在较大区别,单体应用下的简单架构方式如下:

分布式应用的安全认证相对更加复杂,既要考虑它的安全性,一致性,还要考虑它的性能问题,开发成本等问题。分布式应用下的简单架构方式如下:

常见的授权认证解决方案

  1. 单点登录SSO

    统一的认证服务器(CAS)

    缺点:每一个用户请求都要经过认证服务器,容易形成瓶颈

  2. Session共享

    Session共享存储,共享用户信息(Spring Session)

    缺点:不同平台、不同架构中实现难度较高

  3. Token认证

    认证服务器颁发包含用户身份信息的Token,资源服务器验证合法性(OAuth)

    缺点:资源服务器想注销用户Token较困难

  4. Token+Gateway

    所有的请求通过网关,可在网关中完成认证或注销Token

    缺点:Gateway必须保证高可用

OAuth认证介绍

OAuth是一个开放的协议,提供了标准授权方式去访问受保护的资源。

常见的使用者如:Twitter、微信、QQ、GitHub

OAuth角色定义

  • 客户端:发起请求的应用
  • 资源拥有者: 用户
  • 资源服务器: 资源所在的服务器
  • 授权服务器:提供Token相关操作的服务器

OAuth认证流程

客户端的授权模式

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

授权码模式

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

授权码模式是功能最完整、流程最严密的授权模式。

简化模式

简化模式跳过了”授权码”这个步骤,使用较少

密码模式

(A)用户向客户端提供用户名和密码。

(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。

(C)认证服务器确认无误后,向客户端提供访问令牌。

密码模式通常使用在所有的服务都是由同一家公司提供的情况下。

请求示例:

http://localhost:9061/oauth/token?username=user_1&password=123456&grant_type=password&scope=all&client_id=client_2&client_secret=123456

响应示例:

{
   "access_token": "8c95f119-2e2a-45c9-a70a-660e55205d6f",
   "token_type": "bearer",
   "refresh_token": "8033f033-488b-42d2-8bed-4623ab5f66a4",
   "expires_in": 43199,
   "scope": "all"
}

客户端模式

(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。

(B)认证服务器确认无误后,向客户端提供访问令牌。

为了保证安全,该模式一般会配合数字证书,需要使用客户端证书进行认证。

请求示例:

http://localhost:9061/oauth/token?grant_type=client_credentials&scope=all&client_id=client_1&client_secret=123456

响应示例:

{
   "access_token": "47bdce37-d570-439b-9d63-48ca736dbd8c",
   "token_type": "bearer",
   "expires_in": 43199,
   "scope": "all"
}

JWT协议介绍

JSON Web Token(JWT)是一种用于传递认证信息的基于JSON的开放标准。

JWT可单独用于请求认证,也可以与其它认证协议配合使用,如OAuth2.0。

JWT认证流程

A)客户端传入用户密码请求JWT

B)认证中心验证通过后创建JWT并返回

C)客户端使用JWT访问资源服务器

D)资源服务器验证JWT,通过后返回资源内容

JWT结构

JWT 由三段信息构成,每一段内容都是一个 JSON 对象,将每一段 JSON 对象采用 BASE64 编码后,中间用 . 连接:

  • 第一段为头部(Header): 基本信息,例如其类型以及签名所用的算法等
  • 第二段为载荷(Payload) :存放各种声明信息
  • 第三段为签名(Signature): header 和 payload加密结果,可使用对称加密或非对称加密

客户端模式请求示例:

http://localhost:9062/oauth/token?grant_type=client_credentials&scope=all&client_id=client_1&client_secret=123456

生成的JWT:

{
   "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRlcm1jbG91ZHMiXSwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTUyMDg4ODYzOSwianRpIjoiZGJiMzk4MTAtYjVlOC00MjAwLTlkNzItZDBlMmYxYmMxMTM1IiwiY2xpZW50X2lkIjoiY2xpZW50XzEifQ.BdUvz5gI5GLsh-HmERYuCPcb0Z_94OkJBW8meYHIRQ4",
   "token_type": "bearer",
   "expires_in": 43199,
   "scope": "all",
   "jti": "dbb39810-b5e8-4200-9d72-d0e2f1bc1135"
}

密码模式请求示例:

http://localhost:9062/oauth/token?username=user_1&password=123456&grant_type=password&scope=all&client_id=client_2&client_secret=123456

生成JWT:

{
   "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRlcm1jbG91ZHMiXSwidXNlcl9uYW1lIjoidXNlcl8xIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTUyMDg4ODUxNCwiYXV0aG9yaXRpZXMiOlsiVFdUOlBFS1k3NzcwMjMsRVhUUklQOjEzNTg5OTkwMCJdLCJqdGkiOiJjY2E0YWFlYy1hNmVlLTQ4ZDktODBkYS1jYzgxN2E2ODM4NDQiLCJjbGllbnRfaWQiOiJjbGllbnRfMiJ9.lvVfQ5H23gIU_i2cMItdpVfdVkGUL7zI1Tbs59wZa74",
   "token_type": "bearer",
   "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiZXRlcm1jbG91ZHMiXSwidXNlcl9uYW1lIjoidXNlcl8xIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImNjYTRhYWVjLWE2ZWUtNDhkOS04MGRhLWNjODE3YTY4Mzg0NCIsImV4cCI6MTUyMTQ1MDExNCwiYXV0aG9yaXRpZXMiOlsiVFdUOlBFS1k3NzcwMjMsRVhUUklQOjEzNTg5OTkwMCJdLCJqdGkiOiJlMzcxMjdhZC0yMzRiLTRkMDctODBiNC1lYzgzMmY2MzQ5YzciLCJjbGllbnRfaWQiOiJjbGllbnRfMiJ9.BswJy_3hyv_XqtM10sex1XRteJcDoqAwvogL3agt1KY",
   "expires_in": 43199,
   "scope": "all",
   "jti": "cca4aaec-a6ee-48d9-80da-cc817a683844"
}

JWT优点

定义了标准格式,传递与解析简单

JWT缺点

JWT更多的是一种开放TOKEN传递协议,对于多种业务场景(授权模式)的实现支持只停留在理伦层面。

OAuth与JWT结合

OAuth方案中,获得了认证服务器颁发的Token后,资源服务器如何验证与鉴权呢?有几种方案:

  1. 资源服务器请求认证服务器验证Token

    缺点:对认证服务器依赖过高,容易出现认证服务器性能问题

  2. 共享Token存储信息,如Redis共享

    缺点:无法解决外部应用验证问题,如使用github认证服务器

  3. 资源服务器自验证,如JWT协议

    缺点:Token注销是个难点,可以与Gateway结合控制。

Spring Cloud集成OAuth2.0

实际由spring-security-oauth2提供实现。实现较为复杂。

Shiro实现OAuth2.0相对简单,但与spring cloud集成不是很好。

认证服务器主要需要配置:

  • AuthorizationServer配置

资源服务器主要需要配置:

  • ResourceServer配置

AuthorizationServer配置文件

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
  //配置安全策略
  }

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  //配置客户端认证信息
  }

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  //配置token的数据源、自定义的tokenServices等信息
  }

配置示例:

/**
* AuthorizationServerConfig.java
* 功能:授权服务器配置
* @author kavenran 2018年2月23日
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 @Autowired
 private AuthenticationManager authenticationManager;

 @Autowired
 private DataSource dataSource;

 //自定义用户服务覆盖默认User
 @Autowired
 private UserDetailsServiceImpl userDetailsService;

 //token存储数据库
//	@Bean
//	public JdbcTokenStore jdbcTokenStore() {
//		return new JdbcTokenStore(dataSource);
//	}

 //JWT存储Token
 @Bean
   public TokenStore jwtTokenStore() {
       return new JwtTokenStore(jwtAccessTokenConverter());
   }

 //配置OAuth2的客户端相关信息,client信息包括:clientId、secret、scope、authorizedGrantTypes
 @Override
 public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
   clients.withClientDetails(new JdbcClientDetailsService(dataSource));//基于oauth_client_token表的操作
 }

 //配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth2RequestFactory
 @Override
 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
   endpoints.tokenStore(jwtTokenStore())
       .accessTokenConverter(jwtAccessTokenConverter())
       .tokenServices(defaultTokenServices())
       .authenticationManager(authenticationManager);
 }

 //Token校验方式采用JWT协议
 @Bean
 public JwtAccessTokenConverter jwtAccessTokenConverter() {
   JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //CustomTokenEnhancer() //自定义Token信息
   converter.setSigningKey("123");
   return converter;
 }

 //按默认规则生成Token
 @Primary
 @Bean
 public DefaultTokenServices defaultTokenServices() {
   DefaultTokenServices tokenServices = new DefaultTokenServices();
   tokenServices.setTokenStore(jwtTokenStore());
   tokenServices.setSupportRefreshToken(true);
   tokenServices.setClientDetailsService(new JdbcClientDetailsService(dataSource));
   tokenServices.setAccessTokenValiditySeconds(60 * 60 * 12); // token有效期自定义设置,默认12小时
   tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);//默认30天,这里修改为7天
   tokenServices.setTokenEnhancer(jwtAccessTokenConverter());//使用JWT生成密钥
   return tokenServices;
 }

 //对应于配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter核心过滤器
   @Override
   public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
     security.tokenKeyAccess("permitAll()");
     security.checkTokenAccess("isAuthenticated()");//allow check token
     security.allowFormAuthenticationForClients();
   }
}

ResourceServer配置

主要配置以下两个:

@Override
public void configure(ResourceServerSecurityConfigurer resources) {
  //配置资源信息
}


@Override
public void configure(HttpSecurity http) throws Exception {
   //配置安全策略信息
}

配置示例:

/**
* ResourceServerConfig.java
* 功能:资源服务器配置
* @author  kavenran
* 2018年2月23日
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{
 //自定义的JWT
 @Autowired
 JwtTokenStore jwtTokenStore;//自定义的tokenStore

 //自定义的converter
   @Autowired
   JwtAccessTokenConverter jwtAccessTokenConverter;

 //设置资源ID,经过授权访问此资源ID的Token才能访问此资源
 @Override
   public void configure(ResourceServerSecurityConfigurer resources) {
       resources.resourceId("order-service").tokenStore(jwtTokenStore);
   }


 @Override
   public void configure(HttpSecurity http) throws Exception {
       http
               .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
               .and()
               .requestMatchers().anyRequest()
               .and()
               .anonymous()
               .and()
               .authorizeRequests()
               .antMatchers("/order/**").authenticated();//配置order访问控制,必须认证过后才可以访问
   }
}

OAuth自带的认证endpoint:

/oauth/authorize:验证
/oauth/token:获取token
/oauth/confirm_access:用户授权
/oauth/error:认证失败
/oauth/check_token:资源服务器用来校验token
/oauth/token_key:如果jwt模式则可以用此来从认证服务器获取公钥  

刷新token是通过`oauth/token?grant_type=refresh_token`实现的

注:关注作者微信公众号,了解更多分布式架构、微服务、netty、MySQL、spring、、性能优化、等知识点。

公众号:《 Java大蜗牛 

© 著作权归作者所有

共有 人打赏支持
Java大蜗牛
粉丝 18
博文 56
码字总数 173554
作品 0
长沙
认证鉴权与API权限控制在微服务架构中的设计与实现(一)

作者: [Aoho’s Blog] 引言: 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第一篇,本系列预计四篇文章讲解微服务下的认证鉴权与API权限控制的实现。 1. 背景 最近在做...

嘿嘿!!
2017/12/11
0
0
Spring Security

重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API 重拾后端之Spring Boot(一):REST API的搭建可以这样简单重拾后端之Spring Boot(二):MongoDb的无缝集成重拾后端之...

掘金官方
01/04
0
0
认证鉴权与API权限控制在微服务架构中的设计与实现(四)

引言: 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的完结篇,前面三篇已经将认证鉴权与API权限控制的流程和主要细节讲解完。本文比较长,对这个系列进行收尾,主要内容...

aoho
2017/10/26
0
0
认证鉴权与API权限控制在微服务架构中的设计与实现(三)

引言: 本文系《认证鉴权与API权限控制在微服务架构中的设计与实现》系列的第三篇,本文重点讲解token以及API级别的鉴权。本文对涉及到的大部分代码进行了分析,欢迎订阅本系列文章。 1. 前文...

aoho
2017/10/23
0
0
shiro jwt 构建无状态分布式鉴权体系

一:JWT 1、令牌构造 JWT(json web token)是可在网络上传输的用于声明某种主张的令牌(token),以JSON 对象为载体的轻量级开放标准(RFC 7519)。 一个JWT令牌的定义包含头信息、荷载信息、签...

wangjie2016
2017/11/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

angular指令监听ng-repeat渲染完成后执行自定义事件方法

今天工作中遇到需要用到ng-repeat遍历渲染完后执行某个操作,angular本身并没有提供监听ng-repeat渲染完成的指令,所以需要自己创建自定义指令。 在ng-repeat模板实例内部会暴露出一些特殊属...

孟飞阳
43分钟前
1
0
URLEncoder和URLDecoder

public static void main(String[] args) { String str1 = "https://test1-life.pingan.com/ilifecore/productMall/loading.html?productId=8000000241&channelCode=XCX00001&productCode=00......

鬼才王
53分钟前
2
0
对象及变量的并发访问-第一篇

方法内部的变量为线程安全变量 “非线程安全”问题存在于“共享变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得结果也就是“线程安全”的。 package chaprer3;/**...

简心
53分钟前
1
0
程序媛眼中的程序猿原来是这样子的!

一直都想写一篇关于描述程序员的文章,但是一直没能开头,一来因为文笔不好,更主要的原因是貌似对程序员既熟悉又不熟悉,很怕写出来的是以偏概全,给大家造成对程序员的既定印象,不过,管他...

Java小铺
今天
1
0
bean标签

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权;凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记。 bean标签 bean标签中的init-method属性,该属性...

凯哥学堂
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部