什么是Spring Security

217 阅读14分钟

Spring Security

什么是Spring Security

Spring Security是一个强大且高度可定制的认证和访问控制框架。它是保护基于Spring的应用程序的事实标准。

Spring Security是一个专注于为Java应用提供认证和授权的框架。像所有的Spring项目一样,Spring Security的真正强大之处在于它可以很容易地被扩展以满足客户的要求。

  • 对认证和授权的全面和可扩展的支持
  • 对诸如会话固定、点击劫持、跨站请求伪造等攻击的保护
  • 可以与Spring Web MVC集成

安全概念

  • Principal(当事人)执行动作的用户、设备或系统
  • Authentication(认证)确定当事人的认证是否有效
  • Authorization(授权)决定是否允许某个当事人访问某项资源
  • Authority(权限)允许访问的权限或凭证(如角色)
  • Secured Resource(保护资源)受保护的资源

认证 Authentication

  • 有多种认证机制
    • 例如:基本认证、摘要认证、表单认证、X.509认证、OAuth认证
  • 证书和授权数据有许多可选的存储方案
    • 例如:内存中(开发时),数据库,LDAP

授权 Authorization

  • 授权是基于认证的
    • 在决定一个用户是否被允许访问一个资源之前,必须确定用户身份
  • 授权决定了你是否具有必要的权限
  • 决策过程通常是基于角色的
    • ADMIN 角色可以取消订单
    • MEMBER 角色可以下订单
    • GUEST 角色可以浏览目录

Spring Security 优点

  • 便携式
    • 能用于任何Spring项目, 可以应用于Web项目和非WEB项目.
  • 关注点分离( SoC Separation of Concerns)
    • 业务逻辑与安全问题是解耦的
    • 认证与授权是解耦的,改变认证后,对授权没有影响
  • 灵活的 & 可扩展的
    • 认证: 基本认证、表单认证、X.509认证、OAuth认证、Cookies认证、单点登录……
    • 存储: LDAP、RDBMS、属性文件、自定义DAO……
    • 高可定制性

Spring Security 使用步骤

  1. 设置过滤器链(Spring Boot已经为你做了)
    1. 认证信息,密码加密
  1. 配置安全(授权)规则
  2. 设置Web认证

Spring Security 配置

引入 spring-boot-starter-security, Spring Boot 就会自动配置过滤器链

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

启动后,在控制台就能看见默认的测试用户user的密码:

Using generated security password: 129bf6c5- 79c6-4858-b705-26b60e96ee1f

此用户名是内存中创建的测试用户.

访问Web界面,任何请求都需要登录:

可以修改配置application.yml, 修改这个默认配置:

security:
  user:
    password: '1234'
    name: tom
    roles: ADMIN,USER

上述明文密码是非常不安全的!

密码加密和一般信息加密的区别

  • 一般信息加密, 必须可逆, 明文加密为密文, 密文可以解密为明文
  • 密码加密,可以单向编码, 无需解密, 直接比较密文即可验证原文,因为不需要解密, 所以更加安全。
    • 密码加密一般使用消息摘要算法, 进行单向不可逆运算

MD5, SHA256, SHA512等不是加密算法, 是消息摘要算法, 因为其具有单向运算, 不可以逆向运算, 非常适合用于密码加密!

  • 消息摘要不是加密
  • 适合用于密码加密

密码编码器 PasswordEncoder

Spring 提供了强大的委托密码编码器, 使用密码加密可以提升密码的安全性,避免暴力破解:

配置委托密码编码器:

