これまでは、Azure Functionsのアクセス権限をAnonymous(匿名)にしていて、APIキーが不要でもアクセスできていたが、関数へのアクセスをより困難にするために、APIキーを必須にすることもできる。
今回は、Azure Functionsのアクセス権限を関数レベルに変更し、ヘッダーにAPIキーを設定してみたので、そのサンプルプログラムを共有する。
前提条件
下記記事の実装が完了していること。
作成したサンプルプログラム(App Service側)の内容
作成したサンプルプログラム(App Service側)の構成は以下の通り。

なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
コントローラクラスの内容は以下の通りで、Azure Functionsを呼び出すときのヘッダーに「”x-functions-key”, “Azure FunctionsのAPIキーの値”」の値を追加している。
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
@Controller
public class DemoController {
/** RestTemplateオブジェクト */
@Autowired
private RestTemplate restTemplate;
/** ObjectMapperオブジェクト */
@Autowired
private ObjectMapper objectMapper;
/** application.propertiesからdemoAzureFunc.urlBaseの値を取得 */
@Value("${demoAzureFunc.urlBase}")
private String demoAzureFuncBase;
/**
* 検索一覧画面を初期表示する.
* @param model Modelオブジェクト
* @return 検索一覧画面
*/
@GetMapping("/")
public String index(Model model) {
SearchForm searchForm = new SearchForm();
model.addAttribute("searchForm", searchForm);
return "list";
}
/**
* 検索条件に合うユーザーデータを取得し、一覧に表示する
* @param searchForm 検索条件Form
* @param model Modelオブジェクト
* @return 検索一覧画面
*/
@PostMapping("/search")
public String search(SearchForm searchForm, Model model) {
// Azure FunctionsのgetUserDataList関数を呼び出すためのヘッダー情報を設定する
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// Azure FunctionsにアクセスするためのAPIキーを設定する
headers.add("x-functions-key", "(Azure FunctionsにアクセスするためのAPIキー)");
// Azure FunctionsのgetUserDataList関数を呼び出すための引数を設定する
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
try {
map.add("searchName", searchForm.getSearchName());
map.add("searchSex", searchForm.getSearchSex());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
HttpEntity<MultiValueMap<String, String>> entity
= new HttpEntity<>(map, headers);
// Azure FunctionsのgetUserDataList関数を呼び出す
ResponseEntity<String> response = restTemplate.exchange(
demoAzureFuncBase + "getUserDataList", HttpMethod.POST,
entity, String.class);
// Azure Functionsの呼出結果のユーザーデータ一覧を、検索条件Formに設定する
try {
SearchResult searchResult = objectMapper.readValue(
response.getBody(), SearchResult.class);
searchForm.setUserDataList(searchResult.getUserDataList());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
model.addAttribute("searchForm", searchForm);
return "list";
}
}なお、「Azure FunctionsのAPIキーの値」は、以下に示すような、Azure Portal上、Azure Functionsの「アプリキー」メニュー、ホストキー「default」の値となる。

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/azure-functions-api-key/demoAzureApp
作成したサンプルプログラム(Azure Functions側)の内容
作成したサンプルプログラム(Azure Functions側)の構成は以下の通り。

なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
<2021年4月13日 追記>
spring-cloud-function-dependenciesのバージョンは、2021年3月16日にリリースしたバージョン3.1.2を利用すると、1つのAzure Functions内に複数のファンクションを含む場合の不具合が解消できている。
その場合、Handlerクラスの継承するクラスを「AzureSpringBootRequestHandler」クラスから「FunctionInvoker」クラスに変更する。
spring-cloud-function-dependenciesの3.1.2を利用した実装サンプルは、以下の記事を参照のこと。
ハンドラークラスの内容は以下の通りで、権限レベル(authLevel)をANONYMOUSからFUNCTIONに変更している。
package com.example;
import java.util.Optional;
import org.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHandler;
import com.example.model.SearchForm;
import com.example.model.SearchResult;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
public class GetUserDataListHandler
extends AzureSpringBootRequestHandler<SearchForm, SearchResult> {
/**
* HTTP要求に応じて、DemoAzureFunctionクラスのgetUserDataListメソッドを呼び出し、
* その戻り値をボディに設定したレスポンスを返す
* @param request リクエストオブジェクト
* @param context コンテキストオブジェクト
* @return レスポンスオブジェクト
*/
@FunctionName("getUserDataList")
public HttpResponseMessage execute(
@HttpTrigger(name = "request", methods = HttpMethod.POST
, authLevel = AuthorizationLevel.FUNCTION)
HttpRequestMessage<Optional<String>> request,
ExecutionContext context) {
// リクエストオブジェクトからパラメータ値を取得し、検索条件Formに設定する
ObjectMapper mapper = new ObjectMapper();
String jsonParam = request.getBody().get();
jsonParam = jsonParam.replaceAll("\\[", "").replaceAll("\\]", "");
SearchForm searchForm = new SearchForm();
try {
searchForm = mapper.readValue(
jsonParam, new TypeReference<SearchForm>() {});
} catch (Exception ex) {
throw new RuntimeException(ex);
}
// handleRequestメソッド内でDemoAzureFunctionクラスのgetUserDataListメソッドを
// 呼び出し、その戻り値をボディに設定したレスポンスを、JSON形式で返す
return request.createResponseBuilder(HttpStatus.OK)
.body(handleRequest(searchForm, context))
.header("Content-Type", "text/json").build();
}
}
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/azure-functions-api-key/demoAzureFunc
サンプルプログラムの実行結果
サンプルプログラムの実行結果は、以下の通り。
1) 前提条件のプログラムからAzure Functionsのみ変更し、Azure上で実行した場合は、以下のように、Azure Functionsの呼び出しが行えないことが確認できる。


2) 前提条件のプログラムからAzure Functionsのみ変更し、ローカルで実行した場合は、以下のように、正常に動作することが確認できる。

3) 前提条件のプログラムからAzure FunctionsとAzure App Serviceを変更し、Azure上で実行した場合は、以下のように、正常に動作することが確認できる。


4) 前提条件のプログラムからAzure FunctionsとAzure App Serviceを変更し、ローカル環境で実行した場合も、以下のように、正常に動作することが確認できる。


要点まとめ
- Azure Functionsのアクセス権限を関数レベルに変更すると、Anonymous(匿名)の場合より、関数へのアクセスを困難にすることができる。
- Azure Functionsのアクセス権限を関数レベルに変更した場合、Azure Functionsを呼び出す際に、ヘッダーにAPIキーを指定する必要がある。





