授权码模式定义
通过客户端的后台服务器,与“服务提供商”的认证服务器进行认证。
1、用户访问客户端,后者将前者导向认证服务器。
2、用户选择是否给予客户端授权。
3、假设用户给予授权,认证服务器首先生成一个授权码,并返回给用户,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。
4、客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
5、认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
6、Client拿着access token去访问Resource资源
授权码模式的工作流程图
图 1 (网上搜到的授权码工作流程图说明)
之前看上边的流程图,看了不下10遍,还是搞不懂,这个图真心画的不好理解!
我们一步步来,AuthorizationServer与ResourceServer还是用之前的项目
新建项目:AuthorizationCodeGrant
HomeController.cs也简单
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public
ActionResult Index()
{
ViewBag.AccessToken = Request.Form[``"AccessToken"``] ??
""``;
ViewBag.RefreshToken = Request.Form[``"RefreshToken"``] ??
""``;
ViewBag.Action =
""``;
ViewBag.ResourceResponse =
""``;
var
authorizationServerUri =
new
Uri(``"http://localhost:8270/"``);
var
authorizationServer =
new
AuthorizationServerDescription
{
AuthorizationEndpoint =
new
Uri(authorizationServerUri,
"OAuth/Authorize"``),
TokenEndpoint =
new
Uri(authorizationServerUri,
"OAuth/Token"``)
};
var
client =
new
WebServerClient(authorizationServer,
"123456"``,
"abcdef"``);
if
(``string``.IsNullOrEmpty(ViewBag.AccessToken))
{
var
authorizationState = client.ProcessUserAuthorization(Request);
if
(authorizationState !=
null``)
{
ViewBag.AccessToken = authorizationState.AccessToken;
ViewBag.RefreshToken = authorizationState.RefreshToken;
ViewBag.Action = Request.Path;
}
}
if
(!``string``.IsNullOrEmpty(Request.Form.Get(``"btnRequestAuthorize"``)))
{
var
grantRequest = client.PrepareRequestUserAuthorization(``new``[] {
"scopes1"``,
"scopes2"
});
grantRequest.Send(HttpContext);
Response.End();
}
if
(!``string``.IsNullOrEmpty(Request.Form.Get(``"btnRequestResource"``)))
{
var
resourceServerUri =
new
Uri(``"http://localhost:8001/"``);
var
resourceRequest =
new
HttpClient(client.CreateAuthorizingHandler(ViewBag.AccessToken));
ViewBag.ResourceResponse = resourceRequest.GetStringAsync(``new
Uri(resourceServerUri,
"api/Values"``)).Result;
}
return
View();
}
Index.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!``DOCTYPE
html>
<``html
xmlns="http://www.w3.org/1999/xhtml">
<``head``>
<``title``>Authorization Code Grant Client</``title``>
</``head``>
<``body``>
<``form
id="form1" action="@ViewBag.Action" method="POST">
<``div``>
<``input
id="AccessToken" name="AccessToken" value="@ViewBag.AccessToken" type="hidden" />
<``input
id="Authorize" name="btnRequestAuthorize" value="向认证服务器索要授权" type="submit" />
<``input
id="Resource" name="btnRequestResource" value="访问资源(Resource)" type="submit" />
</``div``>
<``div``>@ViewBag.ResourceResponse</``div``>
</``form``>
</``body``>
</``html``>
运行项目
授权过程
点击“向认证服务索要授权”,根据HomeController.cs文件的设置,页面预计会跳转到”http://localhost:8270/OAuth/Authorize“
所以我们需要在认证服务中新增处理授权码模式的处理逻辑
在项目AuthorizationServer中新增OAuthController.cs、Authorize.cshtml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public
class
OAuthController : Controller
{
public
ActionResult Authorize()
{
if
(Response.StatusCode != 200)
{
return
View(``"AuthorizeError"``);
}
var
authentication = HttpContext.GetOwinContext().Authentication;
var
ticket = authentication.AuthenticateAsync(``"Application"``).Result;
var
identity = ticket !=
null
? ticket.Identity :
null``;
if
(identity ==
null``)
{
authentication.Challenge(``"Application"``);
return
new
HttpUnauthorizedResult();
}
ViewBag.IdentityName = identity.Name;
ViewBag.Scopes = (Request.QueryString.Get(``"scope"``) ??
""``).Split(``' '``);
if
(Request.HttpMethod ==
"POST"``)
{
if
(!``string``.IsNullOrEmpty(Request.Form.Get(``"btnGrant"``)))
{
identity =
new
ClaimsIdentity(identity.Claims,
"Bearer"``, identity.NameClaimType, identity.RoleClaimType);
foreach
(``var
scope
in
ViewBag.Scopes)
{
identity.AddClaim(``new
Claim(``"urn:oauth:scope"``, scope));
}
authentication.SignIn(identity);
}
if
(!``string``.IsNullOrEmpty(Request.Form.Get(``"btnOtherLogin"``)))
{
authentication.SignOut(``"Application"``);
authentication.Challenge(``"Application"``);
return
new
HttpUnauthorizedResult();
}
}
return
View();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!``DOCTYPE
html>
<``html
xmlns="http://www.w3.org/1999/xhtml">
<``head``>
<``title``>Authorize</``title``>
</``head``>
<``body``>
<``h1``>认证页面</``h1``>
<``form
method="POST">
<``p``>登录用户:@ViewBag.IdentityName</``p``>
<``p``>第三方应用需要你给他开放以下权限</``p``>
<``ul``>
@foreach (var scope in ViewBag.Scopes)
{
<``li``>@scope</``li``>
}
</``ul``>
<``p``>
<``input
type="submit" name="btnGrant" value="确认授权" />
<``input
type="submit" name="btnOtherLogin" value="以不同用户登录" />
</``p``>
</``form``>
</``body``>
</``html``>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public
class
AccountController : Controller
{
public
ActionResult Login()
{
var
authentication = HttpContext.GetOwinContext().Authentication;
if
(Request.HttpMethod ==
"POST"``)
{
var
username = Request.Form[``"username"``];
authentication.SignIn(
new
AuthenticationProperties { IsPersistent =
true
},
new
ClaimsIdentity(
new``[] {
new
Claim(ClaimsIdentity.DefaultNameClaimType, username) },
"Application"``));
}
return
View();
}
public
ActionResult Logout()
{
return
View();
}
}
运行项目,成功跳转至认证登录页面
点击登录,此时url地址为:
7.1 client_id为客户端ID,即之前我们在AuthorizationCodeGrant项目设置的clientID
7.2 redirect_uri、state为之前登录时就确定的值
7.3 scope为用户确定授权的范围
7.4 response_type=code,即指定为授权码模式
确认授权
注意:这一步会会默认获取Token
点击访问资源
完全能够对上;
url中的code即认证服务返回的授权码,之后Client请求Token会用这个code来交换
这个就是授权码模式的特色的地方了
自此,整个授权码模式已经完毕了哦
asp.net权限认证系列
- asp.net权限认证:Forms认证
- asp.net权限认证:HTTP基本认证(http basic)
- asp.net权限认证:Windows认证
- asp.net权限认证:摘要认证(digest authentication)
- asp.net权限认证:OWIN实现OAuth 2.0 之客户端模式(Client Credential)
- asp.net权限认证:OWIN实现OAuth 2.0 之密码模式(Resource Owner Password Credential)
- asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)
- asp.net权限认证:OWIN实现OAuth 2.0 之简化模式(Implicit)