@Configuration
public class PasswordEncoderConfig {
    Logger logger = 
        LoggerFactory.getLogger(PasswordEncoderConfig.class);
    /**
     * 为 Spring Security 提供密码加密器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder passwordEncoder =
                PasswordEncoderFactories
            	.createDelegatingPasswordEncoder();
        logger.debug("配置委托密码编码器 {}", passwordEncoder
                     .getClass().getName());
        return passwordEncoder;
    }
}

配置后,控制台出现:

配置密码编码器 org.springframework.security.crypto.password.DelegatingPasswordEncoder

测试密码加密:

@SpringBootTest
public class PasswordEncoderTests {

    private static final Logger logger = 
        LoggerFactory.getLogger(PasswordEncoderTests.class);

    @Autowired
    PasswordEncoder passwordEncoder;

    @Test
    void encode(){
        String password = "1234";
        String encodedPassword = passwordEncoder.encode(password);
        logger.debug("{}:{}", password, encodedPassword);

    }
}

测试1234的加密结果结果:

1234:{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia

必须将application.yml文件中的更新为加密密码,否则登陆时候将出现错误:

There is no PasswordEncoder mapped for the id "null"

更新application.yml文件中的更新为加密密码:

security:
  user:
    name: demo
    password: '{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia'
    roles: ADMIN,USER,MANAGER

使用用户名:demo  密码:1234 可以登陆了。

Spring提供委托PasswordEncoder

  • 密码需要加密,可以使用单向哈希值对密码进行编码
    • sha256、bcrypt、(sha, md5……)
    • 可与任何认证机制一起使用
  • 添加“盐”字符串使得加密更加健壮
  • BCrypt 是目前最优秀的加密算法

在Spring Security 5(和Spring Boot 2)中引入委托PasswordEncoder

  • 使用新的密码存储格式。{id}encodedPassword
    • {id}代表一个特定编码器的逻辑名称
  • 根据一个前缀的ID,委托给另一个PasswordEncoder
  • 目前使用BCrypt作为默认的 "最佳实践"编码方案

目前的加密格式为:

{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia

使用接口 UserDetailsService 和 UserDetails 提供认证信息

如果希望利用数据库等数据源提供认证信息, 需要实现UserDetailsService 和 UserDetails接口

首先在数据库中提供用户数据:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `roles` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
);

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'tom', '{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia','ADMIN');
INSERT INTO `user` VALUES ('2', 'jerry', '{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia','USER');
INSERT INTO `user` VALUES ('3', 'master', '{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia','ADMIN,USER,MANAGER');
INSERT INTO `user` VALUES ('4', 'tony', '{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia','MANAGER');

其中密码为“委托PasswordEncoder”加密后的“1234”, 数据库user表内容如下:

IDNamePassword角色ROLES
1tom1234ADMIN
2jerry1234USER
3master1234ADMIN,USER,MANAGER
4tony1234MANAGER

此处略去 Spring JDBC 的数据库访问代码..

实现Spring Security提供 UserDetails接口:

public class UserDetailsImpl implements UserDetails  {
    User user;

    public UserDetailsImpl(User user) {
        this.user = user;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        String[] roles = user.getRoles().split(",");
        List<SimpleGrantedAuthority> list = new ArrayList<>();
        for (String role : roles) {
            //角色必须是以ROLE_开头
            list.add(new SimpleGrantedAuthority("ROLE_"+role));
        }
        return list;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        //没有过期
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        //没有被锁定
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        //证书没有过期
        return true;
    }

    @Override
    public boolean isEnabled() {
        //允许
        return true;
    }
}

实现UserDetailsServices:

/**
 * SpringSecurity 提供认证信息的接口
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    Logger logger = 
        LoggerFactory.getLogger(UserDetailsServiceImpl.class);

    @Autowired
    UserService userService;

    /**
     * username 是登录表单上的用户信息
     * @param username
     * @return 
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {

        User user = userService.getByUsername(username);
        if (user == null) {
            logger.info("获取认证信息失败 {}", username);
            throw new UsernameNotFoundException("没有用户信息");
        }
        logger.debug("提供认证信息:{}", user );
        return new UserDetailsImpl(user);
    }
}

配置UserDetailsService,配置后Spring Security 不再配置测试使用的UserDetailsService:

@Configuration
public class UserDetailsServiceConfig {
    Logger logger = 
        LoggerFactory.getLogger(UserDetailsServiceConfig.class);

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Autowired
    public void config(AuthenticationManagerBuilder builder) 
        throws Exception{
        //把认证管理器中的认证信息更换为 自定义的userDetailsService
        logger.debug("配置 UserDetailsService.");
        builder.userDetailsService(userDetailsService);
    }
}

注意:使用   @Autowired 注解配置 AuthenticationManagerBuilder

配置后启动SpringBoot,控制台出现:

配置 UserDetailsService

可以使用 tom 1234 登陆,登陆时候控制台出现:

提供认证信息:User{id=1, username='tom', password='{bcrypt}$2a$10$Y9Fer2InTmzomIakxG2aEuKab.YIMp8mdIXtTsWjwq0OjXkL6Rkia', roles='ADMIN'}

关于UserDetailsService 自动配置规则:

  • Spring Boot 提供了基于条件注解的自动配置规则
  • 如果没有任何的 UserDetailsService ,则认证管理器会创建基于内存的UserDetailsService
    • 用户 user, 密码在控制台或者配置文件
  • Spring Security 的认证管理器会自动寻找 UserDetailsService 类型组件作为认证信息来源
  • 如果需要手动配置, 就调用 builder.userDetailsService(userDetailsService) 进行手动配置, 如果添加了手动配置, 则自动配置失效。

认证管理器

  • 默认情况下, 认证管理器可以自动工作完成认证功能
    • 采用表单认证方式, 默认认证表单可以更换
    • 如果需要连接数据库,可以提供自己实现的UserDetailsService
  • 如使用其他认证方式, 如ajax jwt 等, 可以手动调用认证管理器, 完成手工认证。

方法安全配置(方法授权)

Spring提供了方法访问授权

  • Spring Security使用AOP 用于处理方法级别的安全
    • Spring自有的注解
      • @PreAuthorize("hasRole('USER')")
    • JSR-250注解(Java EE 标注)
      • @RolesAllowed("ROLE_USER")
      • @RolesAllowed({"ROLE_MEMBER", "ROLE_USER"})
  • 建议:
    • 保证你的业务安全,统一访问一个安全层
    • 不直接访问其它层,避免绕过Security访问业务层

开启方法访问安全配置:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启方法访问授权
public class MethodSecurityConfig {
}

加上 @EnableGlobalMethodSecurity 后Spring Security 会自动添加AOP代理, 添加方法拦截, 实现方法安全.

本质上来说, 方法安全是利用AOP实现的!

在业务方法上开启授权:

@Override
@PreAuthorize("hasRole('ADMIN')")
public List<User> list() {
    return userDao.findAllUser();
}

没有 ADMIN 角色的用户将返回 403 状态码。

Web 安全配置

Spring Security 提供了Web资源保护,这些功能是利用Web过滤器实现的:

Spring Boot 自动配置上述的过滤器。

利用配置类配置URL授权:

    • Spring Boot 不需要使用 @EnableWebSecurity 已经自动配置
    • 这个@EnableWebSecurity注解,会自动配置过滤器链
  • 继承 WebSecurityConfigurerAdapter 添加Web权限配置
    • 在这个文件里面配置对 URL 进行授权
    • configure(HttpSecurity http) 用于配置过滤器链规则
    • configure(WebSecurity web) 可以配置绕过过滤器链规则,这个并不建议使用!
      • 短路规则
@Configuration
// @EnableWebSecurity SpringBoot 无需配置
// 继承 WebSecurityConfigurerAdapter 添加Web权限配置
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //.mvcMatchers("/images/**").permitAll() 通过安全过滤器. 被放过了
        http.authorizeRequests()
                .mvcMatchers("/about/**", "/signup/**").permitAll()
          		.mvcMatchers("/admin/**").hasRole("ADMIN")
                .mvcMatchers("/accounts/edit*").hasRole("ADMIN")
                .mvcMatchers("/accounts/**").hasAnyRole("USER","ADMIN")
                .anyRequest().authenticated()
                .and().formLogin();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //忽略 /images/** 连接, 绕过了 web 安全过滤器
        web.ignoring().mvcMatchers("/images/**", "/css/**",
                                   "/javascripts/**");
    }
}

关于URL授权configure(HttpSecurity http)

为URL定义具体的授权限制

  • 支持“Ant-style”模式匹配
    • "/admin/*" 仅匹配 "/admin/xxx"
    • "/admin/**" 匹配在 /admin 下的任意路径
      • 例如 "/admin/database/access-control"

例子:

//http   授权请求             匹配  /admin下的任意路径  需要有ADMIN角色
http.authorizeRequests().mvcMatchers("/admin/**").hasRole("ADMIN")

上述配置规则为:http的授权请求,匹配 /admin下的任意路径,需要有ADMIN角色

authorizeRequest()可以处理多个授权请求,先匹配到的规则优先处理,所以需要将特定的匹配规则放到前面:

protected void configure(HttpSecurity http) throws Exception {
     http.authorizeRequests()
         //signup 和 about 被许可,无需授权直接放过
       .mvcMatchers("/signup/*", "/about/*").permitAll() 
         //accounts/edit*需要ADMIN角色
       .mvcMatchers(HttpMethod.PUT, "/accounts/edit*").hasRole("ADMIN") 
         // accounts/**需要USER,ADMIN角色
       .mvcMatchers("/accounts/**").hasAnyRole("USER","ADMIN") 
         // 其他任何请求都必须认证(登录即可访问)
       .anyRequest().authenticated() 
       .and().formLogin(); //开启表单登陆功能
}

处理有顺序,第一个匹配规则优先处理,特定的匹配规则放到前面!!!

mvcMatchers() mvc匹配

permitAll() 许可所有

hasRole("ADMIN") 需要有ADMIN角色

hasAnyRole("USER","ADMIN")需要有USER ADMIN 角色之

anyRequest() 任何请求

authenticated() 必须认证完成

.and().formLogin(); 开启表单登陆功能

URL匹配规则

  • 早期 Spring Security 使用antMatchers
  • 现在建议使用mvcMatchers
    • 使用和 @ RequestMapping一样的规则
    • mvcMatchers是新API,不太容易出错,建议使用
http.authorizeRequests()
		// 仅匹配 /admin
		.antMatchers("/admin").hasRole("ADMIN")
		// 匹配 /admin, /admin/, /admin.html, /admin.xxx
		.mvcMatchers("/admin").hasRole("ADMIN")
		//它们看起来是一样的,其实并不是

绕过安全管理

  • 有些URL不需要保护起来(例如静态资源)
    • permitAll()  允许开放访问, 但仍由Spring Security过滤器链进行处理
  • 使用 configure(WebSecurity web) 可以完全绕过安全管理:
    • web.ignoring() 绕过 Spring Security过滤器链, 不进行安全处理
@Override
protected void configure(WebSecurity web) throws Exception {
  web.ignoring().mvcMatchers("/css/**", "/images/**", "/javascript/**");
}
//这些URL "/css/**", "/images/**", "/javascript/**" 直接通过,不检查, 绕过了过滤器,性能好

Spring Actuator

Spring Actuator 是 Spring Boot包括一些额外的功能,以帮助上线生产时监控和管理你的应用程序。可以通过HTTP端点或JMX来管理和监控应用程序。可以用于收集审计、健康指标。

Actuator提供了:

  • 无需自己实现的生产级监控
  • 一个可以轻松收集和返回指标的框架
  • 集成第三方仪表盘显示分布式系统的健康和指标

Actuator库增加了许多可用于生产的监控功能:

  • 可通过JMX访问
  • 或使用HTTP端点(end point)访问

使用Spring Actuator

配置依赖:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

出于安全考虑,默认情况下只有2个端点是暴露的

  • /actuator/health
  • /actuator/info
  • 使用Spring Security保护actuator URL安全(需要登陆认证才能访问)

默认可以访问的HTTP端点:

http://localhost:8080/actuator/health

结果:

{
   "status": "UP"
}

info HTTP端点,默认打开显示application.yml中配置的info信息:

首先配置 application.yml

info:
  application:
    name: Spring Secturty Actuator Demo
    description: Very cool Spring Boot application
    version: 1.0
    message: Hello
    whoami: Robin

默认可以访问 info 端点:

http://localhost:8080/actuator/info

结果:

{
  "application": {
    "name": "Spring Secturty Actuator Demo",
    "description": "Very cool Spring Boot application",
    "version": "@project.version@",
    "message": "Hello",
    "whoami": "Robin"
  }
}

配置Spring Actuator

默认端口与应用程序相同 都是 8080, 访问路径 /actuator/**

修改actuator根路径和访问端口:

management:
  server:
    port: 8081
  endpoints:
    web:
      base-path: /sys

一些可用的 Actuator 端点

端点功能
beans由应用程序创建的Spring  Bean
conditions由自动配置使用的条件
env在Spring的Environment中的Properties
health应用程序当前的状态
httptraceWeb应用程序中最近的HTTP请求
info显示任意应用程序信息
loggers查询和修改日志级别
mappingsSpring  MVC请求映射
metrics可用的metric(指标)列表
session获取或删除用户Session(仅当使用Spring Session时)
shutdown关闭应用程序(优雅地),默认情况下是禁用的
threaddump执行线程转储
jolokia在HTTP上公开JMX  Bean(不仅仅是执行器)。

Actuator端点:启用 和 暴露

  • 启用 是指 创建了给定的端点,其端点Bean存在于应用程序上下文中
    • 默认 = 除了shutdown、httptrace、auditevents(从Spring Boot 2.2.0版本开始),其它端点都已启用
  • 暴露 是指 给定的端点可以通过JMX或HTTP访问
  • JMX默认 = 全部暴露
  • HTTP 默认 = 只暴露info和health

JMX 中的端点

执行命令 jconsole 选择SpringBoot项目

在 JConsole 中查看JMX暴露的端点

控制JMX端点的暴露

  • 可以通过属性控制JMX端点的暴露
  • include(包含)和exclude(排除)这2个属性都可以使用逗号分隔的列表
management:
  endpoints:
    jmx:
      exposure:
        include: '*'
        exclude:

默认设置:全部暴露,无例外(排除)

HTTP Actuator端点

暴露HTTP端点

默认设置:

management:
  endpoints:
    web:
      exposure:
        include: health,info

只暴露beans、env和info端点, health不会暴露:

management:
  endpoints:
    web:
      exposure:
        include: beans,env,info

暴露所有端点:

management:
  endpoints:
    web:
      exposure:
        include: '*'

可以使用Spring Security保护端点:

protected void configure(HttpSecurity http) throws Exception {  
       http.authorizeRequests()
              .mvcMatchers("/actuator/**").hasRole("ACTUATOR")
              .anyRequest().authenticated();
}

测试每个端点。

收集metric(指标)信息

Spring Boot 使用Micrometer 库 收集程序监控指标。

收集metric(指标)信息, 就是收集Java Bean组件的信息,这些信息是通过Micrometer库进行测量,也可以进行自定义测量。

这些指标信息包括:数量统计,耗用时间统计等。

  • Spring Boot 2.0使用Micrometer库
    • 维度化的Metric
  • 它可以对你的基于JVM的应用程序代码进行测量,而不需要锁定供应商
    • 用于Metric的SLF4J
  • 旨在为你的Metric收集活动增加很少或没有开销

访问:http://localhost:8080/actuator/metrics

{
  names: [
    "hikaricp.connections",
    "hikaricp.connections.acquire",
    "hikaricp.connections.active",
    "hikaricp.connections.creation",
    "hikaricp.connections.idle",
    "hikaricp.connections.max",
    "hikaricp.connections.min",
    "hikaricp.connections.pending",
    "hikaricp.connections.timeout",
    "hikaricp.connections.usage",
    "http.server.requests",
    "jdbc.connections.max",
    "jdbc.connections.min",
    "jvm.buffer.count",
    "jvm.buffer.memory.used",
    "jvm.buffer.total.capacity",
    "jvm.classes.loaded",
    "jvm.classes.unloaded",
    "jvm.gc.live.data.size",
    "jvm.gc.max.data.size",
    "jvm.gc.memory.allocated",
    "jvm.gc.memory.promoted",
    "jvm.gc.pause",
    "jvm.memory.committed",
    "jvm.memory.max",
    "jvm.memory.used",
    "jvm.threads.daemon",
    "jvm.threads.live",
    "jvm.threads.peak",
    "jvm.threads.states",
    "logback.events",
    "process.cpu.usage",
    "process.files.max",
    "process.files.open",
    "process.start.time",
    "process.uptime",
    "system.cpu.count",
    "system.cpu.usage",
    "system.load.average.1m",
    "tomcat.sessions.active.current",
    "tomcat.sessions.active.max",
    "tomcat.sessions.alive.max",
    "tomcat.sessions.created",
    "tomcat.sessions.expired",
    "tomcat.sessions.rejected"
  ]
}

访问详细的维度:http://localhost:8080/actuator/metrics/system.cpu.count

{
  name: "system.cpu.count",
  description: "The number of processors available to the Java virtual machine",
  baseUnit: null,
  measurements: [
    {
      statistic: "VALUE",
      value: 8
    }
  ],
  availableTags: [ ]
}

自定义Metric指标

自定义Metric可以使用Micrometer类来测量,如CounterGaugeTimerDistributionSummary

  • 类是通过MeterRegistry Bean创建或注册的
  • 自定义Metric的名称被列在如下端点: /actuator/metrics
  • 自定义Metric数据可以在如下路径获取: /actuator/metrics/[custom-metric-name]

使用Timer监控,Timer提供其Metric(指标)的计数、平均数、最大值和总和:

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    UserService userService;

    //Timer是Micrometer项目中的一部分
    private Timer timer ;

    @Autowired
    public UserController(MeterRegistry registry){
        //注册指标名称
        timer = registry.timer("users");
    }
    
    @GetMapping
    @Timed("users.list") //记录 Timer提供其Metric的计数、平均数、最大值和总和
    public List<User> list(Principal principal){
        System.out.println("当前用户:"+principal.getName());
        List<User> list = userService.list();
        return list ;
    }

}

访问 http://localhost:8080/users 初始化指标数据

然后再访问指标:http://localhost:8080/actuator/metrics/users.list,反馈结果:

{
  name: "users.list",
  description: null,
  baseUnit: "seconds",
  measurements: [
    {
      statistic: "COUNT",
      value: 1
    },
    {
      statistic: "TOTAL_TIME",
      value: 0.037395042
    },
    {
      statistic: "MAX",
      value: 0.037395042
    }
  ],
  availableTags: [
    {
      tag: "exception",
      values: [
        "None"
      ]
    },
    {
      tag: "method",
      values: [
        "GET"
      ]
    },
    {
      tag: "uri",
      values: [
        "/users"
      ]
    },
    {
      tag: "outcome",
      values: [
        "SUCCESS"
      ]
    },
    {
      tag: "status",
      values: [
        "200"
      ]
    }
  ]
}

使用 DistributionSummary 记录指标,Distribution Summary为其Metric提供一个计数、总和、最大值

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    UserService userService;

    private final DistributionSummary summary;

    @Autowired
    public UserController(MeterRegistry registry){
        //注册指标名称
        summary = DistributionSummary.builder("users.summary")
                .baseUnit("units").register(registry);
    }

    @GetMapping
    public List<User> list(Principal principal){
        System.out.println("当前用户:"+principal.getName());
        List<User> list = userService.list();
      	//记录到DistributionSummary 
        summary.record(list.size()); 
        return list ;
    }

}

请求: http://localhost:8080/actuator/metrics/users.summary 反馈:

{
  name: "users.summary",
  description: null,
  baseUnit: null,
  measurements: [
    {
      statistic: "COUNT",
      value: 1
    },
    {
      statistic: "TOTAL",
      value: 4
    },
    {
      statistic: "MAX",
      value: 0
    }
  ],
  availableTags: [
    {
      tag: "units",
      values: [
        "n"
      ]
    }
  ]
}

应用程序的健康状态

默认情况下,health端点只显示最基础的健康信息。设置Spring Boot属性 显示更多健康详情

management:
  endpoint:
    health:
      show-details: always
  • 许多健康指标都是自动设置的
    • 在classpath提供它们的依赖项
    • 磁盘空间、DataSource、Cassandra、Elasticsearch、InfluxDb、JMS、Mail、MongoDB、Neo4J、RabbitMQ、Redis、Solr……
  • 自定义健康检查可加到 /actuator/health 端点,并将被卷入整个应用程序的健康状态
    • 创建类实现HealthIndicator接口
    • 重写health() 方法返回状态
  • 或继承自AbstractHealthIndicator
    • 重写doHealthCheck() 方法
  • 内置的状态值
    • DOWN
    • OUT_OF_SERVICE-
    • UNKNOWN
    • UP
  • 严重程度的顺序可以用以下方法重写(修改)
management:
  endpoint:
    health:
      status:
        order: FATAL, DOWN, OUT_OF_SERVICE, UNKNOWN, UP

自定义监控:

@Component
public class DemoHealthCheck implements HealthIndicator {

    @Override
    public Health health() {
        Random random = new Random();
        double n = random.nextDouble();
        if (n > 0.5){
            return Health.down().build();
        }else{
            return Health.up().build();
        }
    }
}

访问/health

{
  status: "UP",
  components: {
    db: {
      status: "UP",
      details: {
        database: "MySQL",
        validationQuery: "isValid()"
      }
    },
    demoHealthCheck: {
      status: "UP"
    },
    diskSpace: {
      status: "UP",
      details: {
        total: 994662584320,
        free: 23837790208,
        threshold: 10485760,
        exists: true
      }
    },
    ping: {
      status: "UP"
    }
  }
}

集成选项

  • 除了REST端点,Actuator并没有提供任何东西
  • 为了真正增加价值,这些数据需要被收集、汇总,并绘制成图表,以易于利用
  • 可与执行器集成的外部监测系统

Spring boot admin

是一个可视化的监控工具,可以监控Spring Boot应用程序的运行状态,
包括内存、线程、JVM、日志、环境变量、配置属性、HTTP请求等等。
Spring Boot Admin是一个开源项目,可以在GitHub上找到它的源代码。
Spring Boot Admin是一个Spring Boot应用程序,可以通过Maven或者Gradle来构建。
Spring Boot Admin Server是一个Spring Boot应用程序,可以通过Maven或者Gradle来构建。
Spring Boot Admin Client是一个Spring Boot应用程序,可以通过Maven或者Gradle来构建。
Spring Boot Admin Client可以通过HTTP或者HTTPS来连接Spring Boot Admin Serve

使用步骤:
Spring Boot Admin Server 端的配置

  1. 引入依赖
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
  1. 注解配置
@SpringBootApplication
@EnableAdminServer
public class SpringBootAdminServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAdminServerApplication.class, args);
    }

}
  1. 配置文件
server:
  port: 8081

Spring Boot Admin Client 端的配置

  1. 引入依赖
<dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.4.2</version>
</dependency>

注意版本,这个版本好需要和当前Spring-boot版本号码一致,
当前的版本是2.4.2,如果使用2.2.0版本,会出现无法注册的问题。

  1. 配置文件
spring:
  application:
    name: spring-boot-admin-client
  boot:
    admin:
      client:
        url: http://localhost:8081   
management:
  endpoint:
    health:
      # 显示health的详细信息
      show-details: always
  # 暴露所有的Actuator端点
  endpoints:
    web:
      exposure:
        include: "*"
  info:
    env:
      enabled: true
  1. web安全配置
    放过Actuator端点的访问权 "/actuator/**"