中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java怎么實現給接口增加一個參數

發布時間:2022-03-24 13:45:23 來源:億速云 閱讀:908 作者:iii 欄目:大數據

這篇“java怎么實現給接口增加一個參數”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“java怎么實現給接口增加一個參數”文章吧。

一、背景

一般在微服務架構中我們都會使用spring security oauth3來進行權限控制,我們將資源服務全部放在內網環境中,將API網關暴露在公網上,公網如果想要訪問我們的資源必須經過API網關進行鑒權,鑒權通過后再訪問我們的資源服務。我們根據如下圖片來分析一下問題。

java怎么實現給接口增加一個參數

現在我們有三個服務:分別是用戶服務、訂單服務和產品服務。用戶如果購買產品,則需要調用產品服務生成訂單,那么我們在這個調用過程中有必要鑒權嗎?答案是否定的,因為這些資源服務放在內網環境中,完全不用考慮安全問題。 

二、思路

如果要想實現這個功能,我們則需要來區分這兩種請求,來自網關的請求進行鑒權,而服務間的請求則直接調用。

是否可以給接口增加一個參數來標記它是服務間調用的請求?

這樣雖然可以實現兩種請求的區分,但是實際中不會這么做。一般情況下服務間調用和網關請求的數據接口是同一個接口,如果寫成兩個接口來分別給兩種請求調用,這樣無疑增加了大量重復代碼。也就是說我們一般不會通過改變請求參數的個數來實現這兩種服務的區分。

雖然不能增加請求的參數個數來區分,但是我們可以給請求的header中添加一個參數用來區分。這樣完全可以避免上面提到的問題。 

三、實現 

3.1 自定義注解

我們自定義一個Inner的注解,然后利用aop對這個注解進行處理

1@Target(ElementType.METHOD)
2@Retention(RetentionPolicy.RUNTIME)
3@Documented
4public @interface Inner {
5    /**
6     * 是否AOP統一處理
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("訪問接口 {} 沒有權限", 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注解的方法或類,判斷請求頭中是否有我們規定的參數,如果沒有,則不允許訪問接口。 

3.2 暴露url

將所有注解了@Inner的方法和類暴露出來,允許不鑒權可以方法,這里需要注意的點是如果方法使用pathVariable 傳參的,則需要將這個參數轉換為*。如果不轉換,當成接口的訪問路徑,則找不到此接口。

 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            // 獲取類上邊的注解, 替代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}
 

在資源服務器中,將請求暴露出來

 1public void configure(HttpSecurity httpSecurity) throws Exception {
2    //允許使用iframe 嵌套,避免swagger-ui 不被加載的問題
3    httpSecurity.headers().frameOptions().disable();
4    ExpressionUrlAuthorizationConfigurer<HttpSecurity>
5        .ExpressionInterceptUrlRegistry registry = httpSecurity
6        .authorizeRequests();
7    // 將上面獲取到的請求,暴露出來
8    permitAllUrl.getUrls()
9        .forEach(url -> registry.antMatchers(url).permitAll());
10    registry.anyRequest().authenticated()
11        .and().csrf().disable();
12}
   

3.3 如何去請求

定義一個接口:

1@PostMapping("test")
2@Inner
3public String test(@RequestParam String id){
4    return id;
5}
 

定義feign遠程調用接口

1@PostMapping("test")
2MediaFodderBean test(@RequestParam("id") String id,@RequestHeader(SecurityConstants.FROM) String from);

服務間進行調用,傳請求頭

1 String id = testService.test(id, SecurityConstants.FROM_IN);  

四、思考 

4.1 安全性

上面雖然實現了服務間調用,但是我們將@Inner的請求暴露出去了,也就是說不用鑒權既可以訪問到,那么我們是不是可以模擬一個請求頭,然后在其他地方通過網關來調用呢?

答案是可以,那么,這時候我們就需要對網關中分發的請求進行處理,在網關中寫一個全局攔截器,將請求頭的form參數清洗。

 1@Component
2public class RequestGlobalFilter implements GlobalFilter, Ordered {
3
4    @Override
5    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
6        // 清洗請求頭中from 參數
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}
   

4.2 擴展性

我們自定義@Inner注解的時候,放了一個boolean類型的value(),默認為true。如果我們想讓這個請求可以通過網關訪問的話,將value賦值為false即可。

1@PostMapping("test")
2@Inner(value=false)
3public String test(@RequestParam String id){
4    return id;
5}

以上就是關于“java怎么實現給接口增加一個參數”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

海城市| 南部县| 梁河县| 土默特右旗| 民乐县| 巫山县| 兴安盟| 襄汾县| 青河县| 昌宁县| 松原市| 临高县| 东方市| 博客| 齐河县| 城市| 乾安县| 安阳县| 隆昌县| 台中县| 凤台县| 耒阳市| 胶南市| 扶绥县| 淅川县| 邵阳市| 绥化市| 深泽县| 高雄县| 斗六市| 莒南县| 额济纳旗| 怀柔区| 丘北县| 荆门市| 象山县| 久治县| 内黄县| 黄大仙区| 泸溪县| 吐鲁番市|