您好,登錄后才能下訂單哦!
這篇“java怎么實(shí)現(xiàn)給接口增加一個(gè)參數(shù)”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“java怎么實(shí)現(xiàn)給接口增加一個(gè)參數(shù)”文章吧。
一般在微服務(wù)架構(gòu)中我們都會(huì)使用spring security oauth3來(lái)進(jìn)行權(quán)限控制,我們將資源服務(wù)全部放在內(nèi)網(wǎng)環(huán)境中,將API網(wǎng)關(guān)暴露在公網(wǎng)上,公網(wǎng)如果想要訪問(wèn)我們的資源必須經(jīng)過(guò)API網(wǎng)關(guān)進(jìn)行鑒權(quán),鑒權(quán)通過(guò)后再訪問(wèn)我們的資源服務(wù)。我們根據(jù)如下圖片來(lái)分析一下問(wèn)題。
現(xiàn)在我們有三個(gè)服務(wù):分別是用戶服務(wù)、訂單服務(wù)和產(chǎn)品服務(wù)。用戶如果購(gòu)買(mǎi)產(chǎn)品,則需要調(diào)用產(chǎn)品服務(wù)生成訂單,那么我們?cè)谶@個(gè)調(diào)用過(guò)程中有必要鑒權(quán)嗎?答案是否定的,因?yàn)檫@些資源服務(wù)放在內(nèi)網(wǎng)環(huán)境中,完全不用考慮安全問(wèn)題。
如果要想實(shí)現(xiàn)這個(gè)功能,我們則需要來(lái)區(qū)分這兩種請(qǐng)求,來(lái)自網(wǎng)關(guān)的請(qǐng)求進(jìn)行鑒權(quán),而服務(wù)間的請(qǐng)求則直接調(diào)用。
是否可以給接口增加一個(gè)參數(shù)來(lái)標(biāo)記它是服務(wù)間調(diào)用的請(qǐng)求?
這樣雖然可以實(shí)現(xiàn)兩種請(qǐng)求的區(qū)分,但是實(shí)際中不會(huì)這么做。一般情況下服務(wù)間調(diào)用和網(wǎng)關(guān)請(qǐng)求的數(shù)據(jù)接口是同一個(gè)接口,如果寫(xiě)成兩個(gè)接口來(lái)分別給兩種請(qǐng)求調(diào)用,這樣無(wú)疑增加了大量重復(fù)代碼。也就是說(shuō)我們一般不會(huì)通過(guò)改變請(qǐng)求參數(shù)的個(gè)數(shù)來(lái)實(shí)現(xiàn)這兩種服務(wù)的區(qū)分。
雖然不能增加請(qǐng)求的參數(shù)個(gè)數(shù)來(lái)區(qū)分,但是我們可以給請(qǐng)求的header中添加一個(gè)參數(shù)用來(lái)區(qū)分。這樣完全可以避免上面提到的問(wèn)題。
我們自定義一個(gè)Inner的注解,然后利用aop對(duì)這個(gè)注解進(jìn)行處理
1@Target(ElementType.METHOD)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4public @interface Inner {
5 /**
6 * 是否AOP統(tǒng)一處理
7 */
8 boolean value() default true;
9}
1@Aspect
2@Component
3public class InnerAspect implements Ordered {
4
5 private final Logger log = LoggerFactory.getLogger(InnerAspect.class);
6
7 @Around("@annotation(inner)")
8 public Object around(ProceedingJoinPoint point, Inner inner) throws Throwable {
9 String header = ServletUtils.getRequest().getHeader(SecurityConstants.FROM);
10 if (inner.value() && !StringUtils.equals(SecurityConstants.FROM_IN, header)){
11 log.warn("訪問(wèn)接口 {} 沒(méi)有權(quán)限", point.getSignature().getName());
12 throw new AccessDeniedException("Access is denied");
13 }
14 return point.proceed();
15 }
16
17 @Override
18 public int getOrder() {
19 return Ordered.HIGHEST_PRECEDENCE + 1;
20 }
21}
上面這段代碼就是獲取所有加了@Inner注解的方法或類(lèi),判斷請(qǐng)求頭中是否有我們規(guī)定的參數(shù),如果沒(méi)有,則不允許訪問(wèn)接口。
將所有注解了@Inner的方法和類(lèi)暴露出來(lái),允許不鑒權(quán)可以方法,這里需要注意的點(diǎn)是如果方法使用pathVariable 傳參的,則需要將這個(gè)參數(shù)轉(zhuǎn)換為*。如果不轉(zhuǎn)換,當(dāng)成接口的訪問(wèn)路徑,則找不到此接口。
1@Configuration
2public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware{
3
4 private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
5 private ApplicationContext applicationContext;
6 private List<String> urls = new ArrayList<>();
7 public static final String ASTERISK = "*";
8
9 @Override
10 public void afterPropertiesSet() {
11 RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
12 Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
13 map.keySet().forEach(info -> {
14 HandlerMethod handlerMethod = map.get(info);
15 // 獲取方法上邊的注解 替代path variable 為 *
16 Inner method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
17 Optional.ofNullable(method).ifPresent(inner -> info.getPatternsCondition().getPatterns()
18 .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, ASTERISK))));
19 // 獲取類(lèi)上邊的注解, 替代path variable 為 *
20 Inner controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Inner.class);
21 Optional.ofNullable(controller).ifPresent(inner -> info.getPatternsCondition().getPatterns()
22 .forEach(url -> urls.add(ReUtil.replaceAll(url, PATTERN, ASTERISK))));
23 });
24 }
25
26 @Override
27 public void setApplicationContext(ApplicationContext context) {
28 this.applicationContext = context;
29 }
30
31 public List<String> getUrls() {
32 return urls;
33 }
34
35 public void setUrls(List<String> urls) {
36 this.urls = urls;
37 }
38}
在資源服務(wù)器中,將請(qǐng)求暴露出來(lái)
1public void configure(HttpSecurity httpSecurity) throws Exception {
2 //允許使用iframe 嵌套,避免swagger-ui 不被加載的問(wèn)題
3 httpSecurity.headers().frameOptions().disable();
4 ExpressionUrlAuthorizationConfigurer<HttpSecurity>
5 .ExpressionInterceptUrlRegistry registry = httpSecurity
6 .authorizeRequests();
7 // 將上面獲取到的請(qǐng)求,暴露出來(lái)
8 permitAllUrl.getUrls()
9 .forEach(url -> registry.antMatchers(url).permitAll());
10 registry.anyRequest().authenticated()
11 .and().csrf().disable();
12}
定義一個(gè)接口:
1@PostMapping("test")
2@Inner
3public String test(@RequestParam String id){
4 return id;
5}
定義feign遠(yuǎn)程調(diào)用接口
1@PostMapping("test")
2MediaFodderBean test(@RequestParam("id") String id,@RequestHeader(SecurityConstants.FROM) String from);
服務(wù)間進(jìn)行調(diào)用,傳請(qǐng)求頭
1 String id = testService.test(id, SecurityConstants.FROM_IN);
上面雖然實(shí)現(xiàn)了服務(wù)間調(diào)用,但是我們將@Inner的請(qǐng)求暴露出去了,也就是說(shuō)不用鑒權(quán)既可以訪問(wèn)到,那么我們是不是可以模擬一個(gè)請(qǐng)求頭,然后在其他地方通過(guò)網(wǎng)關(guān)來(lái)調(diào)用呢?
答案是可以,那么,這時(shí)候我們就需要對(duì)網(wǎng)關(guān)中分發(fā)的請(qǐng)求進(jìn)行處理,在網(wǎng)關(guān)中寫(xiě)一個(gè)全局?jǐn)r截器,將請(qǐng)求頭的form參數(shù)清洗。
1@Component
2public class RequestGlobalFilter implements GlobalFilter, Ordered {
3
4 @Override
5 public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
6 // 清洗請(qǐng)求頭中from 參數(shù)
7 ServerHttpRequest request = exchange.getRequest().mutate()
8 .headers(httpHeaders -> httpHeaders.remove(SecurityConstants.FROM))
9 .build();
10 addOriginalRequestUrl(exchange, request.getURI());
11 String rawPath = request.getURI().getRawPath();
12 ServerHttpRequest newRequest = request.mutate()
13 .path(rawPath)
14 .build();
15 exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
16 return chain.filter(exchange.mutate()
17 .request(newRequest.mutate()
18 .build()).build());
19 }
20
21 @Override
22 public int getOrder() {
23 return -1000;
24 }
25}
我們自定義@Inner注解的時(shí)候,放了一個(gè)boolean類(lèi)型的value(),默認(rèn)為true。如果我們想讓這個(gè)請(qǐng)求可以通過(guò)網(wǎng)關(guān)訪問(wèn)的話,將value賦值為false即可。
1@PostMapping("test")
2@Inner(value=false)
3public String test(@RequestParam String id){
4 return id;
5}
以上就是關(guān)于“java怎么實(shí)現(xiàn)給接口增加一個(gè)參數(shù)”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。