📄 @Inner 注解使用及原理 👍
内部资料,请刷新扫码登录
pigcloud
# @Inner 注解概述
@Inner 注解用于控制服务间调用的权限和访问控制。它提供了一种优雅的方式来处理微服务架构中的认证需求。
# 使用场景
# 1. 标准鉴权访问(默认模式)
- 1.通过 Gateway 访问,需要完整的认证流程,适用于常规的 CRUD 操作
- 2.前端发起带 token 的请求,涉及 feign 服务间调用,feign 会自动传递 token
- 无需添加 @Inner 注解
# 2. 免鉴权外部访问
- 通过 Gateway 访问,但无需认证
- 适用场景:获取验证码、公开接口等
- 使用方式:
@Inner(false)
# 3. 内部服务调用 (无 token)
- 某些接口未携带 token,涉及 feign 服务间调用
- 定时任务通过 feign 调用接口、MQ 调用接口等
- 使用方式:
@Inner
# 技术实现原理
# 1. 注解定义
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Inner {
/**
* 是否仅允许服务间调用
* true: 仅允许服务间调用
* false: 允许外部直接访问
*/
boolean value() default true;
}
# 2. 自动配置机制
系统启动时会自动扫描带有 @Inner 注解的接口,并将其添加到白名单:
@Slf4j
@Configuration
@ConditionalOnExpression("!'${security.oauth2.client.ignore-urls}'.isEmpty()")
@ConfigurationProperties(prefix = "security.oauth2.client")
public class PermitAllUrlProperties implements InitializingBean {
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
@Autowired
private WebApplicationContext applicationContext;
@Getter
@Setter
private List<String> ignoreUrls = new ArrayList<>();
@Override
public void afterPropertiesSet() {
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(info -> {
HandlerMethod handlerMethod = map.get(info);
// 处理方法级注解
Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
Optional.ofNullable(method)
.ifPresent(inner -> info.getPatternsCondition().getPatterns()
.forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, StringPool.ASTERISK))));
// 处理类级注解
Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);
Optional.ofNullable(controller)
.ifPresent(inner -> info.getPatternsCondition().getPatterns()
.forEach(url -> ignoreUrls.add(ReUtil.replaceAll(url, PATTERN, StringPool.ASTERISK))));
});
}
}
# 3. 安全控制实现
通过 AOP 实现运行时的安全检查:
@Slf4j
@Aspect
@Component
@AllArgsConstructor
public class PigxSecurityInnerAspect {
private final HttpServletRequest request;
@SneakyThrows
@Around("@annotation(inner)")
public Object around(ProceedingJoinPoint point, Inner inner) {
String header = request.getHeader(SecurityConstants.FROM);
if (inner.value() && !StrUtil.equals(SecurityConstants.FROM_IN, header)) {
log.warn("接口访问被拒绝: {}", point.getSignature().getName());
throw new AccessDeniedException("Access is denied");
}
return point.proceed();
}
}
# 最佳实践与注意事项
- 路径变量使用警告
@Inner
@GetMapping("/info/{username}") // 会生成 /info/* 的忽略规则
使用路径变量时要特别注意
- 系统会将路径变量替换为通配符
*
- 可能导致意外的接口暴,建议使用更具体的路径设计
- 在使用
SecurityUtils.getUser()
时,确保接口未被错误地加入忽略列表
- 推荐用法
- 仅在必要的接口上使用 @Inner
- 优先考虑更具体的路径而非通配符
- 定期审查带有 @Inner 注解的接口