Spring Boot应用端点仅对物理在场/SSH授权用户开放的实现问询
嘿,这个需求结合了身份认证和环境校验,挺贴合运维场景的,我给你几个在Spring Boot里能落地的技术方案,都是基于Spring Security生态的,上手不难:
这个方案核心是先限制请求来源的IP范围,再结合用户身份认证,确保只有内网/本机的授权用户能访问端点。
步骤1:配置IP白名单
在Spring Security的配置类里,添加IP地址的校验规则,比如允许服务器本机(127.0.0.1、localhost)、服务器所在局域网的IP段(比如192.168.1.0/24):@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/your-protected-endpoint/**") .access("hasAuthority('ROLE_AUTHORIZED_USER') and (hasIpAddress('127.0.0.1/32') or hasIpAddress('192.168.1.0/24'))") .anyRequest().permitAll() ) .formLogin(Customizer.withDefaults()) // 替换成你实际用的认证方式,比如JWT、OAuth2 .csrf(csrf -> csrf.disable()); // 根据业务场景决定是否关闭CSRF return http.build(); } }这里的
hasIpAddress是Spring Security自带的表达式,能直接校验请求的来源IP。步骤2:用户授权配置
确保你的用户体系里给授权用户分配ROLE_AUTHORIZED_USER权限,不管是从数据库读取还是测试阶段硬编码,都要和上面的表达式对应上。
这个方案需要验证请求用户是否持有服务器的SSH授权密钥,或者能通过服务器的SSH身份校验,这里有两种实现思路:
思路A:校验用户SSH公钥是否在服务器authorized_keys中
让用户在请求时携带自己的SSH公钥内容,后端读取服务器上的~/.ssh/authorized_keys文件,检查该公钥是否存在:
@Component public class SshPermissionValidator { private static final String AUTHORIZED_KEYS_PATH = System.getProperty("user.home") + "/.ssh/authorized_keys"; public boolean hasSshAccess(String userPublicKey) throws IOException { List<String> authorizedKeys = Files.readAllLines(Paths.get(AUTHORIZED_KEYS_PATH)); // 清理公钥格式,只取核心密钥部分(去掉前缀、注释) String cleanedUserKey = userPublicKey.trim().split(" ")[1]; return authorizedKeys.stream() .map(key -> key.trim().split(" ")[1]) .anyMatch(cleanedUserKey::equals); } }
然后在Spring Security里自定义权限表达式,把这个校验逻辑加进去:
@Configuration public class SecurityExpressionConfig extends DefaultWebSecurityExpressionHandler { @Autowired private SshPermissionValidator sshValidator; @Override protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) { WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi); root.setPermissionEvaluator(new PermissionEvaluator() { @Override public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) { if ("SSH_ACCESS".equals(permission)) { // 从请求Header里获取用户的SSH公钥 String userPublicKey = fi.getHttpRequest().getHeader("X-SSH-Public-Key"); try { return sshValidator.hasSshAccess(userPublicKey); } catch (IOException e) { return false; } } return false; } @Override public boolean hasPermission(Authentication auth, Serializable targetId, String targetType, Object permission) { return false; } }); return root; } }
之后在Security配置里修改访问规则,把SSH校验和IP校验做或逻辑:
.requestMatchers("/your-protected-endpoint/**") .access("hasAuthority('ROLE_AUTHORIZED_USER') and (hasIpAddress('127.0.0.1/32') or hasIpAddress('192.168.1.0/24') or hasPermission(null, 'SSH_ACCESS'))")
思路B:基于本地SSH会话校验(仅适用于服务器本机访问场景)
如果用户是通过SSH登录服务器后再访问应用,可以检查请求的环境变量是否包含SSH_AUTH_SOCK(SSH代理套接字路径),或者通过执行系统命令whoami确认当前用户是否在SSH授权列表中。不过这种方式只适合本机会话访问,远程SSH转发的场景需要额外处理。
这个方案是在服务器上存放一个只有授权用户能获取的密钥文件,用户请求时携带密钥内容,后端读取本地文件做对比:
步骤1:生成本地密钥文件
在服务器上生成一个随机密钥,并设置严格的访问权限:openssl rand -hex 32 > /opt/app/secure/endpoint.key chmod 600 /opt/app/secure/endpoint.key # 确保只有应用进程能读取步骤2:后端校验逻辑
@Component public class LocalKeyValidator { private static final String KEY_FILE_PATH = "/opt/app/secure/endpoint.key"; private String validKey; @PostConstruct public void loadValidKey() throws IOException { this.validKey = Files.readString(Paths.get(KEY_FILE_PATH)).trim(); } public boolean isValidKey(String providedKey) { return validKey.equals(providedKey.trim()); } }同样在Spring Security里自定义表达式,把这个校验加入访问规则,和前面的IP、SSH校验组合使用。
- IP白名单方案:简单易维护,适合内网场景,但要注意如果服务器有公网IP,需结合反向代理传递真实IP(比如Nginx的
X-Forwarded-For),避免IP伪造。 - SSH权限方案:贴合运维人员使用习惯,安全性高,但需要用户配合提供公钥,且依赖服务器的SSH配置。
- 本地密钥方案:实现最直接,但密钥需妥善保管,定期轮换,避免泄露。
最后要强调:所有方案都必须先做用户身份认证(比如JWT、OAuth2),再做环境/权限的二次校验,不要跳过身份认证直接用IP或密钥校验,否则容易被绕过。
内容的提问来源于stack exchange,提问作者Sayak Mukhopadhyay




