您的位置:首页 > 运维架构 > Docker

Spring Cloud OAuth2、 Actuator、Docker联配最佳实践

2017-09-15 14:44 232 查看
应用使用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

所有服务实例上的Actuator接口都被Spring Security完全保护起来了,那么如何让Docker Compose中配置的脚本访问Actuator的接口呢,其实这个问题就是脚本如何访问应用的Actuator接口,看demo:

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验证的;

总结

通过上边的配置,提供了一整套基于Spring Cloud、Spring Cloud Security OAuth2、Actuator和Docker环境下的安全配置方案。通过上边的配置方案,我们可以看到,授权服务器上有授权相关的接口和Actuator接口,都得到了保护;资源服务器上的业务接口和Actuator接口,也得到了保护;Zuul Server上只保护Actuator提供的接口,所有的代理的接口,都不做鉴权,直接将请求转发到资源服务器。与Docker环境交互也是在安全机制下进行的交互。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: