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

来源:互联网 发布:域名批量查询工具下载 编辑:程序博客网 时间:2024/06/15 20:02

应用使用Spring Cloud OAuth2保护之后,牵扯到的配置,联动的地方是比较多的,下面给出一个我们在生产环境中使用的配置模型,欢迎留言讨论。

Authorization Server接口分析

  1. /oauth/token_key:需要验证;接口是resource server使用security.oauth2.client.client-id和security.oauth2.client.client-secret作为用户名和密码,使用basic发起请求的(参看JwtTokenServicesConfiguration#getKeyFromServer());
  2. /oauth/token:需要验证;接口自带验证信息
  3. /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@EnableAuthorizationServerpublic 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@Slf4jpublic 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@EnableResourceServerpublic 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@EnableResourceServerpublic 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@EnableOAuth2Clientpublic 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

账号权限体系

  1. 可以为每个应用的actuator创建一个账号,主要是有ACTUATOR权限,也可以通过management.security.roles自己指定这个角色的名称,可以多个应用共用一个账号;
  2. 剩下的就是业务系统的账号权限体系,可以根据自己的业务情况,进行设计

注意

  1. 所有的服务器都要开启Spring Security Http Basic验证;Actuator的接口都是通过Http Basic验证的;

总结

通过上边的配置,提供了一整套基于Spring Cloud、Spring Cloud Security OAuth2、Actuator和Docker环境下的安全配置方案。通过上边的配置方案,我们可以看到,授权服务器上有授权相关的接口和Actuator接口,都得到了保护;资源服务器上的业务接口和Actuator接口,也得到了保护;Zuul Server上只保护Actuator提供的接口,所有的代理的接口,都不做鉴权,直接将请求转发到资源服务器。与Docker环境交互也是在安全机制下进行的交互。

原创粉丝点击