微服務架構下的安全認證與鑑權

  1. 微服務下常見認證解決方案;
  2. OAuth認證與受權;
  3. JWT認證介紹;
  4. Spring Cloud的OAuth2實現;

單體應用轉變爲分佈式應用

單體應用轉變爲分佈式應用在架構方式上存在較大區別,單體應用下的簡單架構方式以下:java

分佈式應用的安全認證相對更加複雜,既要考慮它的安全性,一致性,還要考慮它的性能問題,開發成本等問題。分佈式應用下的簡單架構方式以下:git

常見的受權認證解決方案

  1. 單點登陸SSOgithub

    統一的認證服務器(CAS)算法

    缺點:每個用戶請求都要通過認證服務器,容易造成瓶頸spring

  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大蝸牛 

相關文章
相關標籤/搜索