Spring-Boot Actuator:使用 JWT 访问敏感的管理端点

Posted

技术标签:

【中文标题】Spring-Boot Actuator:使用 JWT 访问敏感的管理端点【英文标题】:Spring-Boot Actuator: access sensitive management endpoints with JWT 【发布时间】:2018-03-26 19:43:36 【问题描述】:

我主要关注https://www.youtube.com/watch?v=EoK5a99Bmjc&list=WL&index=12(总结:https://netcrash.wordpress.com/2017/08/21/summary-of-josh-long-youtube-video-about-security-oauth/#comment-39,直到 ResourceServer 发挥作用)。

我已阅读Access sensitive Spring boot actuator endpoints via tokens in browser,因为它接近我需要的内容,但我不一定需要浏览器,并且该问题没有得到解答。

与教程相比,我在 pom.xml 中包含 spring-boot-starter-actuator 作为依赖项。

我的 application.properties 如下所示:

logging.level=DEBUG
server.port=9191
endpoints.health.enabled=true
endpoints.health.sensitive=false
endpoints.shutdown.enabled=true
endpoints.shutdown.sensitive=true
management.contextPath: /manage
management.security.enabled=true

我的 AccountUserDetailsS​​ervice 如下所示:

package com.divstar.particle.authservice.rest;
import java.text.MessageFormat;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class AccountUserDetailsService implements UserDetailsService 

    private final IAccountRepository accountRepository;

    public AccountUserDetailsService(final IAccountRepository accountRepository) 
        this.accountRepository = accountRepository;
    

    @Override
    public UserDetails loadUserByUsername(final String username) 
        return accountRepository.findByUsername(username)
                                .map(account -> new User(account.getUsername(), account.getPassword(), account.isEnabled(),
                                        account.isNonExpired(), account.isCredentialsNonExpired(), account.isNonLocked(),
                                        AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER", "ROLE_ACTUATOR")))
                                .orElseThrow(() -> new UsernameNotFoundException(
                                        MessageFormat.format("Could not find the user 0!", username)));
    

我包含了角色“ROLE_ACTUATOR”,因为这似乎是执行器访问管理服务端点所需要的。

主应用程序类看起来相当简单:

package com.divstar.particle.authservice;

import java.util.stream.Stream;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import com.divstar.particle.authservice.rest.Account;
import com.divstar.particle.authservice.rest.IAccountRepository;

@SpringBootApplication
public class Application 
    @Bean
    CommandLineRunner initUsers(final IAccountRepository userRepository) 
        return args -> Stream.of("test,long", "blame,short", "pass,secure")
                             .map(tpl -> tpl.split(","))
                             .forEach(tpl -> userRepository.save(new Account(tpl[0], tpl[1], "sample@mail.com", true, true, true, true)));
    

    public static void main(final String[] args) 
        SpringApplication.run(Application.class, args);
    

IAccountRepository 扩展了 JpaRepository 并定义了“findByUsername”,它返回 Optional。

我的 AuthorizationServerConfigurationAdapter 看起来像这样:

package com.divstar.particle.authservice;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;

@Configuration
@EnableAuthorizationServer
public class AuthServiceConfiguration extends AuthorizationServerConfigurerAdapter 
    private final AuthenticationManager authenticationManager;

    public AuthServiceConfiguration(final AuthenticationManager authenticationManager) 
        super();
        this.authenticationManager = authenticationManager;
    

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception 
        clients
               .inMemory()
               .withClient("html5")
               .secret("clientsecret")
               .authorizedGrantTypes("password")
               .scopes("openid");
    

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.authenticationManager(authenticationManager);
    

启动身份验证服务器后,我使用以下 cURL 命令获取令牌:

 curl html5:clientsecret@localhost:9191/oauth/token -d grant_type=password -d username=test -d password=long

它在 JSON 回复中返回 access_token 以及更多信息。

但是,如果我尝试像这样访问敏感端点(例如 /manage/shutdown):

curl -X POST -H"Authorization: bearer b8412762-af80-4b7d-9eb9-3fa472dd37c9" localhost:9191/manage/shutdown

我收到以下错误消息:

"timestamp":1508029184236,"status":401,"error":"Unauthorized","message":"Full 需要身份验证才能访问此 资源","路径":"/manage/shutdown"

我错过了什么/做错了什么?如果令牌有效(给定适当的权限/角色),我如何让 spring-boot 执行器识别 JWT 并执行给定的敏感端点?

【问题讨论】:

【参考方案1】:

根据您共享的信息,您似乎并没有真正告诉 Spring 使用您的自定义 UserDetailsS​​ervice。为此,您必须拥有一个扩展 WebSecurityConfigurerAdapter 的类,然后像这样覆盖 configure(AuthenticationManagerBuilder auth) 方法:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception 
  auth.userDetailsService(customUserDetailsService);

有关 OAuth2 和自定义 UserDetailsS​​ervice 的更多信息,请参见此处:https://***.com/a/43245998/1320627

确保正确配置用户详细信息服务的最简单方法是添加 REST 端点,通过身份验证进行保护,然后尝试点击它。

【讨论】:

我确实没有扩展 WebSecurityConfigurerAdapter 也没有在那里注入我的自定义 UserDetailsS​​ervice。我现在尝试了这个,但它似乎没有改变任何东西。如果我有执行器端点的自定义安全性,我是否需要打开或关闭 "management.security.enabled"?我将使用新创建的课程更新我的问题。我也不完全理解您所说的“添加 REST 端点”是什么意思——我认为执行器已经这样做了?除了我不知道如何正确保护它。 有趣。我的意思是建议您添加一个新的控制器获取映射,以便您可以测试您的身份验证过程是否正常工作。你是对的,Actuator 做同样的事情。如果您有一个有效的现有控制器映射,那么您将获得更多信息来追踪问题。我怀疑问题仍然在于您的授权配置。稍后会更多。

以上是关于Spring-Boot Actuator:使用 JWT 访问敏感的管理端点的主要内容,如果未能解决你的问题,请参考以下文章

spring-boot 速成 actuator

spring-boot Actuator 连同 Spring Security 和 Form Basic Auth

关于spring-boot-actuator的httptrace端点不生效问题解决办法

Spring Boot 2.x Actuator

使用 prometheus 的自定义计数器在 /actuator/prometheus 上不可见

management & Actuator