應用使用Spring Cloud OAuth2保護之後,牽扯到的配置,關聯的地方是比較多的,下面給出一個我們在生産環境中使用的配置模型,歡迎留言讨論。
Authorization Server接口分析
- /oauth/token_key:需要驗證;接口是resource server使用security.oauth2.client.client-id和security.oauth2.client.client-secret作為使用者名和密碼,使用basic發起請求的(參看JwtTokenServicesConfiguration#getKeyFromServer());
- /oauth/token:需要驗證;接口自帶驗證資訊
-
/oauth/check_token:需要驗證;從RemoteTokenServices#getAuthorizationHeader,此接口在請求時,與/oauth/token_key接口類似,也會将security.oauth2.client.client-id和security.oauth2.client.client-secret的配置的值作為賬号和密碼basic協定一起發送給伺服器;
也就是說,隻要我們的Authorization Server開啟了httpBasic驗證,Resource Server或者OAuth2 Client配置了正确的security.oauth2.client.client-id和security.oauth2.client.client-secret,這三個接口可以與Resource Server和OAuth2 Client正常互動,不會産生授權失敗的問題;
Actuator
Actuator提供的接口分為敏感接口和非敏感接口,敏感接口隻有授權登入之後,才能通路,非敏感接口,可以直接通路,獲得全部,或者部分資訊,擷取部分資訊的接口,授權登入之後,可以獲得全部的資訊。我們想實作的效果是,Actuator的接口不再區分是敏感還是不敏感,無論是在Authorization Server還是Resource Server還是Oauth2 Client上,全部被Spring Security保護起來;并且所有的服務都開啟basic驗證,可以讓其他的元件使用basic協定授權登入,通路這些受保護的接口;
Spring Cloud Security OAuth2 Authorization Server和Actuator
在Authorization Server上,我們想實作的功能是,Authorization Server的接口是預設權限,Actuator被Spring Security 完全保護起來;Authorization Server上的核心配置如下:
[Authorization Server]
/**
*
* @author chenzheyang
* @since 0.1.0
*
*/
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(OAuth2AuthorizationServerConfiguration.class);
@Value("${security.oauth2.resource.jwt.private-key}")
private String privateKey;
@Value("${security.oauth2.resource.jwt.public-key}")
private String publicKey;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
public JwtAccessTokenConverter jwtTokenEnhancer() {
logger.info("Initializing JWT with public key:\n" + publicKey);
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
converter.setVerifierKey(publicKey);
return converter;
}
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtTokenEnhancer());
}
/**
*
* @param oauthServer
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()") // permitAll()
.checkTokenAccess("isAuthenticated()"); // isAuthenticated()
oauthServer.allowFormAuthenticationForClients();
}
/**
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
DefaultOAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
// requestFactory.setCheckUserScopes(true);
endpoints.authenticationManager(authenticationManager).tokenStore(jwtTokenStore())
.accessTokenConverter(jwtTokenEnhancer());
endpoints.requestFactory(requestFactory);
}
/**
* client_credentials/password/authorization_code/refresh_token
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
}
[SecurityConfiguration]
/**
*
* @author chenzheyang
* @since 0.1.0
*
*/
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Value("${security.user.name}")
private String actuatorUserName = "admin2";
@Value("${security.user.password}")
private String actuatorUserPassword = "secret2";
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favicon.ico");
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
jdbcUserDetailsManager.setDataSource(dataSource);
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(jdbcUserDetailsManager);
ProviderManager authenticationManager = new ProviderManager(Arrays.asList(authenticationProvider));
UserDetails actuatorUser = User.withUsername(actuatorUserName).password(actuatorUserPassword)
.authorities("ACTUATOR").build();
jdbcUserDetailsManager.deleteUser(actuatorUserName);
jdbcUserDetailsManager.createUser(actuatorUser);
if (log.isInfoEnabled()) {
log.info("created actuator user :{}:{}", actuatorUserName, actuatorUserPassword);
}
return authenticationManager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().csrf().disable();
}
}
Spring Cloud Security OAuth2 Resource Server和Actuator
Resource Server上的配置示例如下:
@Configuration
@EnableWebSecurity
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "weather-forecast";
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
return factory.getUserInfoRestTemplate();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(true);
resources.tokenStore(tokenStore());
}
@Override
public void configure(HttpSecurity http) throws Exception {
//具體的權限配置規則,再此配置; http.csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
Spring Cloud Security OAuth2 OAuth Client(Zuul Server)和Actuator
Zuul Server 有兩種用法,一種是作為UI伺服器,一種是作為接口伺服器(參看Spring Cloud OAuth2 & Zuul);作為UI伺服器,點很多,後門專門開篇部落格描述一下我們的實踐,作為接口伺服器的話;配置如下,所有請求全部允許,鑒權的操作下沉到各個資源伺服器,actuator的接口的權限還是全部需要驗證:
[OAuth2ResourceServerConfig ]
/**
*
* @author chenzhenyang
*
*/
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "zuul-server";
@Autowired
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter);
}
@Bean
public OAuth2RestTemplate loadBalancedRestTemplate(UserInfoRestTemplateFactory factory) {
return factory.getUserInfoRestTemplate();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
resources.tokenStore(tokenStore());
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic().and().authorizeRequests().anyRequest().permitAll();
}
}
[SecurityConfiguration]
/**
*
* @author chenzhenyang
*
*/
@Configuration
@EnableOAuth2Client
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
public OAuth2RestTemplate restTemplate(OAuth2ClientContext oauth2ClientContext,
OAuth2ProtectedResourceDetails details) {
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
/**
* 角色繼承配置
*
* @return
*/
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_DBA ROLE_DBA > ROLE_USER");
return roleHierarchy;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
}
}
别忘了開啟management endpoint的鑒權配置:
management:
context-path: /actuator
security:
enabled: true
Spring Cloud Security OAuth2 OAuth 、Spring Cloud Admin Server和Actuator
各個執行個體與Spring Cloud Admin互動的原理是,各個執行個體通過eureka.instance.metadata-map.user.name和eureka.instance.metadata-map.user.password将本執行個體可以通路actuator接口的賬戶發送給Spring Cloud Admin Server,Spring Cloud Admin Server在請求各個執行個體的actuator的接口的時候,使用basic協定,将此賬号一塊發送給具體的執行個體;如果我們修改了預設的actuator的context-path的話,還需要配置eureka.instance.metadata-map.context-path,将management.context-path一塊發送給Spring Cloud Admin Server;
與Eureka Server
eureka client與eureka server的互動都是eureka-client 主動發起的,也就是沒有ereka- server 通路eureak-client的接口的時候,也就是不需要在eureka-client上做專門的配置,隻需要在eureka-server上做配置就行了,用戶端配置的defaultZone.service: http://username:password@host…
Spring Cloud Security OAuth2 OAuth、Actuator和Docker
version: '3'
services:
weather-forecast:
image: xxx
hostname: weather-forecast
ports:
- 5001:5001/tcp
restart: always
networks:
- global
healthcheck:
test:
- CMD
- curl
- -f
- http://admin:secret@localhost:5001/actuator/health
interval: 0m30s
timeout: 10s
retries: 3
賬号權限體系
- 可以為每個應用的actuator建立一個賬号,主要是有ACTUATOR權限,也可以通過management.security.roles自己指定這個角色的名稱,可以多個應用共用一個賬号;
- 剩下的就是業務系統的賬号權限體系,可以根據自己的業務情況,進行設計
注意
- 所有的伺服器都要開啟Spring Security Http Basic驗證;Actuator的接口都是通過Http Basic驗證的;