Spring Cloud OAuth2、 Actuator、Docker联配最佳实践
2017-09-15 14:44
232 查看
应用使用Spring Cloud OAuth2保护之后,牵扯到的配置,联动的地方是比较多的,下面给出一个我们在生产环境中使用的配置模型,欢迎留言讨论。
/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正常交互,不会产生授权失败的问题;
[Authorization Server]
[SecurityConfiguration]
[OAuth2ResourceServerConfig ]
[SecurityConfiguration]
别忘了开启management endpoint的鉴权配置:
剩下的就是业务系统的账号权限体系,可以根据自己的业务情况,进行设计
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环境交互也是在安全机制下进行的交互。相关文章推荐
- 新IT运维时代 | Docker运维之最佳实践-上篇
- Docker最佳实践-部署LNMP环境
- Dockerfile最佳实践(二)
- [Docker] 写 Dockerfile 的最佳实践理论
- Docker运维之最佳实践
- 在 Docker 上开发应用 - 编写 Dockerfile 的最佳实践
- Docker学习笔记 — Dockerfile最佳实践
- Docker持续部署图文详解 | 高效运维最佳实践06
- Docker运维之最佳实践
- 编写Dockerfiles的最佳实践
- Docker日志收集最佳实践
- DockerFile最佳实践:
- Maven Docker 最佳实践
- Dockerfile 最佳实践
- 运行容器的最佳实践 - 每天5分钟玩转 Docker 容器技术(24)
- [Docker] 容器开发环境最佳实践理论
- 新IT运维时代 | Docker运维之最佳实践-下篇
- 阿里云上搭建Docker Swarm模式集群最佳实践
- Docker镜像原理和最佳实践
- Docker 最佳实践