接上一篇:IdentityServer4實現OAuth2.0四種模式之隱藏模式
授權碼模式隱藏碼模式最大不同是授權碼模式不直接返回token,而是先返回一個授權碼,然后再根據這個授權碼去請求token。這比隱藏模式更為安全。從應用場景上來區分的話,隱藏模式適應於全前端的應用,授權碼模式適用於有后端的應用,因為客戶端根據授權碼去請求token時是需要把客戶端密碼轉進來的,為了避免客戶端密碼被暴露,所以請求token這個過程需要放在后台。
一,服務端配置
1,添加客戶端
新建一個支持授權碼模式的客戶端,請求token時需要客戶端密碼,所以需要設置clientSecret。登錄成功后重定向地址依然用之前建立的HTML頁面。
new Client()
{
//客戶端Id
ClientId="apiClientCode",
ClientName="ApiClient for Code",
//客戶端密碼
ClientSecrets={new Secret("apiSecret".Sha256()) },
//客戶端授權類型,Code:授權碼模式
AllowedGrantTypes=GrantTypes.Code,
//允許登錄后重定向的地址列表,可以有多個
RedirectUris = {"https://localhost:5002/auth.html"},
//允許訪問的資源
AllowedScopes={
"secretapi"
}
}
二,MVC客戶端配置
- 由於和隱藏模式返回token用瞄點的方式不同,授權碼是url參數化傳遞過來的。所以修改一下需要修改一下HTML代碼,使其可以顯示出參數化的授權碼。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var content = "";
window.onload = function () {
var url = window.location.href;
var array = url.split("#");
if (array.length > 1) {
content = array[1];
}
var search = window.location.search;
if (search) {
search = search.substr(1);
var paras = search.split("&");
paras.forEach(element => {
content += element;
content+=";"
});
}
document.getElementById("content").innerHTML = content;
}
</script>
</head>
<body>
<div id="content"></div>
</body>
</html>
三,獲取授權碼
根據OAuth2.0協議,傳遞以下參數,傳遞地址還是參數IdentityServer4的Discover說明中的authorization_endpoint節點值http://localhost:5000/connect/authorize
client_id:客戶端Id redirect_uri=重定向Url,用戶登錄成功后跳回此地址 response_type=code,固定值,表示獲取授權碼 scope=secretapi,此token需要訪問的api
拼接url:http://localhost:5000/connect/authorize?client_id=apiClientCode&redirect_uri=https://localhost:5002/auth.html&response_type=code&scope=secretapi
在瀏覽器中訪問此url,會跳轉到用戶登錄界面,用之前創建的用戶apiUser和密碼登錄后瀏覽器會自動跳轉回設置的重定向Url

可以看到已經取到了code。
四,訪問被保護的API
1,通過后台訪問
public async Task<IActionResult> GetData(string type,string userName,string password,string code)
{
type = type ?? "client";
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
return new JsonResult(new { err=disco.Error});
TokenResponse token = null;
switch (type)
{
case "client":
token = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
{
//獲取Token的地址
Address = disco.TokenEndpoint,
//客戶端Id
ClientId = "apiClientCd",
//客戶端密碼
ClientSecret = "apiSecret",
//要訪問的api資源
Scope = "secretapi"
});
break;
case "password":
token = await client.RequestPasswordTokenAsync(new PasswordTokenRequest()
{
//獲取Token的地址
Address = disco.TokenEndpoint,
//客戶端Id
ClientId = "apiClientPassword",
//客戶端密碼
ClientSecret = "apiSecret",
//要訪問的api資源
Scope = "secretapi",
UserName =userName,
Password = password
});
break;
case "code":
token = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest()
{
Address = disco.TokenEndpoint,
ClientId = "apiClientCode",
//客戶端密碼
ClientSecret = "apiSecret",
Code = code,
RedirectUri = "https://localhost:5002/auth.html"
});
break;
}
if (token.IsError)
return new JsonResult(new { err = token.Error });
client.SetBearerToken(token.AccessToken);
string data = await client.GetStringAsync("https://localhost:5001/api/identity");
JArray json = JArray.Parse(data);
return new JsonResult(json);
}
直接訪問:https://localhost:5002/home/getdata?type=code&code=93516f5af0c644c13228a66954d6c892816d358704536b6ca4e6623f6b00dee0

2,通過原生Http請求訪問
根據OAuth2.0協議,傳以下參數,地址則是之前在客戶端模式和密碼模式獲取token時用到的地址,可以在identityServer4的discover文檔中找到。
client_id:客戶端Id client_secret:客戶端密碼 grant_type:authorization_code,固定值 redirect_uri=重定向Url,用戶登錄成功后跳回此地址 code:獲取到的授權碼 scope=secretapi,此token需要訪問的api

獲取到token就可以訪問api了。

四種模式講完,IdentityServer.Config.GetIdentityResouce還沒用上呢!因為這四種模式只涉及到IdentityServer4的OAuth2.0特性,OpenId部分還沒有涉及,請看下一篇
。
