【重構】Spring Cloud OAuth 無Token呼叫原始碼封裝
阿新 • • 發佈:2019-04-23
背景
重構-改善既有程式碼的設計,重構的目的是是軟體更容易被理解和修改。
書接上回Spring Security OAuth 微服務內部Token傳遞的原始碼解析,本篇主要無token 呼叫過程中,程式碼的不斷完善及其重構過程。
需求很簡單如下圖,如果資源伺服器的提供的介面,客戶端不需要身份驗證即不需要攜帶合法令牌也能訪問,並且可以實現遠端呼叫的安全性校驗。
第一版本
資源伺服器設定介面permitall,配置ignore url 即可
ignore-urls:
- /actuator/**
- /v2/api-docs
保證A對外暴露,A --> B 暴露的服務介面安全
- 自定義 @Inner
- 校驗邏輯,判斷介面請求中是否含有 XX 請求頭
/** * @author lengleng * <p> * 服務間介面不鑑權處理邏輯 */ @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(); } }
- 閘道器請求含有XX 的請求頭,避免偽造
public class PigxRequestGlobalFilter implements GlobalFilter, Ordered { private static final String HEADER_NAME = "X-Forwarded-Prefix"; /** * Process the Web request and (optionally) delegate to the next * {@code WebFilter} through the given {@link GatewayFilterChain}. * * @param exchange the current server exchange * @param chain provides a way to delegate to the next filter * @return {@code Mono<Void>} to indicate when request processing is complete */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 1. 清洗請求頭中from 引數 ServerHttpRequest request = exchange.getRequest().mutate() .headers(httpHeaders -> {httpHeaders.remove(SecurityConstants.FROM);}) .build(); return chain.filter(exchange.mutate() .request(newRequest.mutate() .header(HEADER_NAME, basePath) .build()).build()); } @Override public int getOrder() { return -1000; } }
- 介面使用,首先宣告 B服務的這個介面對外暴露
ignore-urls:
- /info/*
- 介面使用,然後在 B服務的這個介面 新增@Inner註解
@Inner
@GetMapping("/info/{username}")
public R info(@PathVariable String username) {
}
重構
- 上邊第一版本的問題是,對於A/B 資源服務想對外暴露的介面,需要兩步
- 宣告在ResourceServerConfigurerAdapter 的 permitall
- B服務要再次新增@inner 註解
實現@Inner 一步到位到位
- 在ignoreU日曆 獲取全部Controller 中,標誌@Inner 註解的請求,自動維護到忽略的URL,減少開發配置
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);
// 獲取方法上邊的註解 替代path variable 為 *
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))));
});
}
}
- 核心是通過RequestMappingHandlerMapping 獲取全部的路由配置,然後對 Requestmappint 設定的URL 進行規則替換,然後新增到 ignoreurl中,然後在注入到 ResourceServerConfigurerAdapter 進行permitall
- 使用時候,如果是外部暴露
@Inner(value=false)
- 如果僅是服務內部呼叫暴露
@Inner
總結
歡迎關注我們獲得更多