0%

授权码模式定义

通过客户端的后台服务器,与“服务提供商”的认证服务器进行认证。

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地址为:

http://localhost:8270/OAuth/Authorize?client\_id=123456&redirect\_uri=http%3A%2F%2Flocalhost%3A4825%2F&state=IUKeWFTR1HKi4hlzKOOPgw&scope=scopes1%20scopes2&response\_type=code

7.1 client_id为客户端ID,即之前我们在AuthorizationCodeGrant项目设置的clientID

7.2 redirect_uri、state为之前登录时就确定的值

7.3 scope为用户确定授权的范围

7.4 response_type=code,即指定为授权码模式

确认授权

此时url有变化:http://localhost:4825/?code=**efab38fc30c741a198b20663ec60869a36c6b25ff21f4c9986bcb9c9ae8d20eb&state=tjB9jXhNiHvIr4Ko9VhEkw**

注意:这一步会会默认获取Token

点击访问资源

完全能够对上;

url中的code即认证服务返回的授权码,之后Client请求Token会用这个code来交换

这个就是授权码模式的特色的地方了

 

 自此,整个授权码模式已经完毕了哦

asp.net权限认证系列

  1. asp.net权限认证:Forms认证
  2. asp.net权限认证:HTTP基本认证(http basic)
  3. asp.net权限认证:Windows认证
  4. asp.net权限认证:摘要认证(digest authentication)
  5. asp.net权限认证:OWIN实现OAuth 2.0 之客户端模式(Client Credential)
  6. asp.net权限认证:OWIN实现OAuth 2.0 之密码模式(Resource Owner Password Credential)
  7. asp.net权限认证:OWIN实现OAuth 2.0 之授权码模式(Authorization Code)
  8. asp.net权限认证:OWIN实现OAuth 2.0 之简化模式(Implicit)